EFL 1.7 svn doobies
authorraster <raster@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 30 Aug 2012 09:54:57 +0000 (09:54 +0000)
committerraster <raster@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Thu, 30 Aug 2012 09:54:57 +0000 (09:54 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/branches/efreet-1.7@75862 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

120 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
NOTES [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
doc/Doxyfile.in [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/e.css [new file with mode: 0644]
doc/foot.html [new file with mode: 0644]
doc/head.html [new file with mode: 0644]
doc/images/e.png [new file with mode: 0644]
doc/images/edoxy.css [new file with mode: 0644]
doc/images/foot_bg.png [new file with mode: 0644]
doc/images/head_bg.png [new file with mode: 0644]
doc/images/menu_bg.png [new file with mode: 0644]
doc/images/menu_bg_current.png [new file with mode: 0644]
doc/images/menu_bg_hover.png [new file with mode: 0644]
doc/images/menu_bg_last.png [new file with mode: 0644]
doc/images/menu_bg_unsel.png [new file with mode: 0644]
efreet-mime.pc.in [new file with mode: 0644]
efreet-trash.pc.in [new file with mode: 0644]
efreet.pc.in [new file with mode: 0644]
efreet.spec.in [new file with mode: 0644]
m4/ac_attribute.m4 [new file with mode: 0644]
m4/ac_define_dir.m4 [new file with mode: 0644]
m4/efl_compiler_flag.m4 [new file with mode: 0644]
m4/efl_coverage.m4 [new file with mode: 0644]
m4/efl_doxygen.m4 [new file with mode: 0644]
m4/efl_path_max.m4 [new file with mode: 0644]
m4/efl_tests.m4 [new file with mode: 0644]
po/LINGUAS [new file with mode: 0644]
po/Makevars [new file with mode: 0644]
po/POTFILES.in [new file with mode: 0644]
po/cs.po [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/efreet.pot [new file with mode: 0644]
po/el.po [new file with mode: 0644]
po/es.po [new file with mode: 0644]
po/fr.po [new file with mode: 0644]
po/it.po [new file with mode: 0644]
po/ja.po [new file with mode: 0644]
po/ko.po [new file with mode: 0644]
po/nl.po [new file with mode: 0644]
po/pt.po [new file with mode: 0644]
po/ru.po [new file with mode: 0644]
po/sl.po [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/bin/Makefile.am [new file with mode: 0644]
src/bin/efreet_desktop_cache_create.c [new file with mode: 0644]
src/bin/efreet_icon_cache_create.c [new file with mode: 0644]
src/lib/Efreet.h [new file with mode: 0644]
src/lib/Efreet_Mime.h [new file with mode: 0644]
src/lib/Efreet_Trash.h [new file with mode: 0644]
src/lib/Makefile.am [new file with mode: 0644]
src/lib/efreet.c [new file with mode: 0644]
src/lib/efreet_base.c [new file with mode: 0644]
src/lib/efreet_base.h [new file with mode: 0644]
src/lib/efreet_cache.c [new file with mode: 0644]
src/lib/efreet_cache_private.h [new file with mode: 0644]
src/lib/efreet_desktop.c [new file with mode: 0644]
src/lib/efreet_desktop.h [new file with mode: 0644]
src/lib/efreet_desktop_command.c [new file with mode: 0644]
src/lib/efreet_icon.c [new file with mode: 0644]
src/lib/efreet_icon.h [new file with mode: 0644]
src/lib/efreet_ini.c [new file with mode: 0644]
src/lib/efreet_ini.h [new file with mode: 0644]
src/lib/efreet_menu.c [new file with mode: 0644]
src/lib/efreet_menu.h [new file with mode: 0644]
src/lib/efreet_mime.c [new file with mode: 0644]
src/lib/efreet_private.h [new file with mode: 0644]
src/lib/efreet_trash.c [new file with mode: 0644]
src/lib/efreet_uri.c [new file with mode: 0644]
src/lib/efreet_uri.h [new file with mode: 0644]
src/lib/efreet_utils.c [new file with mode: 0644]
src/lib/efreet_utils.h [new file with mode: 0644]
src/lib/efreet_xml.c [new file with mode: 0644]
src/lib/efreet_xml.h [new file with mode: 0644]
src/tests/Makefile.am [new file with mode: 0644]
src/tests/compare/Makefile.am [new file with mode: 0644]
src/tests/compare/comp.h [new file with mode: 0644]
src/tests/compare/efreet_alloc.c [new file with mode: 0644]
src/tests/compare/efreet_menu_alloc.c [new file with mode: 0644]
src/tests/data/Makefile.am [new file with mode: 0644]
src/tests/data/entry [new file with mode: 0644]
src/tests/data/entry.png [new file with mode: 0644]
src/tests/data/long.ini [new file with mode: 0644]
src/tests/data/preferences.menu [new file with mode: 0644]
src/tests/data/sub/Makefile.am [new file with mode: 0644]
src/tests/data/sub/test.desktop [new file with mode: 0644]
src/tests/data/test.desktop [new file with mode: 0644]
src/tests/data/test.ini [new file with mode: 0644]
src/tests/data/test.menu [new file with mode: 0644]
src/tests/data/test_garbage [new file with mode: 0644]
src/tests/data/test_menu_bad_comment.menu [new file with mode: 0644]
src/tests/data/test_menu_slash_bad.menu [new file with mode: 0644]
src/tests/data/test_type.desktop [new file with mode: 0644]
src/tests/ef_cache.c [new file with mode: 0644]
src/tests/ef_data_dirs.c [new file with mode: 0644]
src/tests/ef_desktop.c [new file with mode: 0644]
src/tests/ef_icon_theme.c [new file with mode: 0644]
src/tests/ef_ini.c [new file with mode: 0644]
src/tests/ef_locale.c [new file with mode: 0644]
src/tests/ef_menu.c [new file with mode: 0644]
src/tests/ef_mime.c [new file with mode: 0644]
src/tests/ef_test.h [new file with mode: 0644]
src/tests/ef_utils.c [new file with mode: 0644]
src/tests/efreet_icon_cache_dump.c [new file with mode: 0644]
src/tests/efreet_spec_test.c [new file with mode: 0644]
src/tests/efreet_suite.c [new file with mode: 0644]
src/tests/efreet_suite.h [new file with mode: 0644]
src/tests/efreet_test_efreet.c [new file with mode: 0644]
src/tests/efreet_test_efreet_cache.c [new file with mode: 0644]
src/tests/main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..99c665b
--- /dev/null
@@ -0,0 +1,152 @@
+*.o
+*.lo
+*.la
+.deps
+.libs
+ABOUT-NLS
+Makefile
+Makefile.in
+/aclocal.m4
+autom4te.cache/
+/config.cache
+/config.cache-env
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.status
+/config.sub
+/config.rpath
+/configure
+/depcomp
+doc/Doxyfile
+doc/Makefile
+doc/Makefile.in
+/doc/efreet_doxy_warnings.txt
+/doc/html/
+/doc/latex/
+/efreet-mime.pc
+/efreet-trash.pc
+/efreet.pc
+/efreet.spec
+/install-sh
+/libtool
+/ltmain.sh
+m4/libtool.m4
+m4/ltoptions.m4
+m4/ltsugar.m4
+m4/ltversion.m4
+m4/lt~obsolete.m4
+/missing
+src/bin/efreet_desktop_cache_create
+src/bin/efreet_icon_cache_create
+src/tests/compare/efreet_alloc
+src/tests/compare/efreet_menu_alloc
+src/tests/efreet_cache_test
+src/tests/efreet_icon_cache_dump
+src/tests/efreet_spec_test
+src/tests/efreet_test
+/m4/codeset.m4
+/m4/gettext.m4
+/m4/glibc21.m4
+/m4/iconv.m4
+/m4/intdiv0.m4
+/m4/intmax.m4
+/m4/inttypes-pri.m4
+/m4/inttypes.m4
+/m4/inttypes_h.m4
+/m4/isc-posix.m4
+/m4/lcmessage.m4
+/m4/lib-ld.m4
+/m4/lib-link.m4/m4/codeset.m4
+/m4/gettext.m4
+/m4/glibc21.m4
+/m4/iconv.m4
+/m4/intdiv0.m4
+/m4/intmax.m4
+/m4/inttypes-pri.m4
+/m4/inttypes.m4
+/m4/inttypes_h.m4
+/m4/isc-posix.m4
+/m4/lcmessage.m4
+/m4/lib-ld.m4
+/m4/lib-link.m4
+/m4/lib-prefix.m4
+/m4/libtool.m4
+/m4/longdouble.m4
+/m4/longlong.m4
+/m4/ltoptions.m4
+/m4/ltsugar.m4
+/m4/ltversion.m4
+/m4/lt~obsolete.m4
+/m4/nls.m4
+/m4/po.m4
+/m4/printf-posix.m4
+/m4/progtest.m4
+/m4/signed.m4
+/m4/size_max.m4
+/m4/stdint_h.m4
+/m4/uintmax_t.m4
+/m4/ulonglong.m4
+/m4/wchar_t.m4
+/m4/wint_t.m4
+/m4/xsize.m4
+/m4/glibc2.m4
+/m4/lib-prefix.m4
+/m4/libtool.m4
+/m4/longdouble.m4
+/m4/longlong.m4
+/m4/ltoptions.m4
+/m4/ltsugar.m4
+/m4/ltversion.m4
+/m4/lt~obsolete.m4
+/m4/nls.m4
+/m4/po.m4
+/m4/printf-posix.m4
+/m4/progtest.m4
+/m4/signed.m4
+/m4/size_max.m4
+/m4/stdint_h.m4
+/m4/uintmax_t.m4
+/m4/ulonglong.m4
+/m4/wchar_t.m4
+/m4/wint_t.m4
+/m4/xsize.m4
+/m4/glibc2.m4
+/m4/intl.m4
+/m4/intldir.m4
+/m4/intlmacosx.m4
+/m4/lock.m4
+/m4/visibility.m4/m4/intl.m4
+/m4/intldir.m4
+/m4/intlmacosx.m4
+/m4/lock.m4
+/m4/visibility.m4
+/po/Makefile.in.in
+/po/Makevars.template
+/po/POTFILES
+/po/Rules-quot
+/po/boldquot.sed
+/po/ecore.pot
+/po/en@boldquot.header
+/po/en@quot.header
+/po/insert-header.sin
+/po/quot.sed
+/po/remove-potcdate.sed
+/po/remove-potcdate.sin
+/po/stamp-po
+/po/Makefile.in.in
+/po/Makevars.template
+/po/POTFILES
+/po/Rules-quot
+/po/boldquot.sed
+/po/ecore.pot
+/po/en@boldquot.header
+/po/en@quot.header
+/po/insert-header.sin
+/po/quot.sed
+/po/remove-potcdate.sed
+/po/remove-potcdate.sin
+/po/*.gmo
+/po/stamp-po
+/stamp-h1
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..ad4bc4c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+dan 'dj2' sinclair
+Brian 'rephorm' Mattern
+Sebastian Dransfeld <sd@tango.flipp.net>
+Nathan 'RbdPngn' Ingersoll
+Nicholas 'Mekius' Hughart
+Albin "Lutin" Tonnerre <albin.tonnerre@gmail.com>
+Mathieu Taillefumier <mathieu.taillefumier@free.fr>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..91c13e7
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,25 @@
+Copyright notice for Efreet:
+
+Copyright (C) 2006-2011 Dan Sinclair and various contributors (see AUTHORS)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright 
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..46f0995
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,132 @@
+2011-01-29  Carsten Haitzler (The Rasterman)
+
+        1.0.0 release
+
+2011-01-29  Brian Mattern
+
+        Fix bug when you have an empty <Name></Name> in a menu
+
+2011-01-30  Carsten Haitzler (The Rasterman)
+
+        Fix tests to not build clearenv related code if not available
+
+2011-01-31  Sebastian Dransfeld
+
+       * Fix memleak, free cache icons after retrival
+       * Add temporary memory cache of eet file data
+       * Always use icon cache, remove old code
+       * Make check for stat return more explicit
+       * Check whether dirs is NULL
+       * Create internal struct for theme cache
+       * Check if theme has changed when building cache
+       * Prefix internal efreet cache keys with __
+       * Create one cache file for each theme
+       * Before doing strcmp, check if pointers are equal. They might be
+         because of mmap and stringshare.
+       * Add local icon cache, so we wont hit the cache file for each icon
+         to be retrieved
+
+2011-02-03  Sebastian Dransfeld
+
+        * Fix leak in efreet_mime_type_icon_get()
+
+2011-02-05  Sebastian Dransfeld
+
+        * Remove global log domain and make all files define log domain
+          before including efreet_private.h
+        * Remove efreet_xml_{init,shutdown}() from efreet_menu.c, it is done
+          in efreet_init()
+        * Set EFREET_MODULE_LOG_DOM variable to -1 after unregister
+        * Remove duplicate logging macros
+
+2011-02-05  Hannes Janetzek
+
+        * Clear mem cache when closing eet file
+
+2011-02-06  Sebastian Dransfeld
+
+        * Fix doc for efreet_utils.{h,c},efreet_mime.c
+        * Add comment for all internal EAPI functions
+        * Remove doc for removed _efreet_log_domain_global
+
+2011-02-07  Sebastian Dransfeld
+
+       * Don't create cache dir several times
+       * Do efreet_init before using efreet_*() functions
+       * Move lock creation to own function for desktop cache
+       * Set file owner to calling user
+
+2011-02-09  Sebastian Dransfeld
+
+       * Move theme hash from efreet_icon.c to efreet_cache.c
+       * Don't free data returned by efreet_util_cache_names
+       * Add free callback to eet hashes
+       * Remove unneeded header
+       * Check if pattern equals "*" before doing pattern match
+       * Remove EAPI from efreet_home_dir_get
+       * Make efreet_cache_icon_theme_free only used in efreet_cache.c static
+       * Move struct only used for cache to efreet_cache_private.h
+
+2011-02-10  Sebastian Dransfeld
+
+       * Move all eet cache handling to efreet_cache.c
+       * Free hashes on init error
+       * efreet_cache_icon -> efreet_icon for functions in efreet_icon.c
+       * Fix memleak in desktop cache create
+       * Pass dirs to desktop cache process as arguments
+       * Delay cache recreation with a timer
+       * Move desktop cache to efreet_cache.c, and cache all requests which
+         hit the eet cache
+
+2011-03-18  Mike Blumenkrantz
+
+        * Use eina_log more effectively
+
+2011-04-04  Tom Hacohen (TAsn)
+
+       * Fixed uri encoding when opening files.
+
+2011-08-16  Sebastian Dransfeld
+
+       * Always rebuild cache from scratch when needed, but rely on correct
+       spec behaviour to check for theme changes. This will considerably
+       speed up the cache process when there is no change, and improve the
+       correctness of the cache when changes occur. For example didn't the
+       previous behaviour handle file removal gracefully.
+
+2011-08-16  Sebastian Dransfeld
+
+       * Save whether cache changed in update file, and propagate to update
+       event.
+
+2011-12-02 Carsten Haitzler (The Rasterman)
+
+        1.1.0 release
+
+2012-01-19 Carsten Haitzler (The Rasterman)
+
+        * use eina_file_direct_ls not opendir()
+
+2012-01-19 Jérôme Pinot
+
+        * Documentation fixes.
+
+2012-04-26 Carsten Haitzler (The Rasterman)
+
+        1.2.0 release
+
+2012-05-11 Cedric Bail
+
+       * Faster loading of mime type.
+
+2012-06-25 Sebastian Dransfeld
+
+        * Added efreet_lang_reset() to reset language dependent variables and caches
+
+2012-06-25 Sebastian Dransfeld
+
+        * Support XDG_DESKTOP_DIR and read user-dirs.dirs
+
+2012-07-02 Sebastian Dransfeld
+
+       * Fix minor memory leak in cache update handler
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..23e5f25
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..a159015
--- /dev/null
@@ -0,0 +1,139 @@
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src doc
+
+MAINTAINERCLEANFILES = \
+Makefile.in \
+aclocal.m4 \
+config.guess \
+config.h.in \
+config.h.in~ \
+config.rpath \
+config.sub \
+configure \
+depcomp \
+install-sh \
+ltmain.sh \
+missing \
+$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz \
+$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 \
+$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc.tar.bz2 \
+m4/libtool.m4 \
+m4/lt~obsolete.m4 \
+m4/ltoptions.m4 \
+m4/ltsugar.m4 \
+m4/ltversion.m4 \
+m4/codeset.m4 \
+m4/gettext.m4* \
+m4/glibc2*.m4 \
+m4/iconv.m4 \
+m4/intdiv0.m4 \
+m4/intldir.m4 \
+m4/intl.m4 \
+m4/intlmacosx.m4 \
+m4/intmax.m4* \
+m4/inttypes_h.m4 \
+m4/inttypes.m4 \
+m4/inttypes-pri.m4 \
+m4/isc-posix.m4 \
+m4/lcmessage.m4 \
+m4/lib-ld.m4* \
+m4/lib-link.m4 \
+m4/lib-prefix.m4* \
+m4/lock.m4 \
+m4/longdouble.m4* \
+m4/longlong.m4* \
+m4/nls.m4 \
+m4/po.m4* \
+m4/printf-posix.m4* \
+m4/progtest.m4 \
+m4/signed.m4 \
+m4/size_max.m4* \
+m4/stdint_h.m4 \
+m4/uintmax_t.m4 \
+m4/ulonglong.m4* \
+m4/visibility.m4 \
+m4/wchar_t.m4 \
+m4/wint_t.m4* \
+m4/xsize.m4*
+
+if HAVE_PO
+SUBDIRS += po
+
+MAINTAINERCLEANFILES += \
+                       po/boldquot.sed \
+                       po/en@boldquot.header \
+                       po/en@quot.header \
+                       po/insert-header.sin \
+                       po/Makefile.in.in* \
+                       po/Makevars.template \
+                       po/quot.sed \
+                       po/remove-potcdate.sin \
+                       po/Rules-quot*
+
+endif
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = efreet.pc efreet-mime.pc efreet-trash.pc
+
+EXTRA_DIST = \
+AUTHORS \
+COPYING \
+autogen.sh \
+efreet.pc.in \
+efreet-mime.pc.in \
+efreet.spec.in \
+efreet.spec \
+m4/efl_coverage.m4\
+m4/efl_tests.m4\
+README
+
+.PHONY: doc coverage
+
+doc:
+       @echo "entering doc/"
+       make -C doc doc
+
+# Unit tests
+
+if EFL_ENABLE_TESTS
+
+check-local:
+       @./src/tests/efreet_suite
+
+else
+
+check-local:
+       @echo "reconfigure with --enable-tests"
+
+endif
+
+# Coverage report
+
+if EFL_ENABLE_COVERAGE
+lcov-reset:
+       @rm -rf $(top_builddir)/coverage
+       @find $(top_builddir) -name "*.gcda" -delete
+       @lcov --zerocounters --directory $(top_builddir)
+
+lcov-report:
+       @mkdir $(top_builddir)/coverage
+       lcov --capture --compat-libtool --output-file $(top_builddir)/coverage/coverage.info --directory $(top_builddir)
+       lcov --remove $(top_builddir)/coverage/coverage.info '*.h' --output-file $(top_builddir)/coverage/coverage.cleaned.info
+       genhtml -t "$(PACKAGE_STRING)" -o $(top_builddir)/coverage/html $(top_builddir)/coverage/coverage.cleaned.info
+       @echo "Coverage Report at $(top_builddir)/coverage/html"
+
+coverage:
+       @$(MAKE) lcov-reset
+       @$(MAKE) check
+       @$(MAKE) lcov-report
+else
+lcov-reset:
+       @echo "reconfigure with --enable-coverage"
+
+lcov-report:
+       @echo "reconfigure with --enable-coverage"
+
+coverage:
+       @echo "reconfigure with --enable-tests --enable-coverage"
+endif
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..12d5666
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,58 @@
+Efreet 1.7.0
+
+Changes since Efreet 1.2.0:
+---------------------------
+
+Improvements:
+
+    * Faster mime type loading.
+
+
+Additions:
+
+    * Support XDG_DESKTOP_DIR
+    * efreet_lang_reset() for refreshing language variables and caches after locale switches
+
+Efreet 1.2.0
+
+Changes since Efreet 1.1.0:
+---------------------------
+
+Improvements:
+
+    * use eina_file_direct_ls() instead of opendir().
+    * compilation warnings fixed.
+    * documentation improvements/fixes
+    
+Changes since Efreet 1.0.0:
+---------------------------
+
+Fixes:
+
+    * when you have an empty <Name></Name> in a menu
+    * tests don't do clearenv when it is not available
+    * memory leak fixed in cache icon retrieval
+    * check for NULL dirs in caches
+    * check for theme change when rebuilding cache
+    * leak in efreet_mime_type_icon_get
+    * reset log domain to -1 on unregister
+    * clear memory cache when closing eet file
+    * fixed documentation
+    * remove EAPI from efreet_home_dir_get as it wasn't exported in .h's
+    * don't leak hashes on init error
+    * no more memleak in desktop cache creation
+    * fixed uri encoding when opening files
+
+Improvements:
+
+    * added temporary memory cache for eet file data
+    * always use icon cache
+    * faster string comapre with poter equality for strings
+    * local log domains per file
+    * remove exess init functions for xml
+    * stop creating cache dir multiple times
+    * change ownership of cache files to calling user
+    * fast path for "*" pattern match
+    * delay cache creation with a timer to avoid storms
+    * always rebuild cache from 0 when needed and be more correct
+    * store cache change flags in update file and propagate to update event
diff --git a/NOTES b/NOTES
new file mode 100644 (file)
index 0000000..485be66
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,48 @@
+
+rephorm one thing. if you plan to add .desktop editing, according to the
+spec you need to keep EVERYTHING around (even sections / keys you don't know
+/ care about)
+dj2 k
+rephorm but for most usage (read only) thats a waste of memory
+dj2 hm, i guess the best way to do that is keep everything in a hash
+dj2 and write accessor functions
+dj2 or pull the common stuff out to functions and leve the rest in the hash
+rephorm and maybe have a flag for 'readonly' to kill the hash
+dj2 yea, will have to put some tought into the api for that
+
+rephorm ooh. comments need to be preserved also in edits
+
+
+
+
+rephorm efreet_xml has one bug that i see
+rephorm if you have something like <tag>before<child />after</tag>, you
+can't get to the 'after' text
+dj2 hm, will have to look at that
+rephorm (it stores 'before' as the text on 'tag')
+rephorm it should probably create child nodes with the text
+rephorm so tag would have 3 children, text: before, tag: child and text: after
+
+
+efreet_desktop_string_list_parse() needs to optionally use comma (',') as 
+the separator if the desktop version is < 1.0  (but, what if it isn't set??)
+
+
+desktop_command_get:
+  check for needed types (nNfFuU)
+
+  get lists of needed types (dirs, fullpaths, urls, etc)
+
+  if type in uU:
+    create Pending struct
+    start downloads, pass Pending in as data
+  else:
+    if tyep in UF...
+    exec
+
+download cb:
+  if type in fF
+
+  
+
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..05ea494
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+Efreet 1.7.0
+
+******************************************************************************
+
+ FOR ANY ISSUES PLEASE EMAIL:
+ enlightenment-devel@lists.sourceforge.net
+  
+******************************************************************************
+  
+Requirements:
+-------------
+
+Must:
+  libc
+  eina (at least 1.1.0)
+  ecore (at least 1.1.0)
+  ecore-file (at least 1.1.0)
+  eet (at least 1.5.0)
+
+An implementation of several specifications from freedesktop.org intended for
+use in Enlightenment DR17 (e17) and other applications using the Enlightenment
+Foundation Libraries (EFL). Currently, the following specifications are
+included:
+  * Base Directory
+  * Desktop Entry
+  * Icon Theme
+  * Menu
+  * Trash
+  * Mime
+
+------------------------------------------------------------------------------
+COMPILING AND INSTALLING:
+
+  ./configure
+  make
+(as root unless you are installing in your users directories):
+  make install
+      
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..a40fb31
--- /dev/null
+++ b/TODO
@@ -0,0 +1,24 @@
+TODO
+----
+- efreet_desktop_command_* should free the exec strings for you (set free cb on execs list in _build()). need to make sure users of this function are updated when this change is made (e.g. e17's exec cb)
+- Efreet_Menu should setup an Ecore_File_Monitor on the .menu files and the
+  app_dir and reload the menu when needed
+
+- The move handling is wrong when moving to a destination with /'s in the
+  name. We should be creating intermediate menus for each of the /'d items
+  instead of just making the name as we do now
+
+- Add support for searching for a list of fallback icons in the current theme
+  BEFORE looking in the inherited themes (e.g. for mime-types that go from
+  specific to general).
+
+- If a theme has several icon types (png, xpm..) prefer the first in the
+  extension list.
+
+- Add some ref/free tracking to check if users aren't abusing the system.
+
+Notes from the Menu Spec test
+-----------------------------
+- We're appending the / to the menu names in the test case. We should
+  possibly be doing that in the efreet code itself.
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..2a59cac
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f aclocal.m4 ltmain.sh
+
+touch ABOUT-NLS
+
+echo "Running autopoint..." ; autopoint -f || :
+echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS -I m4 || exit 1
+echo "Running autoheader..." ; autoheader || exit 1
+echo "Running autoconf..." ; autoconf || exit 1
+echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1
+echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1
+
+W=0
+
+rm -f config.cache-env.tmp
+echo "OLD_PARM=\"$@\"" >> config.cache-env.tmp
+echo "OLD_CFLAGS=\"$CFLAGS\"" >> config.cache-env.tmp
+echo "OLD_PATH=\"$PATH\"" >> config.cache-env.tmp
+echo "OLD_PKG_CONFIG_PATH=\"$PKG_CONFIG_PATH\"" >> config.cache-env.tmp
+echo "OLD_LDFLAGS=\"$LDFLAGS\"" >> config.cache-env.tmp
+
+cmp config.cache-env.tmp config.cache-env >> /dev/null
+if [ $? -ne 0 ]; then
+       W=1;
+fi
+
+if [ $W -ne 0 ]; then
+       echo "Cleaning configure cache...";
+       rm -f config.cache config.cache-env
+       mv config.cache-env.tmp config.cache-env
+else
+       rm -f config.cache-env.tmp
+fi
+
+if [ -z "$NOCONFIGURE" ]; then
+       ./configure -C "$@"
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..9535101
--- /dev/null
@@ -0,0 +1,285 @@
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+m4_define([v_maj], [1])
+m4_define([v_min], [7])
+m4_define([v_mic], [0])
+m4_define([v_rev], m4_esyscmd([(svnversion "${SVN_REPO_PATH:-.}" | grep -v '\(export\|Unversioned directory\)' || echo 0) | awk -F : '{printf("%s\n", $1);}' | tr -d ' :MSP\n']))
+m4_if(v_rev, [0], [m4_define([v_rev], m4_esyscmd([git log 2> /dev/null | (grep -m1 git-svn-id || echo 0) | sed -e 's/.*@\([0-9]*\).*/\1/' | tr -d '\n']))])
+##--   When released, remove the dnl on the below line
+m4_undefine([v_rev])
+##--   When doing snapshots - change soname. remove dnl on below line
+dnl m4_define([relname], [ver-pre-svn-07])
+dnl m4_define([v_rel], [-release relname])
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+m4_ifdef([v_rev], [m4_define([v_ver], [v_maj.v_min.v_mic.v_rev])], [m4_define([v_ver], [v_maj.v_min.v_mic])])
+m4_define([lt_cur], m4_eval(v_maj + v_min))
+m4_define([lt_rev], v_mic)
+m4_define([lt_age], v_min)
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+
+AC_INIT([efreet], [v_ver], [enlightenment-devel@lists.sourceforge.net])
+AC_PREREQ([2.52])
+AC_CONFIG_SRCDIR([configure.ac])
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_CONFIG_HEADERS([config.h])
+AH_TOP([
+#ifndef EFL_CONFIG_H__
+#define EFL_CONFIG_H__
+])
+AH_BOTTOM([
+#endif /* EFL_CONFIG_H__ */
+])
+
+AM_INIT_AUTOMAKE([1.6 dist-bzip2])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AC_LIBTOOL_WIN32_DLL
+define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl
+define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl
+AC_PROG_LIBTOOL
+
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+m4_ifdef([v_rev], , [m4_define([v_rev], [0])])
+m4_ifdef([v_rel], , [m4_define([v_rel], [])])
+AC_DEFINE_UNQUOTED(VMAJ, [v_maj], [Major version])
+AC_DEFINE_UNQUOTED(VMIN, [v_min], [Minor version])
+AC_DEFINE_UNQUOTED(VMIC, [v_mic], [Micro version])
+AC_DEFINE_UNQUOTED(VREV, [v_rev], [Revison])
+version_info="lt_cur:lt_rev:lt_age"
+release_info="v_rel"
+AC_SUBST(version_info)
+AC_SUBST(release_info)
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+VMAJ=v_maj
+AC_SUBST(VMAJ)
+
+AM_GNU_GETTEXT_VERSION(0.17)
+
+m4_ifdef([AM_GNU_GETTEXT], [
+AM_GNU_GETTEXT([external])
+po_makefile_in=po/Makefile.in
+have_po="yes"
+],[
+have_po="no"
+])
+AC_SUBST(LTLIBINTL)
+LOCALE_DIR="${localedir}"
+AC_SUBST(LOCALE_DIR)
+
+if test "x${POSUB}" = "x" ; then
+   have_po="no"
+fi
+
+AM_CONDITIONAL([HAVE_PO], [test "x${have_po}" = "xyes"])
+
+### Needed information
+
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+
+
+### Additional options to configure
+
+AC_ARG_ENABLE([strict-spec],
+   [AC_HELP_STRING([--enable-strict-spec], [Enable strict spec compliance @<:@default=disabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       enable_strict_spec="yes"
+    else
+       enable_strict_spec="no"
+    fi
+   ],
+   [enable_strict_spec="no"])
+
+if test "x${enable_strict_spec}" = "xyes" ; then
+   AC_DEFINE([STRICT_SPEC], [1], [Strict Spec Compliance])
+fi
+
+AC_ARG_ENABLE([sloppy-spec],
+   [AC_HELP_STRING([--disable-sloppy-spec], [Enable sloppy spec compliance @<:@default=enabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       enable_sloppy_spec="yes"
+    else
+       enable_sloppy_spec="no"
+    fi
+   ],
+   [enable_sloppy_spec="yes"])
+
+if test "x${enable_sloppy_spec}" = "xyes" ; then
+   AC_DEFINE([SLOPPY_SPEC], [1], [Sloppy Spec Compliance])
+fi
+
+
+#AC_ARG_ENABLE(hidden-visibility,
+#      [AC_HELP_STRING([--enable-hidden-visibility],[Enable hidden visibility])],
+#      [enable_hidden_visibility=$enableval], [enable_hidden_visibility="auto"])
+#if test "x$enable_hidden_visibility" = "xyes" ; then
+#    CPPFLAGS="$CPPFLAGS -fvisibility=hidden"
+#else
+#    AC_DEFINE(DEFAULT_VISIBLITY, 1, [Default visibility])
+#fi
+#AM_CONDITIONAL(DEFAULT_VISIBILITY, test "x$enable_hidden_visibility" != "xyes")
+AM_CONDITIONAL(DEFAULT_VISIBILITY, false)
+
+
+### Checks for programs
+AC_PROG_CC
+
+# pkg-config
+PKG_PROG_PKG_CONFIG
+
+# Check whether pkg-config supports Requires.private
+if $PKG_CONFIG --atleast-pkgconfig-version 0.22; then
+   pkgconfig_requires_private="Requires.private"
+else
+   pkgconfig_requires_private="Requires"
+fi
+AC_SUBST(pkgconfig_requires_private)
+
+# doxygen program for documentation building
+EFL_CHECK_DOXYGEN([build_doc="yes"], [build_doc="no"])
+
+
+### Checks for libraries
+
+# Evil library for compilation on Windows CE
+
+EFL_EFREET_BUILD=""
+EFL_EFREET_MIME_BUILD=""
+EFL_EFREET_TRASH_BUILD=""
+case "$host_os" in
+   mingw*)
+      PKG_CHECK_MODULES([EVIL], [evil >= 1.6.99])
+      AC_DEFINE(HAVE_EVIL, 1, [Set to 1 if evil package is installed])
+      requirement_efreet="evil"
+      EFL_EFREET_BUILD="-DEFL_EFREET_BUILD"
+      EFL_EFREET_MIME_BUILD="-DEFL_EFREET_MIME_BUILD"
+      EFL_EFREET_TRASH_BUILD="-DEFL_EFREET_TRASH_BUILD"
+   ;;
+esac
+AC_SUBST(EFL_EFREET_BUILD)
+AC_SUBST(EFL_EFREET_MIME_BUILD)
+AC_SUBST(EFL_EFREET_TRASH_BUILD)
+
+requirement_efreet="ecore-file >= 1.6.99 ecore >= 1.6.99 eet >= 1.6.99 eina >= 1.6.99 ${requirement_efreet}"
+PKG_CHECK_MODULES(EFREET, [${requirement_efreet}])
+PKG_CHECK_MODULES(EINA, [eina >= 1.6.99])
+
+
+### Checks for header files
+
+AC_CHECK_HEADERS([netinet/in.h arpa/inet.h])
+
+### Checks for types
+
+
+### Checks for structures
+
+
+### Checks for compiler characteristics
+
+AC_C_CONST
+AC_C___ATTRIBUTE__
+AM_PROG_CC_STDC
+AC_HEADER_STDC
+
+case "${host_os}" in
+   mingw*)
+      EFREET_CFLAGS="${EFREET_CFLAGS} ${EVIL_CFLAGS}"
+   ;;
+esac
+
+if ! test "x${VMIC}" = "x" ; then
+   EFL_COMPILER_FLAG([-Wall])
+   EFL_COMPILER_FLAG([-W])
+fi
+
+EFL_COMPILER_FLAG([-Wshadow])
+
+EFL_CHECK_PATH_MAX
+
+
+### Checks for linker characteristics
+lt_enable_auto_import=""
+WIN32_LIBS=""
+case "${host_os}" in
+   mingw*)
+      WIN32_LIBS="-lws2_32"
+      lt_enable_auto_import="-Wl,--enable-auto-import"
+   ;;
+esac
+AC_SUBST(WIN32_LIBS)
+AC_SUBST(lt_enable_auto_import)
+
+
+### Checks for library functions
+AC_ISC_POSIX
+AC_FUNC_ALLOCA
+AC_CHECK_FUNCS(strlcpy clearenv)
+
+### Unit tests, coverage
+
+EFL_CHECK_TESTS([enable_tests="yes"], [enable_tests="no"])
+
+EFL_CHECK_COVERAGE([${enable_tests}], [enable_coverage="yes"], [enable_coverage="no"])
+EFREET_CFLAGS="${EFREET_CFLAGS} ${EFL_COVERAGE_CFLAGS}"
+EFREET_LIBS="${EFREET_LIBS} ${EFL_COVERAGE_LIBS}"
+if test "x$enable_coverage" = "xyes" ; then
+   EFREET_CFLAGS="${EFREET_CFLAGS} ${EFL_DEBUG_CFLAGS}"
+fi
+
+AC_SUBST(requirement_efreet)
+
+AC_OUTPUT([
+efreet.spec
+efreet.pc
+efreet-mime.pc
+efreet-trash.pc
+Makefile
+doc/Makefile
+doc/Doxyfile
+src/Makefile
+src/lib/Makefile
+src/bin/Makefile
+src/tests/Makefile
+src/tests/data/Makefile
+src/tests/data/sub/Makefile
+src/tests/compare/Makefile
+$po_makefile_in
+])
+
+
+#####################################################################
+## Info
+
+echo
+echo
+echo
+echo "------------------------------------------------------------------------"
+echo "$PACKAGE $VERSION"
+echo "------------------------------------------------------------------------"
+echo
+echo "Configuration Options Summary:"
+echo
+echo "  Specification compliance:"
+echo "    Strict.............: ${enable_strict_spec}"
+echo "    Sloppy.............: ${enable_sloppy_spec}"
+echo
+echo "  Tests................: ${enable_tests}"
+echo "  Coverage.............: ${enable_coverage}"
+echo
+echo "  Documentation........: ${build_doc}"
+echo
+echo "Compilation............: make (or gmake)"
+echo "  CPPFLAGS.............: $CPPFLAGS"
+echo "  CFLAGS...............: $CFLAGS"
+echo "  LDFLAGS..............: $LDFLAGS"
+echo
+echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
+echo "  prefix...............: $prefix"
+echo
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
new file mode 100644 (file)
index 0000000..cd56fea
--- /dev/null
@@ -0,0 +1,1692 @@
+# Doxyfile 1.7.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = Efreet
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description for a project that appears at the top of each page and should give viewer a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = .
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = NO
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = @top_srcdir@/src/lib
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          = *.c \
+                         *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             = @top_srcdir@/doc/images
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 2
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = efreet_ \
+                         Efreet_ \
+                         _efreet_ \
+                         _Efreet_ \
+                         EFREET_ \
+                         _EFREET_
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            = @srcdir@/head.html
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            = @srcdir@/foot.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = @srcdir@/e.css
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = YES
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.enlightenment.Efreet
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.enlightenment.Efreet
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Enlightenment
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.enlightenment.Efreet
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.enlightenment.Efreet
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = YES
+
+# This tag can be used to set the number of enum values (range [0,1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+# Note that a value of 0 will completely suppress the enum values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = NO
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             = EINA_MAGIC_DEBUG \
+                         __UNUSED__= \
+                         EINA_ARG_NONNULL()= \
+                         EINA_MALLOC= \
+                         EINA_WARN_UNUSED_RESULT= \
+                         EAPI= \
+                         EINA_PURE= \
+                         EINA_CONST=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = NO
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, svg, gif or svg.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..ad5250b
--- /dev/null
@@ -0,0 +1,32 @@
+MAINTAINERCLEANFILES = Makefile.in Doxyfile efreet_doxy_warnings.txt
+
+.PHONY: doc
+
+PACKAGE_DOCNAME = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc
+
+if EFL_BUILD_DOC
+
+doc-clean:
+       rm -rf html/ latex/ man/ xml/ $(top_builddir)/$(PACKAGE_DOCNAME).tar*
+
+doc: all doc-clean
+       $(efl_doxygen)
+       cp $(srcdir)/images/* html/
+       rm -rf $(PACKAGE_DOCNAME).tar*
+       mkdir -p $(PACKAGE_DOCNAME)/doc
+       cp -R html/ latex/ $(PACKAGE_DOCNAME)/doc
+       tar cf $(PACKAGE_DOCNAME).tar $(PACKAGE_DOCNAME)/
+       bzip2 -9 $(PACKAGE_DOCNAME).tar
+       rm -rf $(PACKAGE_DOCNAME)/
+       mv $(PACKAGE_DOCNAME).tar.bz2 $(top_builddir)
+
+clean-local: doc-clean
+
+else
+
+doc:
+       @echo "Documentation not built. Run ./configure --help"
+
+endif
+
+EXTRA_DIST = Doxyfile.in $(wildcard images/*.*) e.css head.html foot.html
diff --git a/doc/e.css b/doc/e.css
new file mode 100644 (file)
index 0000000..8697a3a
--- /dev/null
+++ b/doc/e.css
@@ -0,0 +1,218 @@
+/*
+    Author:
+        Andres Blanc <andresblanc@gmail.com>
+       DaveMDS Andreoli <dave@gurumeditation.it>
+
+    Supported Browsers:
+        ie7, opera9, konqueror4 and firefox3
+
+        Please use a different file for ie6, ie5, etc. hacks.
+*/
+
+
+/* Necessary to place the footer at the bottom of the page */
+html, body {
+       height: 100%;
+       margin: 0px;
+       padding: 0px;
+}
+
+#container {
+       min-height: 100%;
+       height: auto !important;
+       height: 100%;
+       margin: 0 auto -53px;
+}
+
+#footer, #push {
+       height: 53px;
+}
+
+
+* html #container {
+       height: 100%;
+}
+
+/* Prevent floating elements overflowing containers */
+.clear {
+       clear: both;
+       width: 0px;
+       height: 0px;
+}
+
+/* Flexible & centered layout from 750 to 960 pixels */
+.layout {
+       max-width: 960px;
+       min-width: 760px;
+       margin-left: auto;
+       margin-right: auto;
+}
+
+body {
+       /*font-family: Lucida Grande, Helvetica, sans-serif;*/
+       font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif
+}
+
+/* Prevent design overflowing the viewport in small resolutions */
+#container {
+       padding-right: 17px;
+       padding-left: 17px;
+       background-image: url(head_bg.png);
+       background-repeat: repeat-x;
+}
+
+#header {
+       width: 100%;
+       height: 102px;
+}
+
+#header h1 {
+       width: 63px;
+       height: 63px;
+       background-image: url(e.png);
+       background-repeat: no-repeat;
+       position: absolute;
+       margin: 0px;
+}
+
+#header h1 span {
+       display: none;
+}
+
+#header h2 {
+       display: none;
+}
+
+/* .menu-container is used to set properties common to .menu and .submenu */
+#header .menu-container {
+}
+
+#header .menu-container ul {
+       list-style-type: none;
+       list-style-position: inside;
+       margin: 0;
+}
+
+#header .menu-container li {
+       display: block;
+       float: right;
+}
+
+#header .menu {
+       height: 63px;
+       display: block;
+       background-image: url(menu_bg.png);
+       background-repeat: repeat-x;
+}
+
+#header .menu ul {
+       height: 100%;
+       display: block;
+       background-image: url(menu_bg_last.png);
+       background-repeat: no-repeat;
+       background-position: top right;
+       padding-right: 17px;
+}
+
+#header .menu li {
+       height: 100%;
+       text-align: center;
+       background-image: url(menu_bg_unsel.png);
+       background-repeat: no-repeat;
+}
+
+#header .menu a {
+       height: 100%;
+       display: block;
+       color: #cdcdcd;
+       text-decoration: none;
+       font-size: 10pt;
+       line-height: 59px;
+       text-align: center;
+       padding: 0px 15px 0px 15px;
+}
+
+#header .menu li:hover {
+       background-image: url(menu_bg_hover.png);
+       background-repeat: no-repeat;
+}
+
+#header .menu li:hover a {
+       color: #FFFFFF;
+}
+
+#header .menu li.current {
+       background-image: url(menu_bg_current.png);
+       background-repeat: no-repeat;
+}
+
+#header .menu li.current a {
+       color: #646464;
+}
+
+
+/* Hide all the submenus but the current */
+#header .submenu ul {
+       display: none;
+}
+
+#header .submenu .current {
+       display: block;
+}
+
+#header .submenu {
+       font: bold 10px verdana,'Bitstream Vera Sans',helvetica,arial,sans-serif;
+       margin-top: 10px;
+}
+
+#header .submenu a {
+       color: #888888;
+       text-decoration: none;
+       font-size: 0.9em;
+       line-height: 15px;
+       padding:0px 5px 0px 5px;
+}
+
+#header .submenu a:hover {
+       color: #444444;
+}
+
+#header .submenu li {
+       border-left: 1px solid #DDDDDD;
+}
+
+#header .submenu li:last-child {
+       border-left: 0;
+}
+
+#header .doxytitle {
+       position: absolute;
+       font-size: 1.8em;
+       font-weight: bold;
+       color: #444444;
+       line-height: 35px;
+}
+
+#header small {
+       font-size: 0.4em;
+}
+
+#footer {
+       background-image: url(foot_bg.png);
+       width: 100%;
+}
+
+#footer table {
+       width: 100%;
+       text-align: center;
+       white-space: nowrap;
+       padding: 5px 30px 5px 30px;
+       font-size: 0.8em;
+       font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif;
+       color: #888888;
+}
+
+#footer td.copyright {
+       width: 100%;
+}
+
diff --git a/doc/foot.html b/doc/foot.html
new file mode 100644 (file)
index 0000000..78ef911
--- /dev/null
@@ -0,0 +1,19 @@
+ <div id="push"></div>
+ </div> <!-- #content -->
+  </div> <!-- .layout -->
+ </div> <!-- #container -->
+  <div id="footer">
+    <table><tr>
+      <td class="poweredby"><img src="doxygen.png"></td>
+      <td class="copyright">Copyright &copy;$year Enlightenment</td>
+      <td class="generated">Docs generated $datetime</td>
+    </tr></table>
+  </div>
+
+
+</body>
+</html>
diff --git a/doc/head.html b/doc/head.html
new file mode 100644 (file)
index 0000000..f9be916
--- /dev/null
@@ -0,0 +1,68 @@
+<html>
+<head>
+    <title>$title</title>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8">
+    <meta name="author" content="Andres Blanc" >
+    
+    <link rel="icon" href="images/favicon.png" type="image/x-icon">
+    <link rel="shortcut icon" href="images/favicon.png" type="image/x-icon">
+    <link rel="icon" href="images/favicon.png" type="image/ico">
+    <link rel="shortcut icon" href="images/favicon.png" type="image/ico">
+
+    <link rel="stylesheet" type="text/css" media="screen" href="e.css">
+    <link rel="stylesheet" type="text/css" media="screen" href="edoxy.css">
+</head>
+
+<body>
+
+<div id="container">
+
+<div id="header">
+<div class="layout">
+    
+    <h1><span>Enlightenment</span></h1>
+    <h2><span>Beauty at your fingertips</span></h2>
+
+    <div class="menu-container">
+        <div class="menu">
+            <ul>
+               <li class="current"><a href="http://web.enlightenment.org/p.php?p=docs">Docs</a></li>
+                <li><a href="http://trac.enlightenment.org/e">Tracker</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=contact">Contact</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=contribute">Contribute</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=support">Support</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=download">Download</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=about">About</a></li>
+                <li><a href="http://www.enlightenment.org/p.php?p=news">News</a></li>
+                <li><a href="http://www.enlightenment.org/">Home</a></li>
+            </ul>
+        </div>
+    </div>
+
+    <div class="doxytitle">
+        $projectname Documentation <small>at $date</small>
+    </div>
+
+    <div class="menu-container">
+        <div class="submenu">
+            <ul class="current">
+                <li><a href="group__Efreet__Utils.html">Utils</a></li>
+                <li><a href="group__Efreet__Uri.html">URI</a></li>
+                <li><a href="group__Efreet__Trash.html">Trash</a></li>
+                <li><a href="group__Efreet__Mime.html">Mime Types</a></li>
+                <li><a href="group__Efreet__Menu.html">Menu</a></li>
+                <li><a href="group__Efreet__Icon.html">Icon</a></li>
+                <li><a href="group__Efreet__Desktop.html">Desktop</a></li>
+                <li><a href="group__Efreet__Base.html">Base</a></li>
+               <li class="current"><a  href="index.html">Main Page</a></li>
+            </ul>
+        </div>
+    </div>
+
+
+    <div class="clear"></div>
+</div>
+</div>
+
+<div id="content">
+<div class="layout">
diff --git a/doc/images/e.png b/doc/images/e.png
new file mode 100644 (file)
index 0000000..b3884a5
Binary files /dev/null and b/doc/images/e.png differ
diff --git a/doc/images/edoxy.css b/doc/images/edoxy.css
new file mode 100644 (file)
index 0000000..3caf7a9
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * This file contain a custom doxygen style to match e.org graphics
+ */
+
+
+
+/* BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
+       font-family: Geneva, Arial, Helvetica, sans-serif;
+}*/ 
+BODY, TD {
+       font-size: 12px;
+}
+H1 {
+       text-align: center;
+       font-size: 160%;
+}
+H2 {
+       font-size: 120%;
+}
+H3 {
+       font-size: 100%;
+}
+CAPTION { 
+       font-weight: bold 
+}
+DIV.qindex {
+       width: 100%;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       padding: 2px;
+       line-height: 140%;
+}
+DIV.navpath {
+       width: 100%;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       padding: 2px;
+       line-height: 140%;
+}
+DIV.navtab {
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       margin-right: 15px;
+       padding: 2px;
+}
+TD.navtab {
+       font-size: 70%;
+}
+A.qindex {
+       text-decoration: none;
+       font-weight: bold;
+       color: #1A419D;
+}
+A.qindex:visited {
+       text-decoration: none;
+       font-weight: bold;
+       color: #1A419D
+}
+A.qindex:hover {
+       text-decoration: none;
+       background-color: #ddddff;
+}
+A.qindexHL {
+       text-decoration: none;
+       font-weight: bold;
+       background-color: #6666cc;
+       color: #ffffff;
+       border: 1px double #9295C2;
+}
+A.qindexHL:hover {
+       text-decoration: none;
+       background-color: #6666cc;
+       color: #ffffff;
+}
+A.qindexHL:visited { 
+       text-decoration: none; 
+       background-color: #6666cc; 
+       color: #ffffff 
+}
+A.el { 
+       text-decoration: none; 
+       font-weight: bold 
+}
+A.elRef { 
+       font-weight: bold 
+}
+A.code:link { 
+       text-decoration: none; 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.code:visited { 
+       text-decoration: none; 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.codeRef:link { 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.codeRef:visited { 
+       font-weight: normal; 
+       color: #0000FF
+}
+A:hover, A:visited:hover { 
+       text-decoration: none;  
+       /* background-color: #f2f2ff; */
+       color: #000055;
+}
+A.anchor {
+       color: #000;
+}
+DL.el { 
+       margin-left: -1cm 
+}
+.fragment {
+       font-family: monospace, fixed;
+       font-size: 95%;
+}
+PRE.fragment {
+       border: 1px solid #CCCCCC;
+       background-color: #f5f5f5;
+       margin-top: 4px;
+       margin-bottom: 4px;
+       margin-left: 2px;
+       margin-right: 8px;
+       padding-left: 6px;
+       padding-right: 6px;
+       padding-top: 4px;
+       padding-bottom: 4px;
+}
+DIV.ah { 
+       background-color: black; 
+       font-weight: bold; 
+       color: #ffffff; 
+       margin-bottom: 3px; 
+       margin-top: 3px 
+}
+
+DIV.groupHeader {
+       margin-left: 16px;
+       margin-top: 12px;
+       margin-bottom: 6px;
+       font-weight: bold;
+}
+DIV.groupText { 
+       margin-left: 16px; 
+       font-style: italic; 
+       font-size: 90% 
+}
+/*BODY {
+       background: white;
+       color: black;
+       margin-right: 20px;
+       margin-left: 20px;
+}*/
+TD.indexkey {
+       background-color: #e8eef2;
+       font-weight: bold;
+       padding-right  : 10px;
+       padding-top    : 2px;
+       padding-left   : 10px;
+       padding-bottom : 2px;
+       margin-left    : 0px;
+       margin-right   : 0px;
+       margin-top     : 2px;
+       margin-bottom  : 2px;
+       border: 1px solid #CCCCCC;
+}
+TD.indexvalue {
+       background-color: #e8eef2;
+       font-style: italic;
+       padding-right  : 10px;
+       padding-top    : 2px;
+       padding-left   : 10px;
+       padding-bottom : 2px;
+       margin-left    : 0px;
+       margin-right   : 0px;
+       margin-top     : 2px;
+       margin-bottom  : 2px;
+       border: 1px solid #CCCCCC;
+}
+TR.memlist {
+       background-color: #f0f0f0; 
+}
+P.formulaDsp { 
+       text-align: center; 
+}
+IMG.formulaDsp {
+}
+IMG.formulaInl { 
+       vertical-align: middle; 
+}
+SPAN.keyword       { color: #008000 }
+SPAN.keywordtype   { color: #604020 }
+SPAN.keywordflow   { color: #e08000 }
+SPAN.comment       { color: #800000 }
+SPAN.preprocessor  { color: #806020 }
+SPAN.stringliteral { color: #002080 }
+SPAN.charliteral   { color: #008080 }
+SPAN.vhdldigit     { color: #ff00ff }
+SPAN.vhdlchar      { color: #000000 }
+SPAN.vhdlkeyword   { color: #700070 }
+SPAN.vhdllogic     { color: #ff0000 }
+
+.mdescLeft {
+       padding: 0px 8px 4px 8px;
+       font-size: 80%;
+       font-style: italic;
+       background-color: #FAFAFA;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+.mdescRight {
+        padding: 0px 8px 4px 8px;
+       font-size: 80%;
+       font-style: italic;
+       background-color: #FAFAFA;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+.memItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memItemRight {
+       padding: 1px 8px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: none;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplItemRight {
+       padding: 1px 8px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: none;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplParams {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       color: #606060;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.search { 
+       color: #003399;
+       font-weight: bold;
+}
+FORM.search {
+       margin-bottom: 0px;
+       margin-top: 0px;
+}
+INPUT.search { 
+       font-size: 75%;
+       color: #000080;
+       font-weight: normal;
+       background-color: #e8eef2;
+}
+TD.tiny { 
+       font-size: 75%;
+}
+a {
+       color: #1A41A8;
+}
+a:visited {
+       color: #2A3798;
+}
+.dirtab { 
+       padding: 4px;
+       border-collapse: collapse;
+       border: 1px solid #84b0c7;
+}
+TH.dirtab { 
+       background: #e8eef2;
+       font-weight: bold;
+}
+HR { 
+       height: 1px;
+       border: none;
+       border-top: 1px solid black;
+}
+
+/* Style for detailed member documentation */
+.memtemplate {
+       font-size: 80%;
+       color: #606060;
+       font-weight: normal;
+       margin-left: 3px;
+} 
+.memnav { 
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       margin-right: 15px;
+       padding: 2px;
+}
+.memitem {
+       padding: 4px;
+       background-color: #eef3f5;
+       border-width: 1px;
+       border-style: solid;
+       border-color: #dedeee;
+       -moz-border-radius: 8px 8px 8px 8px;
+}
+.memname {
+       white-space: nowrap;
+       font-weight: bold;
+}
+.memdoc{
+       padding-left: 10px;
+}
+.memproto {
+       background-color: #d5e1e8;
+       width: 100%;
+       border-width: 1px;
+       border-style: solid;
+       border-color: #84b0c7;
+       font-weight: bold;
+       -moz-border-radius: 8px 8px 8px 8px;
+}
+.paramkey {
+       text-align: right;
+}
+.paramtype {
+       white-space: nowrap;
+}
+.paramname {
+       color: #602020;
+       font-style: italic;
+       white-space: nowrap;
+}
+/* End Styling for detailed member documentation */
+
+/* for the tree view */
+.ftvtree {
+       font-family: sans-serif;
+       margin:0.5em;
+}
+/* these are for tree view when used as main index */
+.directory { 
+       font-size: 9pt; 
+       font-weight: bold; 
+}
+.directory h3 { 
+       margin: 0px; 
+       margin-top: 1em; 
+       font-size: 11pt; 
+}
+
+/* The following two styles can be used to replace the root node title */
+/* with an image of your choice.  Simply uncomment the next two styles, */
+/* specify the name of your image and be sure to set 'height' to the */
+/* proper pixel height of your image. */
+
+/* .directory h3.swap { */
+/*     height: 61px; */
+/*     background-repeat: no-repeat; */
+/*     background-image: url("yourimage.gif"); */
+/* } */
+/* .directory h3.swap span { */
+/*     display: none; */
+/* } */
+
+.directory > h3 { 
+       margin-top: 0; 
+}
+.directory p { 
+       margin: 0px; 
+       white-space: nowrap; 
+}
+.directory div { 
+       display: none; 
+       margin: 0px; 
+}
+.directory img { 
+       vertical-align: -30%; 
+}
+/* these are for tree view when not used as main index */
+.directory-alt { 
+       font-size: 100%; 
+       font-weight: bold; 
+}
+.directory-alt h3 { 
+       margin: 0px; 
+       margin-top: 1em; 
+       font-size: 11pt; 
+}
+.directory-alt > h3 { 
+       margin-top: 0; 
+}
+.directory-alt p { 
+       margin: 0px; 
+       white-space: nowrap; 
+}
+.directory-alt div { 
+       display: none; 
+       margin: 0px; 
+}
+.directory-alt img { 
+       vertical-align: -30%; 
+}
+
diff --git a/doc/images/foot_bg.png b/doc/images/foot_bg.png
new file mode 100644 (file)
index 0000000..b24f3a4
Binary files /dev/null and b/doc/images/foot_bg.png differ
diff --git a/doc/images/head_bg.png b/doc/images/head_bg.png
new file mode 100644 (file)
index 0000000..081dc13
Binary files /dev/null and b/doc/images/head_bg.png differ
diff --git a/doc/images/menu_bg.png b/doc/images/menu_bg.png
new file mode 100644 (file)
index 0000000..e978743
Binary files /dev/null and b/doc/images/menu_bg.png differ
diff --git a/doc/images/menu_bg_current.png b/doc/images/menu_bg_current.png
new file mode 100644 (file)
index 0000000..de97c92
Binary files /dev/null and b/doc/images/menu_bg_current.png differ
diff --git a/doc/images/menu_bg_hover.png b/doc/images/menu_bg_hover.png
new file mode 100644 (file)
index 0000000..3fd851d
Binary files /dev/null and b/doc/images/menu_bg_hover.png differ
diff --git a/doc/images/menu_bg_last.png b/doc/images/menu_bg_last.png
new file mode 100644 (file)
index 0000000..88c116c
Binary files /dev/null and b/doc/images/menu_bg_last.png differ
diff --git a/doc/images/menu_bg_unsel.png b/doc/images/menu_bg_unsel.png
new file mode 100644 (file)
index 0000000..50e5fd8
Binary files /dev/null and b/doc/images/menu_bg_unsel.png differ
diff --git a/efreet-mime.pc.in b/efreet-mime.pc.in
new file mode 100644 (file)
index 0000000..d651017
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: efreet-mime
+Description: Freedesktop Shared Mime Info standard implementation for the EFL
+@pkgconfig_requires_private@: @requirement_efreet@
+Version: @VERSION@
+Libs: -L${libdir} -lefreet_mime
+Cflags: -I${includedir}/efreet-@VMAJ@
diff --git a/efreet-trash.pc.in b/efreet-trash.pc.in
new file mode 100644 (file)
index 0000000..5a4ccf8
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: efreet-trash
+Description: Freedesktop Shared Trash implementation for the EFL
+@pkgconfig_requires_private@: @requirement_efreet@
+Version: @VERSION@
+Libs: -L${libdir} -lefreet_trash
+Cflags: -I${includedir}/efreet-@VMAJ@
diff --git a/efreet.pc.in b/efreet.pc.in
new file mode 100644 (file)
index 0000000..2f4db8e
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: efreet
+Description: Freedesktop standards implementation for the EFL
+@pkgconfig_requires_private@: @requirement_efreet@
+Version: @VERSION@
+Libs: -L${libdir} -lefreet
+Cflags: -I${includedir}/efreet-@VMAJ@
diff --git a/efreet.spec.in b/efreet.spec.in
new file mode 100644 (file)
index 0000000..72c8ee9
--- /dev/null
@@ -0,0 +1,69 @@
+%define _missing_doc_files_terminate_build 0
+
+%{!?_rel:%{expand:%%global _rel 0.enl%{?dist}}}
+
+Summary: FreeDesktop.Org Compatibility Library
+Name: @PACKAGE@
+Version: @VERSION@
+Release: %{_rel}
+License: BSD
+Group: System Environment/Libraries
+URL: http://www.enlightenment.org/
+Source: %{name}-%{version}.tar.gz
+Packager: %{?_packager:%{_packager}}%{!?_packager:Michael Jennings <mej@eterm.org>}
+Vendor: %{?_vendorinfo:%{_vendorinfo}}%{!?_vendorinfo:The Enlightenment Project (http://www.enlightenment.org/)}
+Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}}
+Obsoletes: ecore-desktop <= 0.9.9.040
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+
+%description
+Efreet implements the FreeDesktop.Org application and MIME-handling
+standards.
+
+%package devel
+Summary: Efreet headers, static libraries, documentation and test programs
+Group: System Environment/Libraries
+Requires: %{name} = %{version}
+
+%description devel
+Headers, static libraries, test programs and documentation for Efreet
+
+%prep
+%setup -q
+
+%build
+%{configure} --prefix=%{_prefix}
+%{__make} %{?_smp_mflags} %{?mflags}
+
+%install
+%{__make} %{?mflags_install} DESTDIR=$RPM_BUILD_ROOT install
+
+# Get rid of unneeded testing cruft.
+%{__rm} -rf $RPM_BUILD_ROOT%{_datadir}/%{name}
+
+%clean
+test "x$RPM_BUILD_ROOT" != "x/" && rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+
+%files
+%defattr(-, root, root)
+%doc AUTHORS COPYING* README
+%{_bindir}/%{name}*
+%{_libdir}/*.so.*
+%{_libdir}/efreet/
+%{_datadir}/locale/*/LC_MESSAGES/*.mo
+
+%files devel
+%defattr(-, root, root)
+%{_includedir}/%{name}-1
+%{_libdir}/*.so
+%{_libdir}/*.la
+%{_libdir}/*.a
+%{_libdir}/pkgconfig/*
+
+%changelog
diff --git a/m4/ac_attribute.m4 b/m4/ac_attribute.m4
new file mode 100644 (file)
index 0000000..23479a9
--- /dev/null
@@ -0,0 +1,47 @@
+dnl Copyright (C) 2004-2008 Kim Woelders
+dnl Copyright (C) 2008 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+dnl Originally snatched from somewhere...
+
+dnl Macro for checking if the compiler supports __attribute__
+
+dnl Usage: AC_C___ATTRIBUTE__
+dnl call AC_DEFINE for HAVE___ATTRIBUTE__ and __UNUSED__
+dnl if the compiler supports __attribute__, HAVE___ATTRIBUTE__ is
+dnl defined to 1 and __UNUSED__ is defined to __attribute__((unused))
+dnl otherwise, HAVE___ATTRIBUTE__ is not defined and __UNUSED__ is
+dnl defined to nothing.
+
+AC_DEFUN([AC_C___ATTRIBUTE__],
+[
+
+AC_MSG_CHECKING([for __attribute__])
+
+AC_CACHE_VAL([ac_cv___attribute__],
+   [AC_TRY_COMPILE(
+       [
+#include <stdlib.h>
+
+int func(int x);
+int foo(int x __attribute__ ((unused)))
+{
+   exit(1);
+}
+       ],
+       [],
+       [ac_cv___attribute__="yes"],
+       [ac_cv___attribute__="no"]
+    )])
+
+AC_MSG_RESULT($ac_cv___attribute__)
+
+if test "x${ac_cv___attribute__}" = "xyes" ; then
+   AC_DEFINE([HAVE___ATTRIBUTE__], [1], [Define to 1 if your compiler has __attribute__])
+   AC_DEFINE([__UNUSED__], [__attribute__((unused))], [Macro declaring a function argument to be unused])
+  else
+    AC_DEFINE([__UNUSED__], [], [Macro declaring a function argument to be unused])
+fi
+
+])
+
+dnl End of ac_attribute.m4
diff --git a/m4/ac_define_dir.m4 b/m4/ac_define_dir.m4
new file mode 100644 (file)
index 0000000..f3d8734
--- /dev/null
@@ -0,0 +1,47 @@
+##### http://autoconf-archive.cryp.to/ac_define_dir.html
+#
+# SYNOPSIS
+#
+#   AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION])
+#
+# DESCRIPTION
+#
+#   This macro sets VARNAME to the expansion of the DIR variable,
+#   taking care of fixing up ${prefix} and such.
+#
+#   VARNAME is then offered as both an output variable and a C
+#   preprocessor symbol.
+#
+#   Example:
+#
+#      AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.])
+#
+# LAST MODIFICATION
+#
+#   2006-10-13
+#
+# COPYLEFT
+#
+#   Copyright (c) 2006 Stepan Kasal <kasal@ucw.cz>
+#   Copyright (c) 2006 Andreas Schwab <schwab@suse.de>
+#   Copyright (c) 2006 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2006 Alexandre Oliva
+#
+#   Copying and distribution of this file, with or without
+#   modification, are permitted in any medium without royalty provided
+#   the copyright notice and this notice are preserved.
+
+AC_DEFUN([AC_DEFINE_DIR], [
+  prefix_NONE=
+  exec_prefix_NONE=
+  test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix
+  test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix
+dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn
+dnl refers to ${prefix}.  Thus we have to use `eval' twice.
+  eval ac_define_dir="\"[$]$2\""
+  eval ac_define_dir="\"$ac_define_dir\""
+  AC_SUBST($1, "$ac_define_dir")
+  AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3])
+  test "$prefix_NONE" && prefix=NONE
+  test "$exec_prefix_NONE" && exec_prefix=NONE
+])
diff --git a/m4/efl_compiler_flag.m4 b/m4/efl_compiler_flag.m4
new file mode 100644 (file)
index 0000000..618c6a6
--- /dev/null
@@ -0,0 +1,57 @@
+dnl Copyright (C) 2010 Vincent Torri <vtorri at univ-evry dot fr>
+dnl                and Albin Tonnerre <albin dot tonnerre at gmail dot com>
+dnl That code is public domain and can be freely used or copied.
+
+dnl Macro that checks if a compiler flag is supported by the compiler.
+
+dnl Usage: EFL_COMPILER_FLAG(flag)
+dnl flag is added to CFLAGS if supported.
+
+AC_DEFUN([EFL_COMPILER_FLAG],
+[
+
+CFLAGS_save="${CFLAGS}"
+CFLAGS="${CFLAGS} $1"
+  
+AC_LANG_PUSH([C])
+AC_MSG_CHECKING([whether the compiler supports $1])
+
+AC_COMPILE_IFELSE(
+   [AC_LANG_PROGRAM([[]])],
+   [have_flag="yes"],
+   [have_flag="no"])
+AC_MSG_RESULT([${have_flag}])
+
+if test "x${have_flag}" = "xno" ; then
+   CFLAGS="${CFLAGS_save}"
+fi
+AC_LANG_POP([C])
+
+])
+
+dnl Macro that checks if a linker flag is supported by the compiler.
+
+dnl Usage: EFL_LINKER_FLAG(flag)
+dnl flag is added to CFLAGS if supported (will be passed to ld anyway).
+
+AC_DEFUN([EFL_LINKER_FLAG],
+[
+
+CFLAGS_save="${CFLAGS}"
+CFLAGS="${CFLAGS} $1"
+  
+AC_LANG_PUSH([C])
+AC_MSG_CHECKING([whether the compiler supports $1])
+
+AC_LINK_IFELSE(
+   [AC_LANG_PROGRAM([[]])],
+   [have_flag="yes"],
+   [have_flag="no"])
+AC_MSG_RESULT([${have_flag}])
+
+if test "x${have_flag}" = "xno" ; then
+   CFLAGS="${CFLAGS_save}"
+fi
+AC_LANG_POP([C])
+
+])
diff --git a/m4/efl_coverage.m4 b/m4/efl_coverage.m4
new file mode 100644 (file)
index 0000000..85d0321
--- /dev/null
@@ -0,0 +1,62 @@
+dnl Copyright (C) 2008 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+
+dnl Macro that check if coverage support is wanted and, if yes, if
+dnl lcov is available.
+
+dnl Usage: EFL_CHECK_COVERAGE(tests [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl The parameter 'tests' is used if a dependency is needed. If set to "yes",
+dnl the dependency is available.
+dnl Defines EFL_COVERAGE_CFLAGS and EFL_COVERAGE_LIBS variables
+dnl Defines the automake conditionnal EFL_ENABLE_COVERAGE
+
+AC_DEFUN([EFL_CHECK_COVERAGE],
+[
+
+dnl configure option
+
+AC_ARG_ENABLE([coverage],
+   [AC_HELP_STRING([--enable-coverage], [enable coverage profiling instrumentation @<:@default=disabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       _efl_enable_coverage="yes"
+    else
+       _efl_enable_coverage="no"
+    fi
+   ],
+   [_efl_enable_coverage="no"])
+
+AC_MSG_CHECKING([whether to use profiling instrumentation])
+AC_MSG_RESULT([$_efl_enable_coverage])
+
+dnl lcov check
+
+if test "x$_efl_enable_coverage" = "xyes" && test ! "x$1" = "xyes" ; then
+   AC_MSG_WARN([Coverage report requested but tests not being built, disable profiling instrumentation.])
+   AC_MSG_WARN([Run configure with --enable-tests])
+   _efl_enable_coverage="no"
+fi
+
+if test "x$_efl_enable_coverage" = "xyes" ; then
+   AC_CHECK_PROG(have_lcov, [lcov], [yes], [no])
+   if test "x$have_lcov" = "xyes" ; then
+      EFL_COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage"
+      EFL_COVERAGE_LIBS="-lgcov"
+# remove any optimisation flag and force debug symbols
+      EFL_DEBUG_CFLAGS="-g -O0 -DDEBUG"
+   else
+      AC_MSG_WARN([lcov is not found, disable profiling instrumentation])
+      _efl_enable_coverage="no"
+   fi
+fi
+
+dnl Substitution
+AC_SUBST(EFL_COVERAGE_CFLAGS)
+AC_SUBST(EFL_COVERAGE_LIBS)
+
+AM_CONDITIONAL(EFL_ENABLE_COVERAGE, test "x${_efl_enable_coverage}" = "xyes")
+
+AS_IF([test "x$_efl_enable_coverage" = "xyes"], [$2], [$3])
+])
+
+dnl End of efl_coverage.m4
diff --git a/m4/efl_doxygen.m4 b/m4/efl_doxygen.m4
new file mode 100644 (file)
index 0000000..d83ed68
--- /dev/null
@@ -0,0 +1,97 @@
+dnl Copyright (C) 2008 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+
+dnl Macro that check if doxygen is available or not.
+
+dnl EFL_CHECK_DOXYGEN([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for the doxygen program
+dnl Defines efl_doxygen
+dnl Defines the automake conditionnal EFL_BUILD_DOC
+dnl
+AC_DEFUN([EFL_CHECK_DOXYGEN],
+[
+
+dnl
+dnl Disable the build of the documentation
+dnl
+AC_ARG_ENABLE([doc],
+   [AC_HELP_STRING(
+       [--disable-doc],
+       [Disable documentation build @<:@default=enabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       efl_enable_doc="yes"
+    else
+       efl_enable_doc="no"
+    fi
+   ],
+   [efl_enable_doc="yes"])
+
+AC_MSG_CHECKING([whether to build documentation])
+AC_MSG_RESULT([${efl_enable_doc}])
+
+if test "x${efl_enable_doc}" = "xyes" ; then
+
+dnl Specify the file name, without path
+
+   efl_doxygen="doxygen"
+
+   AC_ARG_WITH([doxygen],
+      [AC_HELP_STRING(
+          [--with-doxygen=FILE],
+          [doxygen program to use @<:@default=doxygen@:>@])],
+
+dnl Check the given doxygen program.
+
+      [efl_doxygen=${withval}
+       AC_CHECK_PROG([efl_have_doxygen],
+          [${efl_doxygen}],
+          [yes],
+          [no])
+       if test "x${efl_have_doxygen}" = "xno" ; then
+          echo "WARNING:"
+          echo "The doxygen program you specified:"
+          echo "${efl_doxygen}"
+          echo "was not found.  Please check the path and make sure "
+          echo "the program exists and is executable."
+          AC_MSG_WARN([no doxygen detected. Documentation will not be built])
+       fi
+      ],
+      [AC_CHECK_PROG([efl_have_doxygen],
+          [${efl_doxygen}],
+          [yes],
+          [no])
+       if test "x${efl_have_doxygen}" = "xno" ; then
+          echo "WARNING:"
+          echo "The doxygen program was not found in your execute path."
+          echo "You may have doxygen installed somewhere not covered by your path."
+          echo ""
+          echo "If this is the case make sure you have the packages installed, AND"
+          echo "that the doxygen program is in your execute path (see your"
+          echo "shell manual page on setting the \$PATH environment variable), OR"
+          echo "alternatively, specify the program to use with --with-doxygen."
+          AC_MSG_WARN([no doxygen detected. Documentation will not be built])
+       fi
+      ])
+fi
+
+dnl
+dnl Substitution
+dnl
+AC_SUBST([efl_doxygen])
+
+if ! test "x${efl_have_doxygen}" = "xyes" ; then
+   efl_enable_doc="no"
+fi
+
+AM_CONDITIONAL(EFL_BUILD_DOC, test "x${efl_enable_doc}" = "xyes")
+
+if test "x${efl_enable_doc}" = "xyes" ; then
+  m4_default([$1], [:])
+else
+  m4_default([$2], [:])
+fi
+
+])
+
+dnl End of efl_doxygen.m4
diff --git a/m4/efl_path_max.m4 b/m4/efl_path_max.m4
new file mode 100644 (file)
index 0000000..3ac29bd
--- /dev/null
@@ -0,0 +1,37 @@
+dnl Check for PATH_MAX in limits.h, and define a default value if not found
+dnl This is a workaround for systems not providing PATH_MAX, like GNU/Hurd
+
+dnl EFL_CHECK_PATH_MAX([DEFAULT_VALUE_IF_NOT_FOUND])
+dnl
+dnl If PATH_MAX is not defined in <limits.h>, defines it
+dnl to DEFAULT_VALUE_IF_NOT_FOUND if it exists, or fallback
+dnl to using 4096
+
+AC_DEFUN([EFL_CHECK_PATH_MAX],
+[
+
+default_max=m4_default([$1], "4096")
+
+AC_LANG_PUSH([C])
+
+AC_MSG_CHECKING([for PATH_MAX in limits.h])
+AC_COMPILE_IFELSE(
+   [AC_LANG_PROGRAM(
+       [[
+#include <limits.h>
+       ]],
+       [[
+int i = PATH_MAX;
+       ]])],
+   [AC_MSG_RESULT([yes])],
+   [
+    AC_DEFINE_UNQUOTED([PATH_MAX],
+       [${default_max}],
+       [default value since PATH_MAX is not defined])
+    AC_MSG_RESULT([no: using ${default_max}])
+   ])
+
+AC_LANG_POP([C])
+
+])
+dnl end of efl_path_max.m4
diff --git a/m4/efl_tests.m4 b/m4/efl_tests.m4
new file mode 100644 (file)
index 0000000..3a4dfe2
--- /dev/null
@@ -0,0 +1,43 @@
+dnl Copyright (C) 2008 Vincent Torri <vtorri at univ-evry dot fr>
+dnl That code is public domain and can be freely used or copied.
+
+dnl Macro that check if tests programs are wanted and if yes, if
+dnl the Check library is available.
+
+dnl Usage: EFL_CHECK_TESTS([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Define the automake conditionnal EFL_ENABLE_TESTS
+
+AC_DEFUN([EFL_CHECK_TESTS],
+[
+
+dnl configure option
+
+AC_ARG_ENABLE([tests],
+   [AC_HELP_STRING([--enable-tests], [enable tests @<:@default=disabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       _efl_enable_tests="yes"
+    else
+       _efl_enable_tests="no"
+    fi
+   ],
+   [_efl_enable_tests="no"])
+
+AC_MSG_CHECKING([whether tests are built])
+AC_MSG_RESULT([${_efl_enable_tests}])
+
+AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+
+if test "x${_efl_enable_tests}" = "xyes" ; then
+   PKG_CHECK_MODULES([CHECK],
+      [check >= 0.9.5],
+      [dummy="yes"],
+      [_efl_enable_tests="no"])
+fi
+
+AM_CONDITIONAL(EFL_ENABLE_TESTS, test "x${_efl_enable_tests}" = "xyes")
+
+AS_IF([test "x$_efl_enable_tests" = "xyes"], [$1], [$2])
+])
+
+dnl End of efl_tests.m4
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644 (file)
index 0000000..6e30c56
--- /dev/null
@@ -0,0 +1,12 @@
+cs
+de
+el
+es
+fr
+it
+ja
+ko
+nl
+pt
+ru
+sl
diff --git a/po/Makevars b/po/Makevars
new file mode 100644 (file)
index 0000000..22837ab
--- /dev/null
@@ -0,0 +1,41 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --from-code=UTF-8 --foreign-user
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file.  Set this to the copyright holder of the surrounding
+# package.  (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.)  Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright.  The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = Enlightenment development team
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+#   in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+#   understood.
+# - Strings which make invalid assumptions about notation of date, time or
+#   money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS = enlightenment-devel@lists.sourceforge.net
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used.  It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644 (file)
index 0000000..68cb9d8
--- /dev/null
@@ -0,0 +1 @@
+src/lib/efreet_base.c
diff --git a/po/cs.po b/po/cs.po
new file mode 100644 (file)
index 0000000..b82c806
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,20 @@
+# Czech translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Plocha"
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..20395bd
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,20 @@
+# German translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Arbeitsfläche"
diff --git a/po/efreet.pot b/po/efreet.pot
new file mode 100644 (file)
index 0000000..23abe0b
--- /dev/null
@@ -0,0 +1,22 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Enlightenment development team
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: efreet 1.7.0\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr ""
diff --git a/po/el.po b/po/el.po
new file mode 100644 (file)
index 0000000..5c6ac3c
--- /dev/null
+++ b/po/el.po
@@ -0,0 +1,20 @@
+# Greek translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Επιφάνεια εργασίας"
diff --git a/po/es.po b/po/es.po
new file mode 100644 (file)
index 0000000..5fdf7d8
--- /dev/null
+++ b/po/es.po
@@ -0,0 +1,20 @@
+# Spanish translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Escritorio"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644 (file)
index 0000000..1edbecc
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,20 @@
+# French translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Bureau"
diff --git a/po/it.po b/po/it.po
new file mode 100644 (file)
index 0000000..89cc64c
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,20 @@
+# Italian translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Scrivania"
diff --git a/po/ja.po b/po/ja.po
new file mode 100644 (file)
index 0000000..5aacd28
--- /dev/null
+++ b/po/ja.po
@@ -0,0 +1,20 @@
+# Japanese translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "デスクトップ"
diff --git a/po/ko.po b/po/ko.po
new file mode 100644 (file)
index 0000000..6d4e439
--- /dev/null
+++ b/po/ko.po
@@ -0,0 +1,20 @@
+# Korean translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "바탕화면"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644 (file)
index 0000000..c88443c
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,20 @@
+# Dutch translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Bureaublad"
diff --git a/po/pt.po b/po/pt.po
new file mode 100644 (file)
index 0000000..59e2827
--- /dev/null
+++ b/po/pt.po
@@ -0,0 +1,20 @@
+# Portuguese translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Área de Trabalho"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644 (file)
index 0000000..442bec1
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,20 @@
+# Russian translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Рабочий стол"
diff --git a/po/sl.po b/po/sl.po
new file mode 100644 (file)
index 0000000..a5c6772
--- /dev/null
+++ b/po/sl.po
@@ -0,0 +1,20 @@
+# Slovenian translation for Efreet.
+# Copyright (C) 2012 Enlightenment development team
+# This file is put in the public domain.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Efreet\n"
+"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n"
+"POT-Creation-Date: 2012-08-27 19:16+0900\n"
+"PO-Revision-Date: 2012-06-24 17:10+0900\n"
+"Last-Translator: Jerome Pinot <ngc891@gmail.com>\n"
+"Language-Team: Enlightenment Team\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/lib/efreet_base.c:127
+msgid "Desktop"
+msgstr "Namizje"
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..add035f
--- /dev/null
@@ -0,0 +1,4 @@
+
+SUBDIRS = lib bin tests
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
new file mode 100644 (file)
index 0000000..3040a20
--- /dev/null
@@ -0,0 +1,29 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_srcdir)/src/lib \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)\" \
+@EFREET_CFLAGS@
+
+internal_bindir=$(libdir)/efreet
+internal_bin_PROGRAMS = \
+efreet_desktop_cache_create \
+efreet_icon_cache_create
+
+efreet_desktop_cache_create_LDADD = \
+$(top_builddir)/src/lib/libefreet.la \
+@EFREET_LIBS@
+
+efreet_desktop_cache_create_SOURCES = \
+efreet_desktop_cache_create.c
+
+efreet_icon_cache_create_LDADD = \
+$(top_builddir)/src/lib/libefreet.la \
+@EFREET_LIBS@
+
+efreet_icon_cache_create_SOURCES = \
+efreet_icon_cache_create.c
diff --git a/src/bin/efreet_desktop_cache_create.c b/src/bin/efreet_desktop_cache_create.c
new file mode 100644 (file)
index 0000000..c397022
--- /dev/null
@@ -0,0 +1,541 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <Eina.h>
+#include <Eet.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+#define EFREET_MODULE_LOG_DOM _efreet_desktop_cache_log_dom
+static int _efreet_desktop_cache_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_cache_private.h"
+
+static Eet_Data_Descriptor *edd = NULL;
+static Eet_File *ef = NULL;
+static Eet_File *util_ef = NULL;
+
+static Eina_Hash *desktops = NULL;
+
+static Eina_Hash         *file_ids = NULL;
+static Efreet_Cache_Hash *old_file_ids = NULL;
+static Eina_Hash         *paths = NULL;
+
+static Eina_Hash *mime_types = NULL;
+static Eina_Hash *categories = NULL;
+static Eina_Hash *startup_wm_class = NULL;
+static Eina_Hash *name = NULL;
+static Eina_Hash *generic_name = NULL;
+static Eina_Hash *comment = NULL;
+static Eina_Hash *exec = NULL;
+
+static int
+strcmplen(const void *data1, const void *data2)
+{
+    return strncmp(data1, data2, eina_stringshare_strlen(data1));
+}
+
+static int
+cache_add(const char *path, const char *file_id, int priority __UNUSED__, int *changed)
+{
+    Efreet_Desktop *desk;
+    char *ext;
+
+    INF("FOUND: %s", path);
+    if (file_id) INF(" (id): %s", file_id);
+    ext = strrchr(path, '.');
+    if (!ext || (strcmp(ext, ".desktop") && strcmp(ext, ".directory"))) return 1;
+    desk = efreet_desktop_new(path);
+    if (desk) INF("  OK");
+    else      INF("  FAIL");
+    if (!desk) return 1;
+    if (!desk->eet)
+    {
+        /* This file isn't in cache */
+        *changed = 1;
+        INF("  NEW");
+    }
+    else if (ecore_file_mod_time(desk->orig_path) != desk->load_time)
+    {
+        efreet_desktop_free(desk);
+        *changed = 1;
+        desk = efreet_desktop_uncached_new(path);
+        if (desk) INF("  CHANGED");
+        else      INF("  NO UNCACHED");
+    }
+    if (!desk) return 1;
+    if (file_id && old_file_ids && !eina_hash_find(old_file_ids->hash, file_id))
+    {
+        *changed = 1;
+        INF("  NOT IN UTILS");
+    }
+    if (!eina_hash_find(paths, desk->orig_path))
+    {
+        if (!eet_data_write(ef, edd, desk->orig_path, desk, 0))
+            return 0;
+        eina_hash_add(paths, desk->orig_path, (void *)1);
+    }
+    /* TODO: We should check priority, and not just hope we search in right order */
+    /* TODO: We need to find out if prioritized file id has changed because of
+     * changed search order. */
+    if (!desk->hidden && desk->type == EFREET_DESKTOP_TYPE_APPLICATION &&
+        file_id && !eina_hash_find(file_ids, file_id))
+    {
+        Eina_List *l;
+        char *data;
+        Efreet_Cache_Array_String *array;
+
+#define ADD_LIST(list, hash) \
+        EINA_LIST_FOREACH((list), l, data) \
+        { \
+            array = eina_hash_find((hash), data); \
+            if (!array) \
+                array = NEW(Efreet_Cache_Array_String, 1); \
+            array->array = realloc(array->array, sizeof (char *) * (array->array_count + 1)); \
+            array->array[array->array_count++] = desk->orig_path; \
+            eina_hash_set((hash), data, array); \
+        }
+#define ADD_ELEM(elem, hash) \
+        if ((elem)) \
+        { \
+            data = (elem); \
+            array = eina_hash_find((hash), data); \
+            if (!array) \
+                array = NEW(Efreet_Cache_Array_String, 1); \
+            array->array = realloc(array->array, sizeof (char *) * (array->array_count + 1)); \
+            array->array[array->array_count++] = desk->orig_path; \
+            eina_hash_set((hash), data, array); \
+        }
+        ADD_LIST(desk->mime_types, mime_types);
+        ADD_LIST(desk->categories, categories);
+        ADD_ELEM(desk->startup_wm_class, startup_wm_class);
+        ADD_ELEM(desk->name, name);
+        ADD_ELEM(desk->generic_name, generic_name);
+        ADD_ELEM(desk->comment, comment);
+        ADD_ELEM(desk->exec, exec);
+        eina_hash_add(file_ids, file_id, desk->orig_path);
+        eina_hash_add(desktops, desk->orig_path, desk);
+    }
+    else
+        efreet_desktop_free(desk);
+    return 1;
+}
+
+
+static int
+cache_scan(const char *path, const char *base_id, int priority, int recurse, int *changed)
+{
+    char *file_id = NULL;
+    char id[PATH_MAX];
+    char buf[PATH_MAX];
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *info;
+
+    if (!ecore_file_is_dir(path)) return 1;
+
+    it = eina_file_direct_ls(path);
+    if (!it) return 1;
+
+    id[0] = '\0';
+    EINA_ITERATOR_FOREACH(it, info)
+    {
+        const char *fname;
+       
+        fname = info->path + info->name_start;
+        if (base_id)
+        {
+            if (*base_id)
+                snprintf(id, sizeof(id), "%s-%s", base_id, fname);
+            else
+                strcpy(id, fname);
+            file_id = id;
+        }
+
+        snprintf(buf, sizeof(buf), "%s/%s", path, fname);
+        if (ecore_file_is_dir(buf))
+        {
+            if (recurse)
+                cache_scan(buf, file_id, priority, recurse, changed);
+        }
+        else
+        {
+            if (!cache_add(buf, file_id, priority, changed))
+            {
+                eina_iterator_free(it);
+                return 0;
+            }
+        }
+    }
+    eina_iterator_free(it);
+    return 1;
+}
+
+static int
+cache_lock_file(void)
+{
+    char file[PATH_MAX];
+    struct flock fl;
+    int lockfd;
+
+    snprintf(file, sizeof(file), "%s/efreet/desktop_data.lock", efreet_cache_home_get());
+    lockfd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+    if (lockfd < 0) return -1;
+    efreet_fsetowner(lockfd);
+
+    memset(&fl, 0, sizeof(struct flock));
+    fl.l_type = F_WRLCK;
+    fl.l_whence = SEEK_SET;
+    if (fcntl(lockfd, F_SETLK, &fl) < 0)
+    {
+        INF("LOCKED! You may want to delete %s if this persists", file);
+        close(lockfd);
+        return -1;
+    }
+
+    return lockfd;
+}
+
+int
+main(int argc, char **argv)
+{
+    /* TODO:
+     * - Add file monitor on files, so that we catch changes on files
+     *   during whilst this program runs.
+     * - Maybe linger for a while to reduce number of cache re-creates.
+     */
+    Efreet_Cache_Hash hash;
+    Efreet_Cache_Version version;
+    Eina_List *dirs = NULL;
+    Eina_List *systemdirs = NULL;
+    Efreet_Cache_Array_String *user_dirs = NULL;
+    Eina_List *extra_dirs = NULL;
+    Eina_List *store_dirs = NULL;
+    int priority = 0;
+    char *dir = NULL;
+    char *path;
+    int lockfd = -1, tmpfd;
+    int changed = 0;
+    int i;
+    char file[PATH_MAX] = { '\0' };
+    char util_file[PATH_MAX] = { '\0' };
+
+    if (!eina_init()) goto eina_error;
+    _efreet_desktop_cache_log_dom =
+        eina_log_domain_register("efreet_desktop_cache", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_desktop_cache_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_desktop_cache.");
+        return -1;
+    }
+
+    for (i = 1; i < argc; i++)
+    {
+        if (!strcmp(argv[i], "-v"))
+            eina_log_domain_level_set("efreet_desktop_cache", EINA_LOG_LEVEL_DBG);
+        else if ((!strcmp(argv[i], "-h")) ||
+                 (!strcmp(argv[i], "-help")) ||
+                 (!strcmp(argv[i], "--h")) ||
+                 (!strcmp(argv[i], "--help")))
+        {
+            printf("Options:\n");
+            printf("  -v              Verbose mode\n");
+            printf("  -d dir1 dir2    Extra dirs\n");
+            exit(0);
+        }
+        else if (!strcmp(argv[i], "-d"))
+        {
+            while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
+                extra_dirs = eina_list_append(extra_dirs, argv[++i]);
+        }
+    }
+    extra_dirs = eina_list_sort(extra_dirs, -1, EINA_COMPARE_CB(strcmp));
+
+    /* init external subsystems */
+    if (!eet_init()) goto eet_error;
+    if (!ecore_init()) goto ecore_error;
+
+    efreet_cache_update = 0;
+    /* finish efreet init */
+    if (!efreet_init()) goto efreet_error;
+
+    /* create homedir */
+    snprintf(file, sizeof(file), "%s/efreet", efreet_cache_home_get());
+    if (!ecore_file_exists(file))
+    {
+        if (!ecore_file_mkpath(file)) goto efreet_error;
+        efreet_setowner(file);
+    }
+
+    /* lock process, so that we only run one copy of this program */
+    lockfd = cache_lock_file();
+    if (lockfd == -1) goto efreet_error;
+
+    edd = efreet_desktop_edd();
+    if (!edd) goto edd_error;
+
+    /* read user dirs from old cache */
+    ef = eet_open(efreet_desktop_cache_file(), EET_FILE_MODE_READ);
+    if (ef)
+    {
+        user_dirs = eet_data_read(ef, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS);
+        eet_close(ef);
+    }
+
+    ef = eet_open(efreet_desktop_util_cache_file(), EET_FILE_MODE_READ);
+    if (ef)
+    {
+        old_file_ids = eet_data_read(ef, efreet_hash_string_edd(), "file_id");
+        eet_close(ef);
+    }
+
+    /* create cache */
+    snprintf(file, sizeof(file), "%s.XXXXXX", efreet_desktop_cache_file());
+    tmpfd = mkstemp(file);
+    if (tmpfd < 0) goto error;
+    close(tmpfd);
+    ef = eet_open(file, EET_FILE_MODE_READ_WRITE);
+    if (!ef) goto error;
+
+    snprintf(util_file, sizeof(util_file), "%s.XXXXXX", efreet_desktop_util_cache_file());
+    tmpfd = mkstemp(util_file);
+    if (tmpfd < 0) goto error;
+    close(tmpfd);
+    util_ef = eet_open(util_file, EET_FILE_MODE_READ_WRITE);
+    if (!util_ef) goto error;
+
+    /* write cache version */
+    version.major = EFREET_DESKTOP_UTILS_CACHE_MAJOR;
+    version.minor = EFREET_DESKTOP_UTILS_CACHE_MINOR;
+    eet_data_write(util_ef, efreet_version_edd(), EFREET_CACHE_VERSION, &version, 1);
+    version.major = EFREET_DESKTOP_CACHE_MAJOR;
+    version.minor = EFREET_DESKTOP_CACHE_MINOR;
+    eet_data_write(ef, efreet_version_edd(), EFREET_CACHE_VERSION, &version, 1);
+
+    desktops = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
+
+    file_ids = eina_hash_string_superfast_new(NULL);
+    paths = eina_hash_string_superfast_new(NULL);
+
+    mime_types = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    categories = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    startup_wm_class = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    name = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    generic_name = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    comment = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    exec = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+
+    dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
+                                                                    "applications");
+    if (!dirs) goto error;
+
+    EINA_LIST_FREE(dirs, path)
+    {
+        char file_id[PATH_MAX] = { '\0' };
+
+        if (!cache_scan(path, file_id, priority++, 1, &changed)) goto error;
+        systemdirs = eina_list_append(systemdirs, path);
+    }
+
+    if (user_dirs)
+    {
+        unsigned int j;
+
+        for (j = 0; j < user_dirs->array_count; j++)
+        {
+            if (eina_list_search_unsorted_list(systemdirs, strcmplen, user_dirs->array[j]))
+                continue;
+            if (!ecore_file_is_dir(user_dirs->array[j])) continue;
+            if (!cache_scan(user_dirs->array[j], NULL, priority, 0, &changed)) goto error;
+
+            store_dirs = eina_list_append(store_dirs, user_dirs->array[j]);
+        }
+        store_dirs = eina_list_sort(store_dirs, -1, EINA_COMPARE_CB(strcmp));
+    }
+
+    if (extra_dirs)
+    {
+        Eina_List *l;
+
+        EINA_LIST_FOREACH(extra_dirs, l, path)
+        {
+            if (eina_list_search_unsorted_list(systemdirs, strcmplen, path))
+                continue;
+            if (eina_list_search_unsorted_list(store_dirs, EINA_COMPARE_CB(strcmp), path))
+                continue;
+            if (!ecore_file_is_dir(path)) continue;
+
+            /* If we scan a passed dir, we must have changed */
+            changed = 1;
+            if (!cache_scan(path, NULL, priority, 0, &changed)) goto error;
+
+            store_dirs = eina_list_append(store_dirs, path);
+        }
+        store_dirs = eina_list_sort(store_dirs, -1, EINA_COMPARE_CB(strcmp));
+    }
+
+    if (user_dirs)
+        efreet_cache_array_string_free(user_dirs);
+
+    /* store user dirs */
+    if (store_dirs)
+    {
+        Eina_List *l;
+
+        user_dirs = NEW(Efreet_Cache_Array_String, 1);
+        user_dirs->array = NEW(char *, eina_list_count(store_dirs));
+        user_dirs->array_count = 0;
+        EINA_LIST_FOREACH(store_dirs, l, path)
+            user_dirs->array[user_dirs->array_count++] = path;
+
+        eet_data_write(ef, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS, user_dirs, 1);
+        IF_FREE(user_dirs->array);
+        free(user_dirs);
+    }
+
+    /* store util */
+#define STORE_HASH_ARRAY(_hash) \
+    if (eina_hash_population((_hash)) > 0) \
+    { \
+        Eina_Iterator *it;   \
+        Efreet_Cache_Array_String array; \
+        const char *str;     \
+                             \
+        hash.hash = (_hash); \
+        eet_data_write(util_ef, efreet_hash_array_string_edd(), #_hash "_hash", &hash, 1); \
+        array.array_count = 0; \
+        array.array = malloc(eina_hash_population(hash.hash) * sizeof(char *)); \
+        it = eina_hash_iterator_key_new(hash.hash); \
+        EINA_ITERATOR_FOREACH(it, str) \
+            array.array[array.array_count++] = str; \
+        eina_iterator_free(it); \
+        eet_data_write(util_ef, efreet_array_string_edd(), #_hash "_list", &array, 1); \
+        free(array.array); \
+    }
+    STORE_HASH_ARRAY(mime_types);
+    STORE_HASH_ARRAY(categories);
+    STORE_HASH_ARRAY(startup_wm_class);
+    STORE_HASH_ARRAY(name);
+    STORE_HASH_ARRAY(generic_name);
+    STORE_HASH_ARRAY(comment);
+    STORE_HASH_ARRAY(exec);
+    if (eina_hash_population(file_ids) > 0)
+    {
+        hash.hash = file_ids;
+        eet_data_write(util_ef, efreet_hash_string_edd(), "file_id", &hash, 1);
+    }
+
+    eina_hash_free(mime_types);
+    eina_hash_free(categories);
+    eina_hash_free(startup_wm_class);
+    eina_hash_free(name);
+    eina_hash_free(generic_name);
+    eina_hash_free(comment);
+    eina_hash_free(exec);
+
+    if (old_file_ids)
+    {
+        eina_hash_free(old_file_ids->hash);
+        free(old_file_ids);
+    }
+
+    eina_hash_free(file_ids);
+    eina_hash_free(paths);
+
+    eina_hash_free(desktops);
+
+    /* check if old and new caches contain the same number of entries */
+    if (!changed)
+    {
+        Eet_File *old;
+
+        old = eet_open(efreet_desktop_cache_file(), EET_FILE_MODE_READ);
+        if (!old || eet_num_entries(old) != eet_num_entries(ef)) changed = 1;
+        if (old) eet_close(old);
+        old = eet_open(efreet_desktop_util_cache_file(), EET_FILE_MODE_READ);
+        if (!old || eet_num_entries(old) != eet_num_entries(util_ef)) changed = 1;
+        if (old) eet_close(old);
+    }
+
+    /* cleanup */
+    eet_close(util_ef);
+    eet_close(ef);
+
+    /* unlink old cache files */
+    if (changed)
+    {
+        if (unlink(efreet_desktop_cache_file()) < 0)
+        {
+            if (errno != ENOENT) goto error;
+        }
+        if (unlink(efreet_desktop_util_cache_file()) < 0)
+        {
+            if (errno != ENOENT) goto error;
+        }
+        /* rename tmp files to real files */
+        if (rename(util_file, efreet_desktop_util_cache_file()) < 0) goto error;
+        efreet_setowner(efreet_desktop_util_cache_file());
+        if (rename(file, efreet_desktop_cache_file()) < 0) goto error;
+        efreet_setowner(efreet_desktop_cache_file());
+    }
+    else
+    {
+        unlink(util_file);
+        unlink(file);
+    }
+
+    /* touch update file */
+    snprintf(file, sizeof(file), "%s/efreet/desktop_data.update", efreet_cache_home_get());
+    tmpfd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+    if (tmpfd >= 0)
+    {
+        char c = 'n';
+
+        efreet_fsetowner(tmpfd);
+        if (changed) c = 'c';
+        if (write(tmpfd, &c, 1) != 1) perror("write");
+        close(tmpfd);
+    }
+
+    EINA_LIST_FREE(systemdirs, dir)
+        eina_stringshare_del(dir);
+    eina_list_free(extra_dirs);
+    eina_list_free(store_dirs);
+    efreet_shutdown();
+    ecore_shutdown();
+    eet_shutdown();
+    eina_log_domain_unregister(_efreet_desktop_cache_log_dom);
+    eina_shutdown();
+    close(lockfd);
+    return 0;
+error:
+    IF_FREE(dir);
+edd_error:
+    if (user_dirs) efreet_cache_array_string_free(user_dirs);
+    if (old_file_ids)
+    {
+        eina_hash_free(old_file_ids->hash);
+        free(old_file_ids);
+    }
+    efreet_shutdown();
+efreet_error:
+    ecore_shutdown();
+ecore_error:
+    eet_shutdown();
+eet_error:
+    EINA_LIST_FREE(systemdirs, dir)
+        eina_stringshare_del(dir);
+    eina_list_free(extra_dirs);
+    eina_list_free(store_dirs);
+    eina_log_domain_unregister(_efreet_desktop_cache_log_dom);
+    eina_shutdown();
+eina_error:
+    if (lockfd >= 0) close(lockfd);
+    return 1;
+}
diff --git a/src/bin/efreet_icon_cache_create.c b/src/bin/efreet_icon_cache_create.c
new file mode 100644 (file)
index 0000000..e8d100c
--- /dev/null
@@ -0,0 +1,1131 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <Eina.h>
+#include <Eet.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+#define EFREET_MODULE_LOG_DOM _efreet_icon_cache_log_dom
+static int _efreet_icon_cache_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_cache_private.h"
+
+/* TODO:
+ * - Need to handle programs using different exts
+ */
+
+static Eina_Array *exts = NULL;
+static Eina_Array *extra_dirs = NULL;
+static Eina_Array *strs = NULL;
+static Eina_Hash *icon_themes = NULL;
+
+static Eina_Bool
+cache_directory_modified(Eina_Hash *dirs, const char *dir)
+{
+    Efreet_Cache_Directory *dcache;
+    struct stat st;
+
+    if (!dirs) return EINA_TRUE;
+
+    if (stat(dir, &st) < 0) return EINA_FALSE;
+    dcache = eina_hash_find(dirs, dir);
+    if (!dcache)
+    {
+        dcache = malloc(sizeof (Efreet_Cache_Directory));
+        if (!dcache) return EINA_TRUE;
+
+        dcache->modified_time = (long long) st.st_mtime;
+        eina_hash_add(dirs, dir, dcache);
+    }
+    else if (dcache->modified_time == (long long) st.st_mtime) return EINA_FALSE;
+    dcache->modified_time = st.st_mtime;
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+cache_extension_lookup(const char *ext)
+{
+    unsigned int i;
+
+    for (i = 0; i < exts->count; ++i)
+        if (!strcmp(exts->data[i], ext))
+            return EINA_TRUE;
+    return EINA_FALSE;
+}
+
+static Eina_Bool
+cache_fallback_scan_dir(Eina_Hash *icons, Eina_Hash *dirs, const char *dir)
+{
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *entry;
+
+    if (!cache_directory_modified(dirs, dir)) return EINA_TRUE;
+
+    it = eina_file_stat_ls(dir);
+    if (!it) return EINA_TRUE;
+
+    EINA_ITERATOR_FOREACH(it, entry)
+    {
+        Efreet_Cache_Fallback_Icon *icon;
+        char *name;
+        char *ext;
+        unsigned int i;
+
+        if (entry->type == EINA_FILE_DIR)
+            continue;
+
+        ext = strrchr(entry->path + entry->name_start, '.');
+        if (!ext || !cache_extension_lookup(ext))
+            continue;
+
+        /* icon with known extension */
+        name = entry->path + entry->name_start;
+        *ext = '\0';
+
+        icon = eina_hash_find(icons, name);
+        if (!icon)
+        {
+            icon = NEW(Efreet_Cache_Fallback_Icon, 1);
+            icon->theme = NULL;
+            eina_hash_add(icons, name, icon);
+        }
+
+        *ext = '.';
+
+        for (i = 0; i < icon->icons_count; ++i)
+            if (!strcmp(icon->icons[i], entry->path))
+                break;
+
+        if (i != icon->icons_count)
+            continue;
+
+        icon->icons = realloc(icon->icons, sizeof (char *) * (icon->icons_count + 1));
+        icon->icons[icon->icons_count] = eina_stringshare_add(entry->path);
+        eina_array_push(strs, icon->icons[icon->icons_count++]);
+    }
+
+    eina_iterator_free(it);
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+cache_fallback_scan(Eina_Hash *icons, Eina_Hash *dirs)
+{
+    unsigned int i;
+    Eina_List *xdg_dirs, *l;
+    const char *dir;
+    char path[PATH_MAX];
+
+    for (i = 0; i < extra_dirs->count; i++)
+        cache_fallback_scan_dir(icons, dirs, extra_dirs->data[i]);
+
+    cache_fallback_scan_dir(icons, dirs, efreet_icon_deprecated_user_dir_get());
+    cache_fallback_scan_dir(icons, dirs, efreet_icon_user_dir_get());
+
+    xdg_dirs = efreet_data_dirs_get();
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(path, sizeof(path), "%s/icons", dir);
+        cache_fallback_scan_dir(icons, dirs, path);
+    }
+
+#ifndef STRICT_SPEC
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(path, sizeof(path), "%s/pixmaps", dir);
+        cache_fallback_scan_dir(icons, dirs, path);
+    }
+#endif
+
+    cache_fallback_scan_dir(icons, dirs, "/usr/share/pixmaps");
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+check_fallback_changed(Efreet_Cache_Icon_Theme *theme)
+{
+    unsigned int i;
+    Eina_List *xdg_dirs, *l;
+    const char *dir;
+    char path[PATH_MAX];
+
+    /* Check if the dirs we have cached are changed */
+    if (theme->dirs)
+    {
+        Eina_Iterator *it;
+        Eina_Bool changed = EINA_FALSE;
+
+        it = eina_hash_iterator_key_new(theme->dirs);
+        EINA_ITERATOR_FOREACH(it, dir)
+        {
+            changed = !ecore_file_exists(dir);
+            if (changed) break;
+            changed = cache_directory_modified(theme->dirs, dir);
+            if (changed) break;
+        }
+        eina_iterator_free(it);
+        if (changed) return EINA_TRUE;
+    }
+
+    /* Check if spec dirs have changed */
+    for (i = 0; i < extra_dirs->count; i++)
+        if (cache_directory_modified(theme->dirs, extra_dirs->data[i])) return EINA_TRUE;
+
+    if (cache_directory_modified(theme->dirs, efreet_icon_deprecated_user_dir_get())) return EINA_TRUE;
+    if (cache_directory_modified(theme->dirs, efreet_icon_user_dir_get())) return EINA_TRUE;
+
+    xdg_dirs = efreet_data_dirs_get();
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(path, sizeof(path), "%s/icons", dir);
+        if (cache_directory_modified(theme->dirs, path)) return EINA_TRUE;
+    }
+
+#ifndef STRICT_SPEC
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(path, sizeof(path), "%s/pixmaps", dir);
+        if (cache_directory_modified(theme->dirs, path)) return EINA_TRUE;
+    }
+#endif
+
+    if (cache_directory_modified(theme->dirs, "/usr/share/pixmaps")) return EINA_TRUE;
+    return EINA_FALSE;
+}
+
+static Eina_Bool
+cache_scan_path_dir(Efreet_Icon_Theme *theme,
+                    const char *path,
+                    Efreet_Icon_Theme_Directory *dir,
+                    Eina_Hash *icons)
+{
+    Eina_Iterator *it;
+    char buf[PATH_MAX];
+    Eina_File_Direct_Info *entry;
+
+    snprintf(buf, sizeof(buf), "%s/%s", path, dir->name);
+
+    it = eina_file_stat_ls(buf);
+    if (!it) return EINA_TRUE;
+
+    EINA_ITERATOR_FOREACH(it, entry)
+    {
+        Efreet_Cache_Icon *icon;
+        char *name;
+        char *ext;
+        unsigned int i;
+
+        if (entry->type == EINA_FILE_DIR)
+            continue;
+
+        ext = strrchr(entry->path + entry->name_start, '.');
+        if (!ext || !cache_extension_lookup(ext))
+            continue;
+
+        /* icon with known extension */
+        name = entry->path + entry->name_start;
+        *ext = '\0';
+
+        icon = eina_hash_find(icons, name);
+        if (!icon)
+        {
+            icon = NEW(Efreet_Cache_Icon, 1);
+            icon->theme = eina_stringshare_add(theme->name.internal);
+            eina_array_push(strs, icon->theme);
+            eina_hash_add(icons, name, icon);
+        }
+        else if (icon->theme && strcmp(icon->theme, theme->name.internal))
+        {
+            /* We got this icon from a parent theme */
+            continue;
+        }
+
+        /* find if we have the same icon in another type */
+        for (i = 0; i < icon->icons_count; ++i)
+        {
+            if ((icon->icons[i]->type == dir->type) &&
+                (icon->icons[i]->normal == dir->size.normal) &&
+                (icon->icons[i]->max == dir->size.max) &&
+                (icon->icons[i]->min == dir->size.min))
+                break;
+        }
+
+        *ext = '.';
+
+        if (i != icon->icons_count)
+        {
+            unsigned int j;
+
+            /* check if the path already exist */
+            for (j = 0; j < icon->icons[i]->paths_count; ++j)
+                if (!strcmp(icon->icons[i]->paths[j], entry->path))
+                    break;
+
+            if (j != icon->icons[i]->paths_count)
+                continue;
+        }
+        /* no icon match so add a new one */
+        else
+        {
+            icon->icons = realloc(icon->icons,
+                                  sizeof (Efreet_Cache_Icon_Element*) * (++icon->icons_count));
+            icon->icons[i] = NEW(Efreet_Cache_Icon_Element, 1);
+            icon->icons[i]->type = dir->type;
+            icon->icons[i]->normal = dir->size.normal;
+            icon->icons[i]->min = dir->size.min;
+            icon->icons[i]->max = dir->size.max;
+            icon->icons[i]->paths = NULL;
+            icon->icons[i]->paths_count = 0;
+        }
+
+        /* and finally store the path */
+        icon->icons[i]->paths = realloc(icon->icons[i]->paths,
+                                        sizeof (char*) * (icon->icons[i]->paths_count + 1));
+        icon->icons[i]->paths[icon->icons[i]->paths_count] = eina_stringshare_add(entry->path);
+        eina_array_push(strs, icon->icons[i]->paths[icon->icons[i]->paths_count++]);
+    }
+
+    eina_iterator_free(it);
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+cache_scan_path(Efreet_Icon_Theme *theme, Eina_Hash *icons, const char *path)
+{
+    Eina_List *l;
+    Efreet_Icon_Theme_Directory *dir;
+
+    EINA_LIST_FOREACH(theme->directories, l, dir)
+        if (!cache_scan_path_dir(theme, path, dir, icons)) return EINA_FALSE;
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+cache_scan(Efreet_Icon_Theme *theme, Eina_Hash *themes, Eina_Hash *icons)
+{
+    Eina_List *l;
+    const char *path;
+    const char *name;
+
+    if (!theme) return EINA_TRUE;
+    if (eina_hash_find(themes, theme->name.internal)) return EINA_TRUE;
+    eina_hash_direct_add(themes, theme->name.internal, theme);
+
+    /* scan theme */
+    EINA_LIST_FOREACH(theme->paths, l, path)
+        if (!cache_scan_path(theme, icons, path)) return EINA_FALSE;
+
+    /* scan inherits */
+    if (theme->inherits)
+    {
+        EINA_LIST_FOREACH(theme->inherits, l, name)
+        {
+            Efreet_Icon_Theme *inherit;
+
+            inherit = eina_hash_find(icon_themes, name);
+            if (!inherit)
+                INF("Theme `%s` not found for `%s`.",
+                    name, theme->name.internal);
+            if (!cache_scan(inherit, themes, icons)) return EINA_FALSE;
+        }
+    }
+    else if (strcmp(theme->name.internal, "hicolor"))
+    {
+        theme = eina_hash_find(icon_themes, "hicolor");
+        if (!cache_scan(theme, themes, icons)) return EINA_FALSE;
+    }
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+check_changed(Efreet_Cache_Icon_Theme *theme)
+{
+    Eina_List *l;
+    const char *name;
+
+    if (!theme) return EINA_FALSE;
+
+    if (theme->changed) return EINA_TRUE;
+    if (theme->theme.inherits)
+    {
+        EINA_LIST_FOREACH(theme->theme.inherits, l, name)
+        {
+            Efreet_Cache_Icon_Theme *inherit;
+
+            inherit = eina_hash_find(icon_themes, name);
+            if (!inherit)
+                INF("Theme `%s` not found for `%s`.",
+                        name, theme->theme.name.internal);
+            if (check_changed(inherit)) return EINA_TRUE;
+        }
+    }
+    else if (strcmp(theme->theme.name.internal, "hicolor"))
+    {
+        theme = eina_hash_find(icon_themes, "hicolor");
+        if (check_changed(theme)) return EINA_TRUE;
+    }
+    return EINA_FALSE;
+}
+
+static Efreet_Icon_Theme_Directory *
+icon_theme_directory_new(Efreet_Ini *ini, const char *name)
+{
+    Efreet_Icon_Theme_Directory *dir;
+    int val;
+    const char *tmp;
+
+    if (!ini) return NULL;
+
+    dir = NEW(Efreet_Icon_Theme_Directory, 1);
+    if (!dir) return NULL;
+    dir->name = eina_stringshare_add(name);
+    eina_array_push(strs, dir->name);
+
+    efreet_ini_section_set(ini, name);
+
+    tmp = efreet_ini_string_get(ini, "Context");
+    if (tmp)
+    {
+        if (!strcasecmp(tmp, "Actions"))
+            dir->context = EFREET_ICON_THEME_CONTEXT_ACTIONS;
+
+        else if (!strcasecmp(tmp, "Devices"))
+            dir->context = EFREET_ICON_THEME_CONTEXT_DEVICES;
+
+        else if (!strcasecmp(tmp, "FileSystems"))
+            dir->context = EFREET_ICON_THEME_CONTEXT_FILESYSTEMS;
+
+        else if (!strcasecmp(tmp, "MimeTypes"))
+            dir->context = EFREET_ICON_THEME_CONTEXT_MIMETYPES;
+    }
+
+    /* Threshold is fallback  */
+    dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
+
+    tmp = efreet_ini_string_get(ini, "Type");
+    if (tmp)
+    {
+        if (!strcasecmp(tmp, "Fixed"))
+            dir->type = EFREET_ICON_SIZE_TYPE_FIXED;
+
+        else if (!strcasecmp(tmp, "Scalable"))
+            dir->type = EFREET_ICON_SIZE_TYPE_SCALABLE;
+    }
+
+    dir->size.normal = efreet_ini_int_get(ini, "Size");
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
+    {
+        val = efreet_ini_int_get(ini, "Threshold");
+        if (val < 0) val = 2;
+        dir->size.max = dir->size.normal + val;
+        dir->size.min = dir->size.normal - val;
+    }
+    else if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
+    {
+        val = efreet_ini_int_get(ini, "MinSize");
+        if (val < 0) dir->size.min = dir->size.normal;
+        else dir->size.min = val;
+
+        val = efreet_ini_int_get(ini, "MaxSize");
+        if (val < 0) dir->size.max = dir->size.normal;
+        else dir->size.max = val;
+    }
+
+    return dir;
+}
+
+static Eina_Bool
+icon_theme_index_read(Efreet_Cache_Icon_Theme *theme, const char *path)
+{
+    Efreet_Ini *ini;
+    Efreet_Icon_Theme_Directory *dir;
+    const char *tmp;
+    struct stat st;
+    char rp[PATH_MAX];
+
+    if (!theme || !path) return EINA_FALSE;
+
+    if (!realpath(path, rp)) return EINA_FALSE;
+
+    if (stat(rp, &st) < 0) return EINA_FALSE;
+    if (theme->path && !strcmp(theme->path, rp) && theme->last_cache_check >= (long long) st.st_mtime)
+    {
+        /* no change */
+        theme->valid = 1;
+        return EINA_TRUE;
+    }
+    if (!theme->path || strcmp(theme->path, rp))
+    {
+        theme->path = eina_stringshare_add(rp);
+        eina_array_push(strs, theme->path);
+    }
+    if ((long long) st.st_mtime > theme->last_cache_check)
+        theme->last_cache_check = (long long) st.st_mtime;
+    theme->changed = 1;
+
+    ini = efreet_ini_new(path);
+    if (!ini) return EINA_FALSE;
+    if (!ini->data)
+    {
+        efreet_ini_free(ini);
+        return EINA_FALSE;
+    }
+
+    efreet_ini_section_set(ini, "Icon Theme");
+    tmp = efreet_ini_localestring_get(ini, "Name");
+    if (tmp)
+    {
+        theme->theme.name.name = eina_stringshare_add(tmp);
+        eina_array_push(strs, theme->theme.name.name);
+    }
+
+    tmp = efreet_ini_localestring_get(ini, "Comment");
+    if (tmp)
+    {
+        theme->theme.comment = eina_stringshare_add(tmp);
+        eina_array_push(strs, theme->theme.comment);
+    }
+
+    tmp = efreet_ini_string_get(ini, "Example");
+    if (tmp)
+    {
+        theme->theme.example_icon = eina_stringshare_add(tmp);
+        eina_array_push(strs, theme->theme.example_icon);
+    }
+
+    theme->hidden = efreet_ini_boolean_get(ini, "Hidden");
+
+    theme->valid = 1;
+
+    /* Check the inheritance. If there is none we inherit from the hicolor theme */
+    tmp = efreet_ini_string_get(ini, "Inherits");
+    if (tmp)
+    {
+        char *t, *s, *p;
+        const char *i;
+        size_t len;
+
+        len = strlen(tmp) + 1;
+        t = alloca(len);
+        memcpy(t, tmp, len);
+        s = t;
+        p = strchr(s, ',');
+
+        while (p)
+        {
+            *p = '\0';
+
+            i = eina_stringshare_add(s);
+            theme->theme.inherits = eina_list_append(theme->theme.inherits, i);
+            eina_array_push(strs, i);
+            s = ++p;
+            p = strchr(s, ',');
+        }
+        i = eina_stringshare_add(s);
+        theme->theme.inherits = eina_list_append(theme->theme.inherits, i);
+        eina_array_push(strs, i);
+    }
+
+    /* make sure this one is done last as setting the directory will change
+     * the ini section ... */
+    tmp = efreet_ini_string_get(ini, "Directories");
+    if (tmp)
+    {
+        char *t, *s, *p;
+        size_t len;
+
+        len = strlen(tmp) + 1;
+        t = alloca(len);
+        memcpy(t, tmp, len);
+        s = t;
+        p = s;
+
+        while (p)
+        {
+            p = strchr(s, ',');
+
+            if (p) *p = '\0';
+
+            dir = icon_theme_directory_new(ini, s);
+            if (!dir) goto error;
+            theme->theme.directories = eina_list_append(theme->theme.directories, dir);
+
+            if (p) s = ++p;
+        }
+    }
+
+error:
+    efreet_ini_free(ini);
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+cache_theme_scan(const char *dir)
+{
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *entry;
+
+    it = eina_file_stat_ls(dir);
+    if (!it) return EINA_TRUE;
+
+    EINA_ITERATOR_FOREACH(it, entry)
+    {
+        Efreet_Cache_Icon_Theme *theme;
+        const char *name;
+        const char *path;
+        char buf[PATH_MAX];
+        struct stat st;
+
+        if (stat(entry->path, &st) < 0) continue;
+
+        if ((entry->type != EINA_FILE_DIR) &&
+            (entry->type != EINA_FILE_LNK))
+            continue;
+
+        name = entry->path + entry->name_start;
+        theme = eina_hash_find(icon_themes, name);
+
+        if (!theme)
+        {
+            theme = NEW(Efreet_Cache_Icon_Theme, 1);
+            theme->theme.name.internal = eina_stringshare_add(name);
+            eina_array_push(strs, theme->theme.name.internal);
+            eina_hash_direct_add(icon_themes,
+                          (void *)theme->theme.name.internal, theme);
+            theme->changed = 1;
+        }
+        if ((long long) st.st_mtime > theme->last_cache_check)
+        {
+            theme->last_cache_check = (long long) st.st_mtime;
+            theme->changed = 1;
+        }
+
+        /* TODO: We need to handle change in order of included paths */
+        if (!eina_list_search_unsorted(theme->theme.paths, EINA_COMPARE_CB(strcmp), entry->path))
+        {
+            path = eina_stringshare_add(entry->path);
+            theme->theme.paths = eina_list_append(theme->theme.paths, path);
+            eina_array_push(strs, path);
+            theme->changed = 1;
+        }
+
+        /* we're already valid so no reason to check for an index.theme file */
+        if (theme->valid) continue;
+
+        /* if the index.theme file exists we parse it into the theme */
+        memcpy(buf, entry->path, entry->path_length);
+        memcpy(buf + entry->path_length, "/index.theme", sizeof("/index.theme"));
+        if (ecore_file_exists(buf))
+        {
+            if (!icon_theme_index_read(theme, buf))
+                theme->valid = 0;
+        }
+    }
+    eina_iterator_free(it);
+    return EINA_TRUE;
+}
+
+static int
+cache_lock_file(void)
+{
+    char file[PATH_MAX];
+    struct flock fl;
+    int lockfd;
+
+    snprintf(file, sizeof(file), "%s/efreet/icon_data.lock", efreet_cache_home_get());
+    lockfd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+    if (lockfd < 0) return -1;
+    efreet_fsetowner(lockfd);
+
+    memset(&fl, 0, sizeof(struct flock));
+    fl.l_type = F_WRLCK;
+    fl.l_whence = SEEK_SET;
+    if (fcntl(lockfd, F_SETLK, &fl) < 0)
+    {
+        WRN("LOCKED! You may want to delete %s if this persists", file);
+        close(lockfd);
+        return -1;
+    }
+
+    return lockfd;
+}
+
+static void
+icon_theme_free(Efreet_Cache_Icon_Theme *theme)
+{
+    void *data;
+
+    eina_list_free(theme->theme.paths);
+    eina_list_free(theme->theme.inherits);
+    EINA_LIST_FREE(theme->theme.directories, data)
+        free(data);
+    if (theme->dirs) efreet_hash_free(theme->dirs, free);
+    free(theme);
+}
+
+/**
+ * @internal
+ * @return EINA_TRUE if data adds new
+ */
+static Eina_Bool
+add_data(Eet_File *ef, Eina_Array *data, const char *key)
+{
+    Efreet_Cache_Array_String *add;
+    unsigned int i, j;
+    Eina_Bool added = EINA_FALSE;
+
+    add = eet_data_read(ef, efreet_array_string_edd(), key);
+    if (!add) return EINA_TRUE;
+    /* loop once to check added */
+    for (i = 0; i < data->count; i++)
+    {
+        int found = 0;
+        for (j = 0; j < add->array_count; ++j)
+        {
+            if (!strcmp(add->array[j], data->data[i]))
+            {
+                found = 1;
+                break;
+            }
+        }
+        if (!found)
+        {
+            added = EINA_TRUE;
+            break;
+        }
+    }
+    /* loop again to add all data */
+    for (i = 0; i < add->array_count; i++)
+    {
+        int found = 0;
+        for (j = 0; j < data->count; ++j)
+        {
+            if (!strcmp(add->array[i], data->data[j]))
+            {
+                found = 1;
+                break;
+            }
+        }
+        if (!found)
+            eina_array_push(data, add->array[i]);
+    }
+    IF_FREE(add->array);
+    free(add);
+
+    return added;
+}
+
+
+static void
+save_data(Eet_File *ef, Eina_Array *data, const char *key)
+{
+    Efreet_Cache_Array_String *save;
+    unsigned int i;
+
+    if (!data || !data->count) return;
+
+    save = NEW(Efreet_Cache_Array_String, 1);
+    save->array = NEW(char *, data->count);
+    save->array_count = 0;
+    for (i = 0; i < data->count; ++i)
+        save->array[save->array_count++] = data->data[i];
+    eet_data_write(ef, efreet_array_string_edd(), key, save, 1);
+    IF_FREE(save->array);
+    free(save);
+}
+
+int
+main(int argc, char **argv)
+{
+    /* TODO:
+     * - Add file monitor on files, so that we catch changes on files
+     *   during whilst this program runs.
+     * - Maybe linger for a while to reduce number of cache re-creates.
+     */
+    Eina_Iterator *it;
+    Efreet_Cache_Version *icon_version;
+    Efreet_Cache_Version *theme_version;
+    Efreet_Cache_Icon_Theme *theme;
+    Eet_Data_Descriptor *theme_edd;
+    Eet_Data_Descriptor *icon_edd;
+    Eet_Data_Descriptor *fallback_edd;
+    Eet_File *icon_ef;
+    Eet_File *theme_ef;
+    Eina_List *xdg_dirs = NULL;
+    Eina_List *l = NULL;
+    char file[PATH_MAX];
+    const char *path;
+    char *dir = NULL;
+    Eina_Bool changed = EINA_FALSE;
+    Eina_Bool flush = EINA_FALSE;
+    int lockfd = -1;
+    int tmpfd = -1;
+    char **keys;
+    int num, i;
+
+    /* init external subsystems */
+    if (!eina_init()) return -1;
+    _efreet_icon_cache_log_dom =
+        eina_log_domain_register("efreet_icon_cache", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_icon_cache_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_icon_cache.");
+        return -1;
+    }
+
+    eina_log_domain_level_set("efreet_icon_cache", EINA_LOG_LEVEL_ERR);
+
+    exts = eina_array_new(10);
+    extra_dirs = eina_array_new(10);
+
+    for (i = 1; i < argc; i++)
+    {
+        if (!strcmp(argv[i], "-v"))
+            eina_log_domain_level_set("efreet_icon_cache", EINA_LOG_LEVEL_DBG);
+        else if ((!strcmp(argv[i], "-h")) ||
+                 (!strcmp(argv[i], "-help")) ||
+                 (!strcmp(argv[i], "--h")) ||
+                 (!strcmp(argv[i], "--help")))
+        {
+            printf("Options:\n");
+            printf("  -v              Verbose mode\n");
+            printf("  -e .ext1 .ext2  Extensions\n");
+            printf("  -d dir1 dir2    Extra dirs\n");
+            exit(0);
+        }
+        else if (!strcmp(argv[i], "-e"))
+        {
+            while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
+                eina_array_push(exts, argv[++i]);
+        }
+        else if (!strcmp(argv[i], "-d"))
+        {
+            while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
+                eina_array_push(extra_dirs, argv[++i]);
+        }
+    }
+
+    if (!eet_init()) return -1;
+    if (!ecore_init()) return -1;
+
+    efreet_cache_update = 0;
+    /* finish efreet init */
+    if (!efreet_init()) goto on_error;
+
+    strs = eina_array_new(32);
+
+    /* create homedir */
+    snprintf(file, sizeof(file), "%s/efreet", efreet_cache_home_get());
+    if (!ecore_file_exists(file))
+    {
+        if (!ecore_file_mkpath(file)) return -1;
+        efreet_setowner(file);
+    }
+
+    /* lock process, so that we only run one copy of this program */
+    lockfd = cache_lock_file();
+    if (lockfd == -1) goto on_error;
+
+    /* Need to init edd's, so they are like we want, not like userspace wants */
+    icon_edd = efreet_icon_edd();
+    fallback_edd = efreet_icon_fallback_edd();
+    theme_edd = efreet_icon_theme_edd(EINA_TRUE);
+
+    icon_themes = eina_hash_string_superfast_new(EINA_FREE_CB(icon_theme_free));
+
+    INF("opening theme cache");
+    /* open theme file */
+    theme_ef = eet_open(efreet_icon_theme_cache_file(), EET_FILE_MODE_READ_WRITE);
+    if (!theme_ef) goto on_error_efreet;
+    theme_version = eet_data_read(theme_ef, efreet_version_edd(), EFREET_CACHE_VERSION);
+    if (theme_version &&
+        ((theme_version->major != EFREET_ICON_CACHE_MAJOR) ||
+         (theme_version->minor != EFREET_ICON_CACHE_MINOR)))
+    {
+        // delete old cache
+        eet_close(theme_ef);
+        if (unlink(efreet_icon_theme_cache_file()) < 0)
+        {
+            if (errno != ENOENT) goto on_error_efreet;
+        }
+        theme_ef = eet_open(efreet_icon_theme_cache_file(), EET_FILE_MODE_READ_WRITE);
+        if (!theme_ef) goto on_error_efreet;
+    }
+    if (!theme_version)
+        theme_version = NEW(Efreet_Cache_Version, 1);
+
+    theme_version->major = EFREET_ICON_CACHE_MAJOR;
+    theme_version->minor = EFREET_ICON_CACHE_MINOR;
+
+    if (add_data(theme_ef, exts, EFREET_CACHE_ICON_EXTENSIONS))
+        flush = EINA_TRUE;
+    if (add_data(theme_ef, extra_dirs, EFREET_CACHE_ICON_EXTRA_DIRS))
+        flush = EINA_TRUE;
+    if (flush)
+        changed = EINA_TRUE;
+
+    if (exts->count == 0)
+    {
+        ERR("Need to pass extensions to icon cache create process");
+        goto on_error_efreet;
+    }
+
+    keys = eet_list(theme_ef, "*", &num);
+    if (keys)
+    {
+        for (i = 0; i < num; i++)
+        {
+            if (!strncmp(keys[i], "__efreet", 8)) continue;
+            theme = eet_data_read(theme_ef, theme_edd, keys[i]);
+            if (theme)
+            {
+                theme->valid = 0;
+                eina_hash_direct_add(icon_themes, theme->theme.name.internal, theme);
+            }
+        }
+        free(keys);
+    }
+
+    INF("scan for themes");
+    /* scan themes */
+    cache_theme_scan(efreet_icon_deprecated_user_dir_get());
+    cache_theme_scan(efreet_icon_user_dir_get());
+
+    xdg_dirs = efreet_data_dirs_get();
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(file, sizeof(file), "%s/icons", dir);
+        cache_theme_scan(file);
+    }
+
+#ifndef STRICT_SPEC
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(file, sizeof(file), "%s/pixmaps", dir);
+        cache_theme_scan(file);
+    }
+#endif
+
+    cache_theme_scan("/usr/share/pixmaps");
+
+    /* scan icons */
+    it = eina_hash_iterator_data_new(icon_themes);
+    EINA_ITERATOR_FOREACH(it, theme)
+    {
+        if (!theme->valid) continue;
+#ifndef STRICT_SPEC
+        if (!theme->theme.name.name) continue;
+#endif
+        INF("scan theme %s", theme->theme.name.name);
+
+        theme->changed = check_changed(theme);
+        if (flush)
+            theme->changed = EINA_TRUE;
+
+        INF("open icon file");
+        /* open icon file */
+        icon_ef = eet_open(efreet_icon_cache_file(theme->theme.name.internal), EET_FILE_MODE_READ_WRITE);
+        if (!icon_ef) goto on_error_efreet;
+        icon_version = eet_data_read(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION);
+        if (theme->changed || (icon_version &&
+            ((icon_version->major != EFREET_ICON_CACHE_MAJOR) ||
+             (icon_version->minor != EFREET_ICON_CACHE_MINOR))))
+        {
+            // delete old cache
+            eet_close(icon_ef);
+            if (unlink(efreet_icon_cache_file(theme->theme.name.internal)) < 0)
+            {
+                if (errno != ENOENT) goto on_error_efreet;
+            }
+            icon_ef = eet_open(efreet_icon_cache_file(theme->theme.name.internal), EET_FILE_MODE_READ_WRITE);
+            if (!icon_ef) goto on_error_efreet;
+            theme->changed = EINA_TRUE;
+        }
+
+        if (theme->changed)
+            changed = EINA_TRUE;
+
+        if (theme->changed)
+        {
+            Eina_Hash *themes;
+            Eina_Hash *icons;
+
+            if (!icon_version)
+                icon_version = NEW(Efreet_Cache_Version, 1);
+
+            icon_version->major = EFREET_ICON_CACHE_MAJOR;
+            icon_version->minor = EFREET_ICON_CACHE_MINOR;
+
+            themes = eina_hash_string_superfast_new(NULL);
+            icons = eina_hash_string_superfast_new(NULL);
+
+            INF("scan icons\n");
+            if (cache_scan(&(theme->theme), themes, icons))
+            {
+                Eina_Iterator *icons_it;
+                Eina_Hash_Tuple *tuple;
+
+                INF("generated: '%s' %i (%i)",
+                    theme->theme.name.internal,
+                    changed,
+                    eina_hash_population(icons));
+
+                icons_it = eina_hash_iterator_tuple_new(icons);
+                EINA_ITERATOR_FOREACH(icons_it, tuple)
+                    eet_data_write(icon_ef, icon_edd, tuple->key, tuple->data, 1);
+                eina_iterator_free(icons_it);
+
+                INF("theme change: %s %lld", theme->theme.name.internal, theme->last_cache_check);
+                eet_data_write(theme_ef, theme_edd, theme->theme.name.internal, theme, 1);
+            }
+            eina_hash_free(themes);
+            eina_hash_free(icons);
+        }
+
+        eet_data_write(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION, icon_version, 1);
+        eet_close(icon_ef);
+        efreet_setowner(efreet_icon_cache_file(theme->theme.name.internal));
+        free(icon_version);
+    }
+    eina_iterator_free(it);
+
+    INF("scan fallback icons");
+    theme = eet_data_read(theme_ef, theme_edd, EFREET_CACHE_ICON_FALLBACK);
+    if (!theme)
+    {
+        theme = NEW(Efreet_Cache_Icon_Theme, 1);
+        theme->changed = EINA_TRUE;
+    }
+    if (flush)
+        theme->changed = EINA_TRUE;
+
+    INF("open fallback file");
+    /* open icon file */
+    icon_ef = eet_open(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EET_FILE_MODE_READ_WRITE);
+    if (!icon_ef) goto on_error_efreet;
+    icon_version = eet_data_read(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION);
+    if (theme->changed || (icon_version &&
+        ((icon_version->major != EFREET_ICON_CACHE_MAJOR) ||
+         (icon_version->minor != EFREET_ICON_CACHE_MINOR))))
+    {
+        // delete old cache
+        eet_close(icon_ef);
+        if (unlink(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK)) < 0)
+        {
+            if (errno != ENOENT) goto on_error_efreet;
+        }
+        icon_ef = eet_open(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EET_FILE_MODE_READ_WRITE);
+        if (!icon_ef) goto on_error_efreet;
+        theme->changed = EINA_TRUE;
+    }
+    if (!theme->changed)
+        theme->changed = check_fallback_changed(theme);
+    if (theme->changed && theme->dirs)
+    {
+        efreet_hash_free(theme->dirs, free);
+        theme->dirs = NULL;
+    }
+    if (!theme->dirs)
+        theme->dirs = eina_hash_string_superfast_new(NULL);
+
+    if (theme->changed)
+        changed = EINA_TRUE;
+
+    if (theme->changed)
+    {
+        Eina_Hash *icons;
+
+        if (!icon_version)
+            icon_version = NEW(Efreet_Cache_Version, 1);
+
+        icon_version->major = EFREET_ICON_CACHE_MAJOR;
+        icon_version->minor = EFREET_ICON_CACHE_MINOR;
+
+        icons = eina_hash_string_superfast_new(NULL);
+
+        INF("scan fallback icons");
+        /* Save fallback in the right part */
+        if (cache_fallback_scan(icons, theme->dirs))
+        {
+            Eina_Iterator *icons_it;
+            Eina_Hash_Tuple *tuple;
+
+            INF("generated: fallback %i (%i)", theme->changed, eina_hash_population(icons));
+
+            icons_it = eina_hash_iterator_tuple_new(icons);
+            EINA_ITERATOR_FOREACH(icons_it, tuple)
+                eet_data_write(icon_ef, fallback_edd, tuple->key, tuple->data, 1);
+            eina_iterator_free(icons_it);
+        }
+        eina_hash_free(icons);
+
+        eet_data_write(theme_ef, theme_edd, EFREET_CACHE_ICON_FALLBACK, theme, 1);
+    }
+
+    icon_theme_free(theme);
+
+    eet_data_write(icon_ef, efreet_version_edd(), EFREET_CACHE_VERSION, icon_version, 1);
+    eet_close(icon_ef);
+    efreet_setowner(efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK));
+    free(icon_version);
+
+    eina_hash_free(icon_themes);
+
+    /* save data */
+    eet_data_write(theme_ef, efreet_version_edd(), EFREET_CACHE_VERSION, theme_version, 1);
+    save_data(theme_ef, exts, EFREET_CACHE_ICON_EXTENSIONS);
+    save_data(theme_ef, extra_dirs, EFREET_CACHE_ICON_EXTRA_DIRS);
+
+    eet_close(theme_ef);
+    efreet_setowner(efreet_icon_theme_cache_file());
+    free(theme_version);
+
+    /* touch update file */
+    snprintf(file, sizeof(file), "%s/efreet/icon_data.update", efreet_cache_home_get());
+    tmpfd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+    if (tmpfd >= 0)
+    {
+        char c = 'n';
+
+        efreet_fsetowner(tmpfd);
+        if (changed) c = 'c';
+        if (write(tmpfd, &c, 1) != 1) perror("write");
+        close(tmpfd);
+    }
+
+    INF("done");
+on_error_efreet:
+    efreet_shutdown();
+
+on_error:
+    if (lockfd >= 0) close(lockfd);
+
+    while ((path = eina_array_pop(strs)))
+        eina_stringshare_del(path);
+    eina_array_free(strs);
+    eina_array_free(exts);
+    eina_array_free(extra_dirs);
+
+    ecore_shutdown();
+    eet_shutdown();
+    eina_log_domain_unregister(_efreet_icon_cache_log_dom);
+    eina_shutdown();
+
+    return 0;
+}
diff --git a/src/lib/Efreet.h b/src/lib/Efreet.h
new file mode 100644 (file)
index 0000000..c5b8104
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef EFREET_H
+#define EFREET_H
+
+/**
+ * @file Efreet.h
+ * @brief The file that must be included by any project wishing to use
+ * Efreet. Efreet.h provides all of the necessary headers and includes to
+ * work with Efreet.
+ */
+
+/**
+ * @mainpage The Efreet Library
+ *
+ * @section intro Introduction
+ *
+ * Efreet is a library designed to help apps work several of the
+ * Freedesktop.org standards regarding Icons, Desktop files and Menus. To
+ * that end it implements the following specifications:
+ *
+ * @li XDG Base Directory Specification
+ * @li Icon Theme Specification
+ * @li Desktop Entry Specification
+ * @li Desktop Menu Specification
+ * @li FDO URI Specification
+ * @li Shared Mime Info Specification
+ * @li Trash Specification
+ */
+
+#include <Eina.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EFREET_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_EFREET_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EFREET_VERSION_MAJOR 1
+#define EFREET_VERSION_MINOR 7
+   
+   typedef struct _Efreet_Version
+     {
+        int major;
+        int minor;
+        int micro;
+        int revision;
+     } Efreet_Version;
+   
+   EAPI extern Efreet_Version *efreet_version;
+   
+#include "efreet_base.h"
+#include "efreet_ini.h"
+#include "efreet_icon.h"
+#include "efreet_desktop.h"
+#include "efreet_menu.h"
+#include "efreet_utils.h"
+#include "efreet_uri.h"
+
+/**
+ * @return Value > @c 0 if the initialization was successful, @c 0 otherwise.
+ * @brief Initializes the Efreet system
+ */
+EAPI int efreet_init(void);
+
+/**
+ * @return The number of times the init function has been called minus the
+ * corresponding init call.
+ * @brief Shuts down Efreet if a balanced number of init/shutdown calls have
+ * been made
+ */
+EAPI int efreet_shutdown(void);
+
+/**
+ * @brief Resets language dependent variables and resets language dependent
+ * caches This must be called whenever the locale is changed.
+ * @since 1.7
+ */
+EAPI void efreet_lang_reset(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/Efreet_Mime.h b/src/lib/Efreet_Mime.h
new file mode 100644 (file)
index 0000000..d6873fc
--- /dev/null
@@ -0,0 +1,125 @@
+#ifndef EFREET_MIME_H
+#define EFREET_MIME_H
+
+/**
+ * @file Efreet_Mime.h
+ * @brief The file that must be included by any project wishing to use
+ * @addtogroup Efreet_Mime Efreet_Mime: The XDG Shared Mime Info standard
+ * Efreet Mime is a library designed to help apps work with the
+ * Freedesktop.org Shared Mime Info standard.
+ * Efreet_Mime.h provides all of the necessary headers and
+ * includes to work with Efreet_Mime.
+ * @{
+ */
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EFREET_MIME_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_EFREET_MIME_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @return @c 1 on success or @c 0 on failure.
+ * @brief Initializes the efreet mime settings
+ */
+EAPI int         efreet_mime_init(void);
+
+/**
+ * @return No value.
+ * @brief Cleans up the efreet mime settings system
+ */
+EAPI int         efreet_mime_shutdown(void);
+
+/**
+ * @param file The file to find the mime type
+ * @return Mime type as a string.
+ * @brief Retrieve the mime type of a file
+ */
+EAPI const char *efreet_mime_type_get(const char *file);
+
+/**
+ * @param file The file to check the mime type
+ * @return Mime type as a string.
+ * @brief Retrieve the mime type of a file using magic
+ */
+EAPI const char *efreet_mime_magic_type_get(const char *file);
+
+/**
+ * @param file The file to check the mime type
+ * @return Mime type as a string.
+ * @brief Retrieve the mime type of a file using globs
+ */
+EAPI const char *efreet_mime_globs_type_get(const char *file);
+
+/**
+ * @param file The file to check the mime type
+ * @return Mime type as a string.
+ * @brief Retrieve the special mime type of a file
+ */
+EAPI const char *efreet_mime_special_type_get(const char *file);
+
+/**
+ * @param file The file to check the mime type
+ * @return Mime type as a string.
+ * @brief Retrieve the fallback mime type of a file.
+ */
+EAPI const char *efreet_mime_fallback_type_get(const char *file);
+
+
+/**
+ * @param mime The name of the mime type
+ * @param theme The name of the theme to search icons in
+ * @param size The wanted size of the icon
+ * @return Mime type icon path as a string.
+ * @brief Retrieve the mime type icon for a file.
+ */
+EAPI const char *efreet_mime_type_icon_get(const char *mime, const char *theme,
+                                           unsigned int size);
+
+/**
+ * @brief Clear mime icons mapping cache
+ */
+EAPI void efreet_mime_type_cache_clear(void);
+
+/**
+ * @brief Flush mime icons mapping cache
+ *
+ * Flush timeout is defined at compile time by
+ * EFREET_MIME_ICONS_FLUSH_TIMEOUT
+ */
+EAPI void efreet_mime_type_cache_flush(void);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/Efreet_Trash.h b/src/lib/Efreet_Trash.h
new file mode 100644 (file)
index 0000000..3f86aa2
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef EFREET_TRASH_H
+#define EFREET_TRASH_H
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EFREET_TRASH_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_EFREET_TRASH_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file Efreet_Trash.h
+ * @brief Contains the methods used to support the FDO trash specification.
+ * @addtogroup Efreet_Trash Efreet_Trash: The XDG Trash Specification
+ * Efreet_Trash.h provides all of the necessary headers and includes to
+ * work with Efreet_Trash.
+ * @{
+ */
+
+/**
+ * @return @c 1 on success or @c 0 on failure.
+ * @brief Initializes the efreet trash system
+ */
+EAPI int         efreet_trash_init(void);
+
+/**
+ * @return No value.
+ * @brief Cleans up the efreet trash system
+ */
+EAPI int         efreet_trash_shutdown(void);
+
+/**
+ * @return The XDG Trash local directory or @c NULL on errors.
+ * Return value must be freed with eina_stringshare_del.
+ * @brief Retrieves the XDG Trash local directory
+ */
+EAPI const char *efreet_trash_dir_get(const char *for_file);
+
+/**
+ * @param uri The local uri to move in the trash
+ * @param force_delete If you set this to @c 1 than files on different filesystems
+ * will be deleted permanently
+ * @return @c 1 on success, @c 0 on failure or @c -1 in case the uri is not on
+ * the same filesystem and force_delete is not set.
+ * @brief This function try to move the given uri to the trash. Files on 
+ * different filesystem can't be moved to trash. If force_delete
+ * is @c 0 than non-local files will be ignored and @c -1 is returned, if you set
+ * force_delete to @c 1 non-local files will be deleted without asking.
+ */
+EAPI int         efreet_trash_delete_uri(Efreet_Uri *uri, int force_delete);
+
+/**
+ * @return A list of strings with filename (remember to free the list
+ * when you don't need anymore).
+ * @brief List all the files and directory currently inside the trash.
+ */
+EAPI Eina_List  *efreet_trash_ls(void);
+
+/**
+ * @return @c 1 if the trash is empty or @c 0 if some file are in.
+ * @brief Check if the trash is currently empty
+ */
+EAPI int         efreet_trash_is_empty(void);
+
+/**
+ * @return @c 1 on success or @c 0 on failure.
+ * @brief Delete all the files inside the trash.
+ */
+EAPI int         efreet_trash_empty_trash(void);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644 (file)
index 0000000..824c8de
--- /dev/null
@@ -0,0 +1,69 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+lib_LTLIBRARIES = libefreet.la libefreet_mime.la libefreet_trash.la
+
+INCLUDES = \
+-DLOCALE_DIR=\"@LOCALE_DIR@\"
+
+EFREETHEADERS = \
+Efreet.h \
+efreet_base.h \
+efreet_desktop.h \
+efreet_icon.h \
+efreet_ini.h \
+efreet_menu.h \
+efreet_utils.h \
+efreet_uri.h
+
+EFREETSOURCES = \
+efreet.c \
+efreet_base.c \
+efreet_icon.c \
+efreet_xml.c \
+efreet_ini.c \
+efreet_desktop.c \
+efreet_desktop_command.c \
+efreet_menu.c \
+efreet_utils.c \
+efreet_uri.c \
+efreet_cache.c
+
+includes_HEADERS = $(EFREETHEADERS) Efreet_Mime.h Efreet_Trash.h
+includesdir = $(includedir)/efreet-@VMAJ@
+
+# Not sure if this was for 'make dist', so left it in but commented - dh
+# dist_installed_headers_DATA = $(EFREETHEADERS) Efreet_Mime.h Efreet_Trash.h
+
+libefreet_la_SOURCES = $(EFREETSOURCES)
+libefreet_la_CPPFLAGS =  \
+-DPACKAGE_DATA_DIR=\"$(datadir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-I$(top_builddir)/src/lib \
+-I$(top_srcdir)/src/lib \
+@EFL_EFREET_BUILD@ \
+@EFREET_CFLAGS@
+libefreet_la_LIBADD = @EFREET_LIBS@ @WIN32_LIBS@
+libefreet_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @release_info@
+
+
+libefreet_mime_la_SOURCES = efreet_mime.c
+libefreet_mime_la_CPPFLAGS = \
+-I$(top_builddir)/src/lib \
+-I$(top_srcdir)/src/lib \
+@EFL_EFREET_MIME_BUILD@ \
+@EFREET_CFLAGS@
+libefreet_mime_la_LIBADD = @EFREET_LIBS@ libefreet.la @WIN32_LIBS@
+libefreet_mime_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @release_info@
+
+
+libefreet_trash_la_SOURCES = efreet_trash.c
+libefreet_trash_la_CPPFLAGS = \
+-I$(top_builddir)/src/lib \
+-I$(top_srcdir)/src/lib \
+@EFL_EFREET_TRASH_BUILD@ \
+@EFREET_CFLAGS@
+libefreet_trash_la_LIBADD = @EFREET_LIBS@ libefreet.la
+libefreet_trash_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @release_info@
+
+EXTRA_DIST = efreet_private.h efreet_xml.h efreet_cache_private.h
diff --git a/src/lib/efreet.c b/src/lib/efreet.c
new file mode 100644 (file)
index 0000000..c48223f
--- /dev/null
@@ -0,0 +1,366 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <Eet.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM /* no logging in this file */
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_xml.h"
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI int efreet_cache_update = 1;
+
+static int _efreet_init_count = 0;
+static int efreet_parsed_locale = 0;
+static const char *efreet_lang = NULL;
+static const char *efreet_lang_country = NULL;
+static const char *efreet_lang_modifier = NULL;
+static void efreet_parse_locale(void);
+static int efreet_parse_locale_setting(const char *env);
+
+#ifndef _WIN32
+static uid_t ruid;
+static uid_t rgid;
+#endif
+
+EAPI int
+efreet_init(void)
+{
+#ifndef _WIN32
+    char *tmp;
+#endif
+
+    if (++_efreet_init_count != 1)
+        return _efreet_init_count;
+
+#ifndef _WIN32
+    /* Find users real uid and gid */
+    tmp = getenv("SUDO_UID");
+    if (tmp)
+        ruid = strtoul(tmp, NULL, 10);
+    else
+        ruid = getuid();
+
+    tmp = getenv("SUDO_GID");
+    if (tmp)
+        rgid = strtoul(tmp, NULL, 10);
+    else
+        rgid = getgid();
+#endif
+
+    if (!eina_init())
+        return --_efreet_init_count;
+    if (!eet_init())
+        goto shutdown_eina;
+    if (!ecore_init())
+        goto shutdown_eet;
+    if (!ecore_file_init())
+        goto shutdown_ecore;
+
+    if (!efreet_base_init())
+        goto shutdown_ecore_file;
+
+    if (!efreet_cache_init())
+        goto shutdown_efreet_base;
+
+    if (!efreet_xml_init())
+        goto shutdown_efreet_cache;
+
+    if (!efreet_icon_init())
+        goto shutdown_efreet_xml;
+
+    if (!efreet_ini_init())
+        goto shutdown_efreet_icon;
+
+    if (!efreet_desktop_init())
+        goto shutdown_efreet_ini;
+
+    if (!efreet_menu_init())
+        goto shutdown_efreet_desktop;
+
+    if (!efreet_util_init())
+        goto shutdown_efreet_menu;
+
+#ifdef ENABLE_NLS
+    bindtextdomain(PACKAGE, LOCALE_DIR);
+    bind_textdomain_codeset(PACKAGE, "UTF-8");
+#endif
+
+    return _efreet_init_count;
+
+shutdown_efreet_menu:
+    efreet_menu_shutdown();
+shutdown_efreet_desktop:
+    efreet_desktop_shutdown();
+shutdown_efreet_ini:
+    efreet_ini_shutdown();
+shutdown_efreet_icon:
+    efreet_icon_shutdown();
+shutdown_efreet_xml:
+    efreet_xml_shutdown();
+shutdown_efreet_cache:
+    efreet_cache_shutdown();
+shutdown_efreet_base:
+    efreet_base_shutdown();
+shutdown_ecore_file:
+    ecore_file_shutdown();
+shutdown_ecore:
+    ecore_shutdown();
+shutdown_eet:
+    eet_shutdown();
+shutdown_eina:
+    eina_shutdown();
+
+    return --_efreet_init_count;
+}
+
+EAPI int
+efreet_shutdown(void)
+{
+    if (_efreet_init_count <= 0)
+      {
+         EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
+         return 0;
+      }
+    if (--_efreet_init_count != 0)
+        return _efreet_init_count;
+
+    efreet_util_shutdown();
+    efreet_menu_shutdown();
+    efreet_desktop_shutdown();
+    efreet_ini_shutdown();
+    efreet_icon_shutdown();
+    efreet_xml_shutdown();
+    efreet_cache_shutdown();
+    efreet_base_shutdown();
+
+    IF_RELEASE(efreet_lang);
+    IF_RELEASE(efreet_lang_country);
+    IF_RELEASE(efreet_lang_modifier);
+    efreet_parsed_locale = 0;  /* reset this in case they init efreet again */
+
+    ecore_file_shutdown();
+    ecore_shutdown();
+    eet_shutdown();
+    eina_shutdown();
+
+    return _efreet_init_count;
+}
+
+EAPI void
+efreet_lang_reset(void)
+{
+    IF_RELEASE(efreet_lang);
+    IF_RELEASE(efreet_lang_country);
+    IF_RELEASE(efreet_lang_modifier);
+    efreet_parsed_locale = 0;  /* reset this in case they init efreet again */
+
+    efreet_dirs_reset();
+    efreet_cache_desktop_close();
+    efreet_cache_desktop_update();
+}
+
+ /**
+ * @internal
+ * @return Returns the current users language setting or NULL if none set
+ * @brief Retrieves the current language setting
+ */
+const char *
+efreet_lang_get(void)
+{
+    if (efreet_parsed_locale) return efreet_lang;
+
+    efreet_parse_locale();
+    return efreet_lang;
+}
+
+/**
+ * @internal
+ * @return Returns the current language country setting or NULL if none set
+ * @brief Retrieves the current country setting for the current language or
+ */
+const char *
+efreet_lang_country_get(void)
+{
+    if (efreet_parsed_locale) return efreet_lang_country;
+
+    efreet_parse_locale();
+    return efreet_lang_country;
+}
+
+/**
+ * @internal
+ * @return Returns the current language modifier setting or NULL if none
+ * set.
+ * @brief Retrieves the modifier setting for the language.
+ */
+const char *
+efreet_lang_modifier_get(void)
+{
+    if (efreet_parsed_locale) return efreet_lang_modifier;
+
+    efreet_parse_locale();
+    return efreet_lang_modifier;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Parses out the language, country and modifer setting from the
+ * LC_MESSAGES environment variable
+ */
+static void
+efreet_parse_locale(void)
+{
+    efreet_parsed_locale = 1;
+
+    if (efreet_parse_locale_setting("LANG"))
+        return;
+
+    if (efreet_parse_locale_setting("LC_ALL"))
+        return;
+
+    efreet_parse_locale_setting("LC_MESSAGES");
+}
+
+/**
+ * @internal
+ * @param env The environment variable to grab
+ * @return Returns 1 if we parsed something of @a env, 0 otherwise
+ * @brief Tries to parse the lang settings out of the given environment
+ * variable
+ */
+static int
+efreet_parse_locale_setting(const char *env)
+{
+    int found = 0;
+    char *setting;
+    char *p;
+    size_t len;
+
+    p = getenv(env);
+    if (!p) return 0;
+    len = strlen(p) + 1;
+    setting = alloca(len);
+    memcpy(setting, p, len);
+
+    /* pull the modifier off the end */
+    p = strrchr(setting, '@');
+    if (p)
+    {
+        *p = '\0';
+        efreet_lang_modifier = eina_stringshare_add(p + 1);
+        found = 1;
+    }
+
+    /* if there is an encoding we ignore it */
+    p = strrchr(setting, '.');
+    if (p) *p = '\0';
+
+    /* get the country if available */
+    p = strrchr(setting, '_');
+    if (p)
+    {
+        *p = '\0';
+        efreet_lang_country = eina_stringshare_add(p + 1);
+        found = 1;
+    }
+
+    if (*setting != '\0')
+    {
+        efreet_lang = eina_stringshare_add(setting);
+        found = 1;
+    }
+
+    return found;
+}
+
+/**
+ * @internal
+ * @param buffer The destination buffer
+ * @param size The destination buffer size
+ * @param strs The strings to concatenate together
+ * @return Returns the size of the string in @a buffer
+ * @brief Concatenates the strings in @a strs into the given @a buffer not
+ * exceeding the given @a size.
+ */
+size_t
+efreet_array_cat(char *buffer, size_t size, const char *strs[])
+{
+    int i;
+    size_t n;
+    for (i = 0, n = 0; n < size && strs[i]; i++)
+    {
+        n += eina_strlcpy(buffer + n, strs[i], size - n);
+    }
+    return n;
+}
+
+#ifndef _WIN32
+EAPI void
+efreet_fsetowner(int fd)
+{
+    struct stat st;
+
+    if (fd < 0) return;
+    if (fstat(fd, &st) < 0) return;
+    if (st.st_uid == ruid) return;
+
+    if (fchown(fd, ruid, rgid) != 0) return;
+}
+#else
+EAPI void
+efreet_fsetowner(int fd __UNUSED__)
+{
+}
+#endif
+
+#ifndef _WIN32
+EAPI void
+efreet_setowner(const char *path)
+{
+    EINA_SAFETY_ON_NULL_RETURN(path);
+
+    int fd;
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0) return;
+    efreet_fsetowner(fd);
+    close(fd);
+}
+#else
+EAPI void
+efreet_setowner(const char *path __UNUSED__)
+{
+}
+#endif
diff --git a/src/lib/efreet_base.c b/src/lib/efreet_base.c
new file mode 100644 (file)
index 0000000..afb5920
--- /dev/null
@@ -0,0 +1,387 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <unistd.h>
+
+#ifdef _WIN32
+# include <winsock2.h>
+#endif
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_base_log_dom
+static int _efreet_base_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static Efreet_Version _version = { VMAJ, VMIN, VMIC, VREV };
+EAPI Efreet_Version *efreet_version = &_version;
+
+#ifdef _WIN32
+# define EFREET_PATH_SEP ';'
+#else
+# define EFREET_PATH_SEP ':'
+#endif
+
+static const char *efreet_home_dir = NULL;
+static const char *xdg_data_home = NULL;
+static const char *xdg_config_home = NULL;
+static const char *xdg_cache_home = NULL;
+static Eina_List  *xdg_data_dirs = NULL;
+static Eina_List  *xdg_config_dirs = NULL;
+static const char *xdg_desktop_dir = NULL;
+static const char *hostname = NULL;
+
+static const char *efreet_dir_get(const char *key, const char *fallback);
+static Eina_List  *efreet_dirs_get(const char *key,
+                                        const char *fallback);
+static const char *efreet_user_dir_get(const char *key, const char *fallback);
+
+/**
+ * @internal
+ * @return Returns @c 1 on success or @c 0 on failure
+ * @brief Initializes the efreet base settings
+ */
+int
+efreet_base_init(void)
+{
+    _efreet_base_log_dom = eina_log_domain_register
+      ("efreet_base", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_base_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_base.\n");
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Cleans up the efreet base settings system
+ */
+void
+efreet_base_shutdown(void)
+{
+    IF_RELEASE(efreet_home_dir);
+    IF_RELEASE(xdg_desktop_dir);
+    IF_RELEASE(xdg_data_home);
+    IF_RELEASE(xdg_config_home);
+    IF_RELEASE(xdg_cache_home);
+
+    IF_FREE_LIST(xdg_data_dirs, eina_stringshare_del);
+    IF_FREE_LIST(xdg_config_dirs, eina_stringshare_del);
+
+    IF_RELEASE(hostname);
+
+    eina_log_domain_unregister(_efreet_base_log_dom);
+    _efreet_base_log_dom = -1;
+}
+
+/**
+ * @internal
+ * @return Returns the users home directory
+ * @brief Gets the users home directory and returns it.
+ */
+const char *
+efreet_home_dir_get(void)
+{
+    if (efreet_home_dir) return efreet_home_dir;
+
+    efreet_home_dir = getenv("HOME");
+#ifdef _WIN32
+    if (!efreet_home_dir || efreet_home_dir[0] == '\0')
+        efreet_home_dir = getenv("USERPROFILE");
+#endif
+    if (!efreet_home_dir || efreet_home_dir[0] == '\0')
+        efreet_home_dir = "/tmp";
+
+    efreet_home_dir = eina_stringshare_add(efreet_home_dir);
+
+    return efreet_home_dir;
+}
+
+EAPI const char *
+efreet_desktop_dir_get(void)
+{
+    if (xdg_desktop_dir) return xdg_desktop_dir;
+    xdg_desktop_dir = efreet_user_dir_get("XDG_DESKTOP_DIR", _("Desktop"));
+    return xdg_desktop_dir;
+}
+
+EAPI const char *
+efreet_data_home_get(void)
+{
+    if (xdg_data_home) return xdg_data_home;
+    xdg_data_home = efreet_dir_get("XDG_DATA_HOME", "/.local/share");
+    return xdg_data_home;
+}
+
+EAPI Eina_List *
+efreet_data_dirs_get(void)
+{
+#ifdef _WIN32
+    char buf[4096];
+#endif
+
+    if (xdg_data_dirs) return xdg_data_dirs;
+
+#ifdef _WIN32
+    snprintf(buf, 4096, "%s\\Efl;" PACKAGE_DATA_DIR ";/usr/share;/usr/local/share", getenv("APPDATA"));
+    xdg_data_dirs = efreet_dirs_get("XDG_DATA_DIRS", buf);
+#else
+    xdg_data_dirs = efreet_dirs_get("XDG_DATA_DIRS",
+                            PACKAGE_DATA_DIR ":/usr/share:/usr/local/share");
+#endif
+    return xdg_data_dirs;
+}
+
+EAPI const char *
+efreet_config_home_get(void)
+{
+    if (xdg_config_home) return xdg_config_home;
+    xdg_config_home = efreet_dir_get("XDG_CONFIG_HOME", "/.config");
+    return xdg_config_home;
+}
+
+EAPI Eina_List *
+efreet_config_dirs_get(void)
+{
+    if (xdg_config_dirs) return xdg_config_dirs;
+    xdg_config_dirs = efreet_dirs_get("XDG_CONFIG_DIRS", "/etc/xdg");
+    return xdg_config_dirs;
+}
+
+EAPI const char *
+efreet_cache_home_get(void)
+{
+    if (xdg_cache_home) return xdg_cache_home;
+    xdg_cache_home = efreet_dir_get("XDG_CACHE_HOME", "/.cache");
+    return xdg_cache_home;
+}
+
+EAPI const char *
+efreet_hostname_get(void)
+{
+    char buf[256];
+
+    if (hostname) return hostname;
+    if (gethostname(buf, sizeof(buf)) < 0)
+        hostname = eina_stringshare_add("");
+    else
+        hostname = eina_stringshare_add(buf);
+    return hostname;
+}
+
+void
+efreet_dirs_reset(void)
+{
+    eina_stringshare_replace(&xdg_desktop_dir, NULL);
+}
+
+/**
+ * @internal
+ * @param key The environment key to lookup
+ * @param fallback The fallback value to use
+ * @return Returns the directory related to the given key or the fallback
+ * @brief This tries to determine the correct directory name given the
+ * environment key @a key and fallbacks @a fallback.
+ */
+static const char *
+efreet_dir_get(const char *key, const char *fallback)
+{
+    char *dir;
+    const char *t;
+
+    dir = getenv(key);
+    if (!dir || dir[0] == '\0')
+    {
+        int len;
+        const char *user;
+
+        user = efreet_home_dir_get();
+        len = strlen(user) + strlen(fallback) + 1;
+        dir = alloca(len);
+        snprintf(dir, len, "%s%s", user, fallback);
+
+        t = eina_stringshare_add(dir);
+    }
+    else t = eina_stringshare_add(dir);
+
+    return t;
+}
+
+/**
+ * @internal
+ * @param key The environment key to lookup
+ * @param fallback The fallback value to use
+ * @return Returns a list of directories specified by the given key @a key
+ * or from the list of fallbacks in @a fallback.
+ * @brief Creates a list of directories as given in the environment key @a
+ * key or from the fallbacks in @a fallback
+ */
+static Eina_List *
+efreet_dirs_get(const char *key, const char *fallback)
+{
+    Eina_List *dirs = NULL;
+    const char *path;
+    char *tmp, *s, *p;
+    char ts[PATH_MAX];
+    size_t len;
+
+    path = getenv(key);
+    if (!path || (path[0] == '\0')) path = fallback;
+
+    if (!path) return dirs;
+
+    len = strlen(path) + 1;
+    tmp = alloca(len);
+    memcpy(tmp, path, len);
+    s = tmp;
+    p = strchr(s, EFREET_PATH_SEP);
+    while (p)
+    {
+        *p = '\0';
+        if (!eina_list_search_unsorted(dirs, EINA_COMPARE_CB(strcmp), s))
+        {
+            // resolve path properly/fully to remove path//path2 to
+            // path/path2, path/./path2 to path/path2 etc.
+            if (realpath(s, ts))
+                dirs = eina_list_append(dirs, (void *)eina_stringshare_add(ts));
+        }
+
+        s = ++p;
+        p = strchr(s, EFREET_PATH_SEP);
+    }
+    if (!eina_list_search_unsorted(dirs, EINA_COMPARE_CB(strcmp), s))
+    {
+        // resolve path properly/fully to remove path//path2 to
+        // path/path2, path/./path2 to path/path2 etc.
+        if (realpath(s, ts))
+            dirs = eina_list_append(dirs, (void *)eina_stringshare_add(ts));
+    }
+
+    return dirs;
+}
+
+static const char *
+efreet_env_expand(const char *in)
+{
+   Eina_Strbuf *sb;
+   const char *ret, *p, *e1 = NULL, *e2 = NULL, *val;
+   char *env;
+
+   if (!in) return NULL;
+   sb = eina_strbuf_new();
+   if (!sb) return NULL;
+   
+   /* maximum length of any env var is the input string */
+   env = alloca(strlen(in) + 1);
+   for (p = in; *p; p++)
+     {
+        if (!e1)
+          {
+             if (*p == '$') e1 = p + 1;
+             else eina_strbuf_append_char(sb, *p);
+          }
+        else if (!(((*p >= 'a') && (*p <= 'z')) ||
+                   ((*p >= 'A') && (*p <= 'Z')) ||
+                   ((*p >= '0') && (*p <= '9')) ||
+                   (*p == '_')))
+          {
+             size_t len;
+             
+             e2 = p;
+             len = (size_t)(e2 - e1);
+             if (len > 0)
+               {
+                  memcpy(env, e1, len);
+                  env[len] = 0;
+                  val = getenv(env);
+                  if (val) eina_strbuf_append(sb, val);
+               }
+             e1 = NULL;
+             eina_strbuf_append_char(sb, *p);
+          }
+     }
+   ret = eina_stringshare_add(eina_strbuf_string_get(sb));
+   eina_strbuf_free(sb);
+   return ret;
+}
+
+/**
+ * @internal
+ * @param key The user-dirs key to lookup
+ * @param fallback The fallback value to use
+ * @return Returns the directory related to the given key or the fallback
+ * @brief This tries to determine the correct directory name given the
+ * user-dirs key @a key and fallbacks @a fallback.
+ */
+static const char *
+efreet_user_dir_get(const char *key, const char *fallback)
+{
+   Eina_File *file = NULL;
+   Eina_File_Line *line;
+   Eina_Iterator *it = NULL;
+   const char *config_home;
+   char path[PATH_MAX];
+   char *ret = NULL;
+
+   config_home = efreet_config_home_get();
+   snprintf(path, sizeof(path), "%s/user-dirs.dirs", config_home);
+
+   file = eina_file_open(path, EINA_FALSE);
+   if (!file) goto fallback;
+   it = eina_file_map_lines(file);
+   if (!it) goto fallback;
+   EINA_ITERATOR_FOREACH(it, line)
+     {
+        const char *eq, *end;
+
+        if (line->length < 3) continue;
+        if (line->start[0] == '#') continue;
+        if (strncmp(line->start, "XDG", 3)) continue;
+        eq = memchr(line->start, '=', line->length);
+        if (!eq) continue;
+        if (strncmp(key, line->start, eq - line->start)) continue;
+        if (++eq >= line->end) continue;
+        if (*eq != '"') continue;
+        if (++eq >= line->end) continue;
+        end = memchr(eq, '"', line->end - eq);
+        if (!end) continue;
+        ret = alloca(end - eq + 1);
+        memcpy(ret, eq, end - eq);
+        ret[end - eq] = '\0';
+        break;
+     }
+fallback:
+   if (it) eina_iterator_free(it);
+   if (file) eina_file_close(file);
+   if (!ret)
+     {
+        const char *home;
+        home = efreet_home_dir_get();
+        ret = alloca(strlen(home) + strlen(fallback) + 2);
+        sprintf(ret, "%s/%s", home, fallback);
+     }
+   return efreet_env_expand(ret);
+}
diff --git a/src/lib/efreet_base.h b/src/lib/efreet_base.h
new file mode 100644 (file)
index 0000000..0eb3d52
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef EFREET_BASE_H
+#define EFREET_BASE_H
+/**
+ * @file efreet_base.h
+ * @brief Contains the methods used to support the FDO base directory
+ * specification.
+ * @addtogroup Efreet_Base Efreet_Base: The XDG Base Directory Specification
+ * functions
+ *
+ * @{
+ */
+
+
+/**
+ * @return Returns the XDG Data Home directory
+ * @brief Retrieves the XDG Data Home directory
+ */
+EAPI const char *efreet_data_home_get(void);
+
+/**
+ * @return Returns the Eina_List of preference ordered extra data directories
+ * @brief Returns the Eina_List of preference ordered extra data directories
+ *
+ * @note The returned list is static inside Efreet. If you add/remove from the
+ * list then the next call to efreet_data_dirs_get() will return your
+ * modified values. DO NOT free this list.
+ */
+EAPI Eina_List *efreet_data_dirs_get(void);
+
+
+/**
+ * @return Returns the XDG Config Home directory
+ * @brief Retrieves the XDG Config Home directory
+ */
+EAPI const char *efreet_config_home_get(void);
+
+/**
+ * @return Returns the XDG Desktop directory
+ * @brief Retrieves the XDG Desktop directory
+ * @since 1.3
+ */
+EAPI const char *efreet_desktop_dir_get(void);
+
+/**
+ * @return Returns the Eina_List of preference ordered extra config directories
+ * @brief Returns the Eina_List of preference ordered extra config
+ * directories
+ *
+ * @note The returned list is static inside Efreet. If you add/remove from the
+ * list then the next call to efreet_config_dirs_get() will return your
+ * modified values. DO NOT free this list.
+ */
+EAPI Eina_List *efreet_config_dirs_get(void);
+
+
+/**
+ * @return Returns the XDG Cache Home directory
+ * @brief Retrieves the XDG Cache Home directory
+ */
+EAPI const char *efreet_cache_home_get(void);
+
+/**
+ * @return Returns the current hostname
+ * @brief Returns the current hostname or empty string if not found
+ */
+EAPI const char *efreet_hostname_get(void);
+
+/**
+ * Efreet_Event_Cache_Update
+ */
+typedef struct _Efreet_Event_Cache_Update Efreet_Event_Cache_Update;
+
+/**
+ * Efreet_Event_Cache_Update
+ * @brief event struct sent with EFREET_EVENT_*_CACHE_UPDATE
+ */
+struct _Efreet_Event_Cache_Update
+{
+    int dummy;
+};
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_cache.c b/src/lib/efreet_cache.c
new file mode 100644 (file)
index 0000000..a53c8e1
--- /dev/null
@@ -0,0 +1,1398 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* TODO: Consider flushing local icons cache after idling.
+ *       Icon requests will probably come in batches, f.ex. during menu
+ *       browsing.
+ */
+
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <Eet.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_cache_log_dom
+static int _efreet_cache_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_cache_private.h"
+
+#define NON_EXISTING (void *)-1
+
+typedef struct _Efreet_Old_Cache Efreet_Old_Cache;
+
+struct _Efreet_Old_Cache
+{
+    Eina_Hash *hash;
+    Eet_File *ef;
+};
+
+/**
+ * Data for cache files
+ */
+static Eet_Data_Descriptor *directory_edd = NULL;
+static Eet_Data_Descriptor *icon_theme_edd = NULL;
+static Eet_Data_Descriptor *icon_theme_directory_edd = NULL;
+
+static Eet_Data_Descriptor *icon_fallback_edd = NULL;
+static Eet_Data_Descriptor *icon_element_pointer_edd = NULL;
+static Eet_Data_Descriptor *icon_element_edd = NULL;
+static Eet_Data_Descriptor *icon_edd = NULL;
+
+static Eet_File            *icon_cache = NULL;
+static Eet_File            *fallback_cache = NULL;
+static Eet_File            *icon_theme_cache = NULL;
+
+static Eina_Hash           *themes = NULL;
+static Eina_Hash           *icons = NULL;
+static Eina_Hash           *fallbacks = NULL;
+
+static const char          *icon_theme_cache_file = NULL;
+
+static const char          *theme_name = NULL;
+
+static Eet_Data_Descriptor *version_edd = NULL;
+static Eet_Data_Descriptor *desktop_edd = NULL;
+static Eet_Data_Descriptor *hash_array_string_edd = NULL;
+static Eet_Data_Descriptor *array_string_edd = NULL;
+static Eet_Data_Descriptor *hash_string_edd = NULL;
+
+static Eina_Hash           *desktops = NULL;
+static Eina_List           *desktop_dirs_add = NULL;
+static Eet_File            *desktop_cache = NULL;
+static const char          *desktop_cache_file = NULL;
+
+static Ecore_File_Monitor  *cache_monitor = NULL;
+
+static Ecore_Event_Handler *cache_exe_handler = NULL;
+static Ecore_Timer         *icon_cache_timer = NULL;
+static Ecore_Exe           *icon_cache_exe = NULL;
+static int                  icon_cache_exe_lock = -1;
+static Ecore_Timer         *desktop_cache_timer = NULL;
+static Ecore_Exe           *desktop_cache_exe = NULL;
+static int                  desktop_cache_exe_lock = -1;
+
+static Eina_List           *old_desktop_caches = NULL;
+
+static const char                *util_cache_file = NULL;
+static Eet_File                  *util_cache = NULL;
+static Efreet_Cache_Hash         *util_cache_hash = NULL;
+static const char                *util_cache_hash_key = NULL;
+static Efreet_Cache_Array_String *util_cache_names = NULL;
+static const char                *util_cache_names_key = NULL;
+
+static void efreet_cache_edd_shutdown(void);
+static void efreet_cache_icon_free(Efreet_Cache_Icon *icon);
+static void efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon);
+static void efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme);
+
+static Eina_Bool efreet_cache_check(Eet_File **ef, const char *path, int major);
+static void *efreet_cache_close(Eet_File *ef);
+
+static Eina_Bool cache_exe_cb(void *data, int type, void *event);
+static Eina_Bool cache_check_change(const char *path);
+static void cache_update_cb(void *data, Ecore_File_Monitor *em,
+                            Ecore_File_Event event, const char *path);
+
+static Eina_Bool desktop_cache_update_cache_cb(void *data);
+static Eina_Bool icon_cache_update_cache_cb(void *data);
+static void desktop_cache_update_free(void *data, void *ev);
+static void icon_cache_update_free(void *data, void *ev);
+
+static void *hash_array_string_add(void *hash, const char *key, void *data);
+
+EAPI int EFREET_EVENT_ICON_CACHE_UPDATE = 0;
+EAPI int EFREET_EVENT_DESKTOP_CACHE_UPDATE = 0;
+EAPI int EFREET_EVENT_DESKTOP_CACHE_BUILD = 0;
+
+int
+efreet_cache_init(void)
+{
+    char buf[PATH_MAX];
+
+    _efreet_cache_log_dom = eina_log_domain_register("efreet_cache", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_cache_log_dom < 0)
+        return 0;
+
+    EFREET_EVENT_ICON_CACHE_UPDATE = ecore_event_type_new();
+    EFREET_EVENT_DESKTOP_CACHE_UPDATE = ecore_event_type_new();
+    EFREET_EVENT_DESKTOP_CACHE_BUILD = ecore_event_type_new();
+
+    themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free));
+    icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
+    fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free));
+    desktops = eina_hash_string_superfast_new(NULL);
+
+    if (efreet_cache_update)
+    {
+        snprintf(buf, sizeof(buf), "%s/efreet", efreet_cache_home_get());
+        if (!ecore_file_exists(buf))
+        {
+            if (!ecore_file_mkpath(buf))
+            {
+                ERR("Failed to create directory '%s'", buf);
+                goto error;
+            }
+            efreet_setowner(buf);
+        }
+
+        cache_exe_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
+                                                    cache_exe_cb, NULL);
+        if (!cache_exe_handler)
+        {
+            ERR("Failed to add exe del handler");
+            goto error;
+        }
+
+        cache_monitor = ecore_file_monitor_add(buf,
+                                               cache_update_cb,
+                                               NULL);
+        if (!cache_monitor)
+        {
+            ERR("Failed to set up ecore file monitor for '%s'", buf);
+            goto error;
+        }
+
+        efreet_cache_icon_update();
+        efreet_cache_desktop_update();
+    }
+
+    return 1;
+error:
+    if (themes) eina_hash_free(themes);
+    themes = NULL;
+    if (icons) eina_hash_free(icons);
+    icons = NULL;
+    if (fallbacks) eina_hash_free(fallbacks);
+    fallbacks = NULL;
+    if (desktops) eina_hash_free(desktops);
+    desktops = NULL;
+
+    if (cache_exe_handler) ecore_event_handler_del(cache_exe_handler);
+    cache_exe_handler = NULL;
+    if (cache_monitor) ecore_file_monitor_del(cache_monitor);
+    cache_monitor = NULL;
+    efreet_cache_edd_shutdown();
+    return 0;
+}
+
+void
+efreet_cache_shutdown(void)
+{
+    Efreet_Old_Cache *d;
+    void *data;
+
+    IF_RELEASE(theme_name);
+
+    icon_cache = efreet_cache_close(icon_cache);
+    icon_theme_cache = efreet_cache_close(icon_theme_cache);
+
+    IF_FREE_HASH(themes);
+    IF_FREE_HASH(icons);
+    IF_FREE_HASH(fallbacks);
+
+    IF_FREE_HASH_CB(desktops, EINA_FREE_CB(efreet_cache_desktop_free));
+    EINA_LIST_FREE(desktop_dirs_add, data)
+        eina_stringshare_del(data);
+    desktop_cache = efreet_cache_close(desktop_cache);
+    IF_RELEASE(desktop_cache_file);
+
+    if (cache_exe_handler) ecore_event_handler_del(cache_exe_handler);
+    cache_exe_handler = NULL;
+    if (cache_monitor) ecore_file_monitor_del(cache_monitor);
+    cache_monitor = NULL;
+
+    efreet_cache_edd_shutdown();
+    if (desktop_cache_timer)
+    {
+        ecore_timer_del(desktop_cache_timer);
+        desktop_cache_timer = NULL;
+    }
+    IF_RELEASE(icon_theme_cache_file);
+    if (icon_cache_exe_lock > 0)
+    {
+        close(icon_cache_exe_lock);
+        icon_cache_exe_lock = -1;
+    }
+
+    if (desktop_cache_exe_lock > 0)
+    {
+        close(desktop_cache_exe_lock);
+        desktop_cache_exe_lock = -1;
+    }
+
+    if (old_desktop_caches)
+        ERR("This application has not properly closed all its desktop references!");
+    EINA_LIST_FREE(old_desktop_caches, d)
+    {
+        eina_hash_free(d->hash);
+        eet_close(d->ef);
+        free(d);
+    }
+
+    IF_RELEASE(util_cache_names_key);
+    efreet_cache_array_string_free(util_cache_names);
+    util_cache_names = NULL;
+
+    IF_RELEASE(util_cache_hash_key);
+    if (util_cache_hash)
+    {
+        eina_hash_free(util_cache_hash->hash);
+        free(util_cache_hash);
+        util_cache_hash = NULL;
+    }
+
+    util_cache = efreet_cache_close(util_cache);
+    IF_RELEASE(util_cache_file);
+
+    eina_log_domain_unregister(_efreet_cache_log_dom);
+    _efreet_cache_log_dom = -1;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI const char *
+efreet_icon_cache_file(const char *theme)
+{
+    static char cache_file[PATH_MAX] = { '\0' };
+    const char *cache;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
+
+    cache = efreet_cache_home_get();
+
+    snprintf(cache_file, sizeof(cache_file), "%s/efreet/icons_%s_%s.eet", cache, theme, efreet_hostname_get());
+
+    return cache_file;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI const char *
+efreet_icon_theme_cache_file(void)
+{
+    char tmp[PATH_MAX] = { '\0' };
+
+    if (icon_theme_cache_file) return icon_theme_cache_file;
+
+    snprintf(tmp, sizeof(tmp), "%s/efreet/icon_themes_%s.eet",
+             efreet_cache_home_get(), efreet_hostname_get());
+    icon_theme_cache_file = eina_stringshare_add(tmp);
+
+    return icon_theme_cache_file;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI const char *
+efreet_desktop_util_cache_file(void)
+{
+    char tmp[PATH_MAX] = { '\0' };
+    const char *cache_dir, *lang, *country, *modifier;
+
+    if (util_cache_file) return util_cache_file;
+
+    cache_dir = efreet_cache_home_get();
+    lang = efreet_lang_get();
+    country = efreet_lang_country_get();
+    modifier = efreet_lang_modifier_get();
+
+    if (lang && country && modifier)
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s@%s.eet", cache_dir, efreet_hostname_get(), lang, country, modifier);
+    else if (lang && country)
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s.eet", cache_dir, efreet_hostname_get(), lang, country);
+    else if (lang)
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s.eet", cache_dir, efreet_hostname_get(), lang);
+    else
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s.eet", cache_dir, efreet_hostname_get());
+
+    util_cache_file = eina_stringshare_add(tmp);
+    return util_cache_file;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_version_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (version_edd) return version_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Version);
+    version_edd = eet_data_descriptor_file_new(&eddc);
+    if (!version_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version,
+                                  "minor", minor, EET_T_UCHAR);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version,
+                                  "major", major, EET_T_UCHAR);
+
+    return version_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_hash_array_string_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (hash_array_string_edd) return hash_array_string_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash);
+    eddc.func.hash_add = hash_array_string_add;
+    hash_array_string_edd = eet_data_descriptor_file_new(&eddc);
+    if (!hash_array_string_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_HASH(hash_array_string_edd, Efreet_Cache_Hash,
+                                  "hash", hash, efreet_array_string_edd());
+
+    return hash_array_string_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_hash_string_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (hash_string_edd) return hash_string_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash);
+    hash_string_edd = eet_data_descriptor_file_new(&eddc);
+    if (!hash_string_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_HASH_STRING(hash_string_edd, Efreet_Cache_Hash,
+                                  "hash", hash);
+
+    return hash_string_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_array_string_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (array_string_edd) return array_string_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Array_String);
+    array_string_edd = eet_data_descriptor_file_new(&eddc);
+    if (!array_string_edd) return NULL;
+    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(array_string_edd, Efreet_Cache_Array_String,
+                                             "array", array);
+
+    return array_string_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI const char *
+efreet_desktop_cache_file(void)
+{
+    char tmp[PATH_MAX] = { '\0' };
+    const char *cache, *lang, *country, *modifier;
+
+    if (desktop_cache_file) return desktop_cache_file;
+
+    cache = efreet_cache_home_get();
+    lang = efreet_lang_get();
+    country = efreet_lang_country_get();
+    modifier = efreet_lang_modifier_get();
+
+    if (lang && country && modifier)
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s@%s.eet", cache, efreet_hostname_get(), lang, country, modifier);
+    else if (lang && country)
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s.eet", cache, efreet_hostname_get(), lang, country);
+    else if (lang)
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s.eet", cache, efreet_hostname_get(), lang);
+    else
+        snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s.eet", cache, efreet_hostname_get());
+
+    desktop_cache_file = eina_stringshare_add(tmp);
+    return desktop_cache_file;
+}
+
+#define EDD_SHUTDOWN(Edd)                       \
+    if (Edd) eet_data_descriptor_free(Edd);       \
+Edd = NULL;
+
+static void
+efreet_cache_edd_shutdown(void)
+{
+    EDD_SHUTDOWN(version_edd);
+    EDD_SHUTDOWN(desktop_edd);
+    EDD_SHUTDOWN(hash_array_string_edd);
+    EDD_SHUTDOWN(array_string_edd);
+    EDD_SHUTDOWN(hash_string_edd);
+    EDD_SHUTDOWN(icon_theme_edd);
+    EDD_SHUTDOWN(icon_theme_directory_edd);
+    EDD_SHUTDOWN(directory_edd);
+    EDD_SHUTDOWN(icon_fallback_edd);
+    EDD_SHUTDOWN(icon_element_pointer_edd);
+    EDD_SHUTDOWN(icon_element_edd);
+    EDD_SHUTDOWN(icon_edd);
+}
+
+#define EFREET_POINTER_TYPE(Edd_Dest, Edd_Source, Type)   \
+{                                                                     \
+    typedef struct _Efreet_##Type##_Pointer Efreet_##Type##_Pointer;   \
+    struct _Efreet_##Type##_Pointer                                    \
+    {                                                                  \
+        Efreet_##Type *pointer;                                         \
+    };                                                                 \
+    \
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_##Type##_Pointer); \
+    Edd_Dest = eet_data_descriptor_file_new(&eddc);                    \
+    EET_DATA_DESCRIPTOR_ADD_SUB(Edd_Dest, Efreet_##Type##_Pointer,     \
+                                "pointer", pointer, Edd_Source);       \
+}
+
+static Eet_Data_Descriptor *
+efreet_icon_directory_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (directory_edd) return directory_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Directory);
+    directory_edd = eet_data_descriptor_file_new(&eddc);
+    if (!directory_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
+                                  "modified_time", modified_time, EET_T_LONG_LONG);
+
+    return directory_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_icon_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (icon_edd) return icon_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Element);
+    icon_element_edd = eet_data_descriptor_file_new(&eddc);
+    if (!icon_element_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
+                                  "type", type, EET_T_USHORT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
+                                  "normal", normal, EET_T_USHORT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
+                                  "normal", normal, EET_T_USHORT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
+                                  "min", min, EET_T_USHORT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
+                                  "max", max, EET_T_USHORT);
+    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_element_edd, Efreet_Cache_Icon_Element,
+                                             "paths", paths);
+
+    EFREET_POINTER_TYPE(icon_element_pointer_edd, icon_element_edd, Cache_Icon_Element);
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon);
+    icon_edd = eet_data_descriptor_file_new(&eddc);
+    if (!icon_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_edd, Efreet_Cache_Icon,
+                                  "theme", theme, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(icon_edd, Efreet_Cache_Icon,
+                                      "icons", icons, icon_element_pointer_edd);
+
+    return icon_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_icon_theme_edd(Eina_Bool cache)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (icon_theme_edd) return icon_theme_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Icon_Theme_Directory);
+    icon_theme_directory_edd = eet_data_descriptor_file_new(&eddc);
+    if (!icon_theme_directory_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "name", name, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "context", context, EET_T_UCHAR);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "type", type, EET_T_UCHAR);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "size.normal", size.normal, EET_T_UINT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "size.min", size.min, EET_T_UINT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "size.max", size.max, EET_T_UINT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
+                                  "size.threshold", size.threshold, EET_T_UINT);
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Theme);
+    icon_theme_edd = eet_data_descriptor_file_new(&eddc);
+    if (!icon_theme_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                  "name.internal", theme.name.internal, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                  "name.name", theme.name.name, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                  "comment", theme.comment, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                  "example_icon", theme.example_icon, EET_T_STRING);
+
+    eet_data_descriptor_element_add(icon_theme_edd, "paths", EET_T_STRING, EET_G_LIST,
+                                    offsetof(Efreet_Cache_Icon_Theme, theme.paths), 0, NULL, NULL);
+    eet_data_descriptor_element_add(icon_theme_edd, "inherits", EET_T_STRING, EET_G_LIST,
+                                    offsetof(Efreet_Cache_Icon_Theme, theme.inherits), 0, NULL, NULL);
+    EET_DATA_DESCRIPTOR_ADD_LIST(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                  "directories", theme.directories, icon_theme_directory_edd);
+
+    if (cache)
+    {
+        EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                      "last_cache_check", last_cache_check, EET_T_LONG_LONG);
+
+        EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                      "path", path, EET_T_STRING);
+
+        EET_DATA_DESCRIPTOR_ADD_HASH(icon_theme_edd, Efreet_Cache_Icon_Theme,
+                                     "dirs", dirs, efreet_icon_directory_edd());
+    }
+
+    return icon_theme_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_icon_fallback_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (icon_fallback_edd) return icon_fallback_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Fallback_Icon);
+    icon_fallback_edd = eet_data_descriptor_file_new(&eddc);
+    if (!icon_fallback_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_fallback_edd,
+                                             Efreet_Cache_Fallback_Icon, "icons", icons);
+
+    return icon_fallback_edd;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eet_Data_Descriptor *
+efreet_desktop_edd(void)
+{
+    Eet_Data_Descriptor_Class eddc;
+
+    if (desktop_edd) return desktop_edd;
+
+    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Desktop);
+    desktop_edd = eet_data_descriptor_file_new(&eddc);
+    if (!desktop_edd) return NULL;
+
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "type", desktop.type, EET_T_INT);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "version", desktop.version, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "orig_path", desktop.orig_path, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "load_time", desktop.load_time, EET_T_LONG_LONG);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "name", desktop.name, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "generic_name", desktop.generic_name, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "comment", desktop.comment, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "icon", desktop.icon, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "try_exec", desktop.try_exec, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "exec", desktop.exec, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "path", desktop.path, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_wm_class", desktop.startup_wm_class, EET_T_STRING);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "url", desktop.url, EET_T_STRING);
+    eet_data_descriptor_element_add(desktop_edd, "only_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.only_show_in), 0, NULL, NULL);
+    eet_data_descriptor_element_add(desktop_edd, "not_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.not_show_in), 0, NULL, NULL);
+    eet_data_descriptor_element_add(desktop_edd, "categories", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.categories), 0, NULL, NULL);
+    eet_data_descriptor_element_add(desktop_edd, "mime_types", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.mime_types), 0, NULL, NULL);
+    eet_data_descriptor_element_add(desktop_edd, "x", EET_T_STRING, EET_G_HASH, offsetof(Efreet_Cache_Desktop, desktop.x), 0, NULL, NULL);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "no_display", desktop.no_display, EET_T_UCHAR);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "hidden", desktop.hidden, EET_T_UCHAR);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "terminal", desktop.terminal, EET_T_UCHAR);
+    EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_notify", desktop.startup_notify, EET_T_UCHAR);
+
+    return desktop_edd;
+}
+
+Efreet_Cache_Icon *
+efreet_cache_icon_find(Efreet_Icon_Theme *theme, const char *icon)
+{
+    Efreet_Cache_Icon *cache = NULL;
+
+    if (theme_name && strcmp(theme_name, theme->name.internal))
+    {
+        /* FIXME: this is bad if people have pointer to this cache, things will go wrong */
+        INF("theme_name change from `%s` to `%s`", theme_name, theme->name.internal);
+        IF_RELEASE(theme_name);
+        icon_cache = efreet_cache_close(icon_cache);
+        eina_hash_free(icons);
+        icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
+    }
+
+    if (!efreet_cache_check(&icon_cache, efreet_icon_cache_file(theme->name.internal), EFREET_ICON_CACHE_MAJOR)) return NULL;
+    if (!theme_name)
+        theme_name = eina_stringshare_add(theme->name.internal);
+
+    cache = eina_hash_find(icons, icon);
+    if (cache == NON_EXISTING) return NULL;
+    if (cache) return cache;
+
+    cache = eet_data_read(icon_cache, efreet_icon_edd(), icon);
+    if (cache)
+        eina_hash_add(icons, icon, cache);
+    else
+        eina_hash_add(icons, icon, NON_EXISTING);
+    return cache;
+}
+
+Efreet_Cache_Fallback_Icon *
+efreet_cache_icon_fallback_find(const char *icon)
+{
+    Efreet_Cache_Fallback_Icon *cache;
+
+    if (!efreet_cache_check(&fallback_cache, efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EFREET_ICON_CACHE_MAJOR)) return NULL;
+
+    cache = eina_hash_find(fallbacks, icon);
+    if (cache == NON_EXISTING) return NULL;
+    if (cache) return cache;
+
+    cache = eet_data_read(fallback_cache, efreet_icon_fallback_edd(), icon);
+    if (cache)
+        eina_hash_add(fallbacks, icon, cache);
+    else
+        eina_hash_add(fallbacks, icon, NON_EXISTING);
+    return cache;
+}
+
+Efreet_Icon_Theme *
+efreet_cache_icon_theme_find(const char *theme)
+{
+    Efreet_Cache_Icon_Theme *cache;
+
+    if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL;
+
+    cache = eina_hash_find(themes, theme);
+    if (cache == NON_EXISTING) return NULL;
+    if (cache) return &(cache->theme);
+
+    cache = eet_data_read(icon_theme_cache, efreet_icon_theme_edd(EINA_FALSE), theme);
+    if (cache)
+    {
+        eina_hash_add(themes, theme, cache);
+        return &(cache->theme);
+    }
+    else
+        eina_hash_add(themes, theme, NON_EXISTING);
+    return NULL;
+}
+
+static void
+efreet_cache_icon_free(Efreet_Cache_Icon *icon)
+{
+    unsigned int i;
+
+    if (!icon) return;
+    if (icon == NON_EXISTING) return;
+
+    for (i = 0; i < icon->icons_count; ++i)
+    {
+        free(icon->icons[i]->paths);
+        free(icon->icons[i]);
+    }
+
+    free(icon->icons);
+    free(icon);
+}
+
+static void
+efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon)
+{
+    if (!icon) return;
+    if (icon == NON_EXISTING) return;
+
+    free(icon->icons);
+    free(icon);
+}
+
+static void
+efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme)
+{
+    void *data;
+
+    if (!theme) return;
+    if (theme == NON_EXISTING) return;
+
+    eina_list_free(theme->paths);
+    eina_list_free(theme->inherits);
+    EINA_LIST_FREE(theme->directories, data)
+        free(data);
+
+    free(theme);
+}
+
+Eina_List *
+efreet_cache_icon_theme_list(void)
+{
+    Eina_List *ret = NULL;
+    char **keys;
+    int i, num;
+
+    if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL;
+    keys = eet_list(icon_theme_cache, "*", &num);
+    for (i = 0; i < num; i++)
+    {
+        Efreet_Icon_Theme *theme;
+        if (!strncmp(keys[i], "__efreet", 8)) continue;
+
+        theme = eina_hash_find(themes, keys[i]);
+        if (!theme)
+            theme = efreet_cache_icon_theme_find(keys[i]);
+        if (theme && theme != NON_EXISTING)
+            ret = eina_list_append(ret, theme);
+    }
+    free(keys);
+    return ret;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI void
+efreet_cache_array_string_free(Efreet_Cache_Array_String *array)
+{
+    if (!array) return;
+    free(array->array);
+    free(array);
+}
+
+Efreet_Desktop *
+efreet_cache_desktop_find(const char *file)
+{
+    Efreet_Cache_Desktop *cache;
+    char rp[PATH_MAX];
+
+    if (!realpath(file, rp)) return NULL;
+
+    if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) return NULL;
+
+    cache = eina_hash_find(desktops, rp);
+    if (cache == NON_EXISTING) return NULL;
+    if (cache)
+    {
+        /* If less than one second since last stat, return desktop */
+        if ((ecore_time_get() - cache->check_time) < 1)
+        {
+            INF("Return without stat %f %f", ecore_time_get(), cache->check_time);
+            return &cache->desktop;
+        }
+        if (cache->desktop.load_time == ecore_file_mod_time(cache->desktop.orig_path))
+        {
+            INF("Return with stat %f %f", ecore_time_get(), cache->check_time);
+            cache->check_time = ecore_time_get();
+            return &cache->desktop;
+        }
+
+        /* We got stale data. The desktop will be free'd eventually as
+         * users will call efreet_desktop_free */
+        eina_hash_set(desktops, rp, NON_EXISTING);
+        cache = NULL;
+    }
+
+    cache = eet_data_read(desktop_cache, efreet_desktop_edd(), rp);
+    if (cache)
+    {
+        if (cache->desktop.load_time != ecore_file_mod_time(cache->desktop.orig_path))
+        {
+            /* Don't return stale data */
+            INF("We got stale data in the desktop cache");
+            efreet_cache_desktop_free(&cache->desktop);
+            eina_hash_set(desktops, rp, NON_EXISTING);
+        }
+        else
+        {
+            cache->desktop.eet = 1;
+            cache->check_time = ecore_time_get();
+            eina_hash_set(desktops, cache->desktop.orig_path, cache);
+            return &cache->desktop;
+        }
+    }
+    else
+        eina_hash_set(desktops, rp, NON_EXISTING);
+    return NULL;
+}
+
+void
+efreet_cache_desktop_free(Efreet_Desktop *desktop)
+{
+    Efreet_Old_Cache *d;
+    Efreet_Desktop *curr;
+    Eina_List *l;
+
+    if (!desktop ||
+        desktop == NON_EXISTING ||
+        !desktop->eet) return;
+
+    curr = eina_hash_find(desktops, desktop->orig_path);
+    if (curr == desktop)
+    {
+        INF("Found in current cache, purge\n");
+        eina_hash_del_by_key(desktops, desktop->orig_path);
+    }
+
+    EINA_LIST_FOREACH(old_desktop_caches, l, d)
+    {
+        curr = eina_hash_find(d->hash, desktop->orig_path);
+        if (curr == desktop)
+        {
+            INF("Found in old cache, purge\n");
+            eina_hash_del_by_key(d->hash, desktop->orig_path);
+            if (eina_hash_population(d->hash) == 0)
+            {
+                INF("Cache empty, close file\n");
+                eina_hash_free(d->hash);
+                eet_close(d->ef);
+                free(d);
+                old_desktop_caches = eina_list_remove_list(old_desktop_caches, l);
+            }
+            break;
+        }
+    }
+
+    eina_list_free(desktop->only_show_in);
+    eina_list_free(desktop->not_show_in);
+    eina_list_free(desktop->categories);
+    eina_list_free(desktop->mime_types);
+    IF_FREE_HASH(desktop->x);
+    free(desktop);
+}
+
+void
+efreet_cache_desktop_add(Efreet_Desktop *desktop)
+{
+    char buf[PATH_MAX];
+    char *dir;
+    Efreet_Cache_Array_String *arr;
+
+    /*
+     * Read file from disk, save path in cache so it will be included in next
+     * cache update
+     */
+    strncpy(buf, desktop->orig_path, PATH_MAX);
+    buf[PATH_MAX - 1] = '\0';
+    dir = dirname(buf);
+    arr = efreet_cache_desktop_dirs();
+    if (arr)
+    {
+        unsigned int i;
+
+        for (i = 0; i < arr->array_count; i++)
+        {
+            /* Check if we already have this dir in cache */
+            if (!strcmp(dir, arr->array[i]))
+                return;
+        }
+        efreet_cache_array_string_free(arr);
+    }
+    if (!eina_list_search_unsorted_list(desktop_dirs_add, EINA_COMPARE_CB(strcmp), dir))
+        desktop_dirs_add = eina_list_append(desktop_dirs_add, eina_stringshare_add(dir));
+
+    efreet_cache_desktop_update();
+}
+
+Efreet_Cache_Array_String *
+efreet_cache_desktop_dirs(void)
+{
+    if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) return NULL;
+
+    return eet_data_read(desktop_cache, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS);
+}
+
+void
+efreet_cache_desktop_update(void)
+{
+    if (!efreet_cache_update) return;
+
+    if (desktop_cache_timer)
+        ecore_timer_delay(desktop_cache_timer, 0.2);
+    else
+        desktop_cache_timer = ecore_timer_add(0.2, desktop_cache_update_cache_cb, NULL);
+}
+
+void
+efreet_cache_desktop_close(void)
+{
+    IF_RELEASE(util_cache_names_key);
+    IF_RELEASE(util_cache_hash_key);
+
+    if ((desktop_cache) && (desktop_cache != NON_EXISTING))
+    {
+        Efreet_Old_Cache *d = NEW(Efreet_Old_Cache, 1);
+        if (d)
+        {
+            d->hash = desktops;
+            d->ef = desktop_cache;
+            old_desktop_caches = eina_list_append(old_desktop_caches, d);
+        }
+
+        desktops = eina_hash_string_superfast_new(NULL);
+    }
+    desktop_cache = NULL;
+
+    efreet_cache_array_string_free(util_cache_names);
+    util_cache_names = NULL;
+
+    if (util_cache_hash)
+    {
+        eina_hash_free(util_cache_hash->hash);
+        free(util_cache_hash);
+        util_cache_hash = NULL;
+    }
+
+    util_cache = efreet_cache_close(util_cache);
+
+    IF_RELEASE(desktop_cache_file);
+    IF_RELEASE(util_cache_file);
+}
+
+void
+efreet_cache_icon_update(void)
+{
+    if (!efreet_cache_update) return;
+
+    if (icon_cache_timer)
+        ecore_timer_delay(icon_cache_timer, 0.2);
+    else
+        icon_cache_timer = ecore_timer_add(0.2, icon_cache_update_cache_cb, NULL);
+}
+
+static Eina_Bool
+efreet_cache_check(Eet_File **ef, const char *path, int major)
+{
+    Efreet_Cache_Version *version;
+
+    if (*ef == NON_EXISTING) return EINA_FALSE;
+    if (*ef) return EINA_TRUE;
+    if (!*ef)
+        *ef = eet_open(path, EET_FILE_MODE_READ);
+    if (!*ef)
+    {
+        *ef = NON_EXISTING;
+        return EINA_FALSE;
+    }
+
+    version = eet_data_read(*ef, efreet_version_edd(), EFREET_CACHE_VERSION);
+    if ((!version) || (version->major != major))
+    {
+        IF_FREE(version);
+        eet_close(*ef);
+        *ef = NON_EXISTING;
+        return EINA_FALSE;
+    }
+    free(version);
+    return EINA_TRUE;
+}
+
+static void *
+efreet_cache_close(Eet_File *ef)
+{
+    if (ef && ef != NON_EXISTING)
+        eet_close(ef);
+    return NULL;
+}
+
+Efreet_Cache_Hash *
+efreet_cache_util_hash_string(const char *key)
+{
+    if (util_cache_hash_key && !strcmp(key, util_cache_hash_key))
+        return util_cache_hash;
+    if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
+
+    if (util_cache_hash)
+    {
+        /* free previous util_cache */
+        IF_RELEASE(util_cache_hash_key);
+        eina_hash_free(util_cache_hash->hash);
+        free(util_cache_hash);
+    }
+    util_cache_hash_key = eina_stringshare_add(key);
+    util_cache_hash = eet_data_read(util_cache, efreet_hash_string_edd(), key);
+    return util_cache_hash;
+}
+
+Efreet_Cache_Hash *
+efreet_cache_util_hash_array_string(const char *key)
+{
+    if (util_cache_hash_key && !strcmp(key, util_cache_hash_key))
+        return util_cache_hash;
+    if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
+
+    IF_RELEASE(util_cache_hash_key);
+    if (util_cache_hash)
+    {
+        /* free previous cache */
+        eina_hash_free(util_cache_hash->hash);
+        free(util_cache_hash);
+    }
+    util_cache_hash_key = eina_stringshare_add(key);
+    util_cache_hash = eet_data_read(util_cache, efreet_hash_array_string_edd(), key);
+    return util_cache_hash;
+}
+
+Efreet_Cache_Array_String *
+efreet_cache_util_names(const char *key)
+{
+    if (util_cache_names_key && !strcmp(key, util_cache_names_key))
+        return util_cache_names;
+    if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
+
+    if (util_cache_names)
+    {
+        /* free previous util_cache */
+        IF_RELEASE(util_cache_names_key);
+        efreet_cache_array_string_free(util_cache_names);
+    }
+    util_cache_names_key = eina_stringshare_add(key);
+    util_cache_names = eet_data_read(util_cache, efreet_array_string_edd(), key);
+    return util_cache_names;
+}
+
+static Eina_Bool
+cache_exe_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
+{
+    Ecore_Exe_Event_Del *ev;
+
+    ev = event;
+    if (ev->exe == desktop_cache_exe)
+    {
+        if (desktop_cache_exe_lock > 0)
+        {
+            close(desktop_cache_exe_lock);
+            desktop_cache_exe_lock = -1;
+        }
+        desktop_cache_exe = NULL;
+    }
+    else if (ev->exe == icon_cache_exe)
+    {
+        if (icon_cache_exe_lock > 0)
+        {
+            close(icon_cache_exe_lock);
+            icon_cache_exe_lock = -1;
+        }
+        icon_cache_exe = NULL;
+    }
+    return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+cache_check_change(const char *path)
+{
+    const char *data;
+    Eina_Bool changed = EINA_TRUE;
+    Eina_File *f;
+
+    f = eina_file_open(path, EINA_FALSE);
+    if (!f) return EINA_TRUE;
+    if (eina_file_size_get(f) < 1) return EINA_TRUE;
+    data = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
+    if (*data == 'n') changed = EINA_FALSE;
+    eina_file_close(f);
+    return changed;
+}
+
+static void
+cache_update_cb(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__,
+                Ecore_File_Event event, const char *path)
+{
+    const char *file;
+    Efreet_Event_Cache_Update *ev = NULL;
+    Efreet_Old_Cache *d = NULL;
+    Eina_List *l = NULL;
+
+    if (event != ECORE_FILE_EVENT_CLOSED)
+        return;
+
+    file = ecore_file_file_get(path);
+    if (!file) return;
+    if (!strcmp(file, "desktop_data.update"))
+    {
+        if (cache_check_change(path))
+        {
+            ev = NEW(Efreet_Event_Cache_Update, 1);
+            if (!ev) goto error;
+
+            efreet_cache_desktop_close();
+
+            ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_UPDATE, ev, desktop_cache_update_free, d);
+        }
+        ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_BUILD, NULL, NULL, NULL);
+        /* TODO: Check if desktop_dirs_add exists, and rebuild cache if */
+    }
+    else if (!strcmp(file, "icon_data.update"))
+    {
+        if (cache_check_change(path))
+        {
+            ev = NEW(Efreet_Event_Cache_Update, 1);
+            if (!ev) goto error;
+
+            IF_RELEASE(theme_name);
+
+            /* Save all old caches */
+            d = NEW(Efreet_Old_Cache, 1);
+            if (!d) goto error;
+            d->hash = themes;
+            d->ef = icon_theme_cache;
+            l = eina_list_append(l, d);
+
+            d = NEW(Efreet_Old_Cache, 1);
+            if (!d) goto error;
+            d->hash = icons;
+            d->ef = icon_cache;
+            l = eina_list_append(l, d);
+
+            d = NEW(Efreet_Old_Cache, 1);
+            if (!d) goto error;
+            d->hash = fallbacks;
+            d->ef = fallback_cache;
+            l = eina_list_append(l, d);
+
+            /* Create new empty caches */
+            themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free));
+            icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
+            fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free));
+
+            icon_theme_cache = NULL;
+            icon_cache = NULL;
+            fallback_cache = NULL;
+
+            /* Send event */
+            ecore_event_add(EFREET_EVENT_ICON_CACHE_UPDATE, ev, icon_cache_update_free, l);
+        }
+    }
+    return;
+error:
+    IF_FREE(ev);
+    IF_FREE(d);
+    EINA_LIST_FREE(l, d)
+        free(d);
+}
+
+static Eina_Bool
+desktop_cache_update_cache_cb(void *data __UNUSED__)
+{
+    char file[PATH_MAX];
+    struct flock fl;
+    int prio;
+
+    desktop_cache_timer = NULL;
+
+    /* TODO: Retry update cache later */
+    if (desktop_cache_exe_lock > 0) return ECORE_CALLBACK_CANCEL;
+
+    snprintf(file, sizeof(file), "%s/efreet/desktop_exec.lock", efreet_cache_home_get());
+
+    desktop_cache_exe_lock = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+    if (desktop_cache_exe_lock < 0) goto error;
+    efreet_fsetowner(desktop_cache_exe_lock);
+    memset(&fl, 0, sizeof(struct flock));
+    fl.l_type = F_WRLCK;
+    fl.l_whence = SEEK_SET;
+    if (fcntl(desktop_cache_exe_lock, F_SETLK, &fl) < 0) goto error;
+    prio = ecore_exe_run_priority_get();
+    ecore_exe_run_priority_set(19);
+    eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_desktop_cache_create", sizeof(file));
+    if (desktop_dirs_add)
+    {
+        const char *str;
+
+        eina_strlcat(file, " -d", sizeof(file));
+        EINA_LIST_FREE(desktop_dirs_add, str)
+        {
+            eina_strlcat(file, " ", sizeof(file));
+            eina_strlcat(file, str, sizeof(file));
+            eina_stringshare_del(str);
+        }
+    }
+    INF("Run desktop cache creation: %s", file);
+    desktop_cache_exe = ecore_exe_run(file, NULL);
+    ecore_exe_run_priority_set(prio);
+    if (!desktop_cache_exe) goto error;
+
+    return ECORE_CALLBACK_CANCEL;
+error:
+    if (desktop_cache_exe_lock > 0)
+    {
+        close(desktop_cache_exe_lock);
+        desktop_cache_exe_lock = -1;
+    }
+    return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+icon_cache_update_cache_cb(void *data __UNUSED__)
+{
+    char file[PATH_MAX];
+    struct flock fl;
+    int prio;
+    Eina_List **l, *l2;
+
+    icon_cache_timer = NULL;
+
+    /* TODO: Retry update cache later */
+    if (icon_cache_exe_lock > 0) return ECORE_CALLBACK_CANCEL;
+
+    snprintf(file, sizeof(file), "%s/efreet/icon_exec.lock", efreet_cache_home_get());
+
+    icon_cache_exe_lock = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+    if (icon_cache_exe_lock < 0) goto error;
+    efreet_fsetowner(icon_cache_exe_lock);
+    memset(&fl, 0, sizeof(struct flock));
+    fl.l_type = F_WRLCK;
+    fl.l_whence = SEEK_SET;
+    if (fcntl(icon_cache_exe_lock, F_SETLK, &fl) < 0) goto error;
+    prio = ecore_exe_run_priority_get();
+    ecore_exe_run_priority_set(19);
+    eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_icon_cache_create", sizeof(file));
+    l = efreet_icon_extra_list_get();
+    if (l && eina_list_count(*l) > 0)
+    {
+        Eina_List *ll;
+        char *p;
+
+        eina_strlcat(file, " -d", sizeof(file));
+        EINA_LIST_FOREACH(*l, ll, p)
+        {
+            eina_strlcat(file, " ", sizeof(file));
+            eina_strlcat(file, p, sizeof(file));
+        }
+    }
+    l2 = efreet_icon_extensions_list_get();
+    if (eina_list_count(l2) > 0)
+    {
+        Eina_List *ll;
+        char *p;
+
+        eina_strlcat(file, " -e", sizeof(file));
+        EINA_LIST_FOREACH(l2, ll, p)
+        {
+            eina_strlcat(file, " ", sizeof(file));
+            eina_strlcat(file, p, sizeof(file));
+        }
+    }
+    icon_cache_exe = ecore_exe_run(file, NULL);
+    ecore_exe_run_priority_set(prio);
+    if (!icon_cache_exe) goto error;
+
+    return ECORE_CALLBACK_CANCEL;
+
+error:
+    if (icon_cache_exe_lock > 0)
+    {
+        close(icon_cache_exe_lock);
+        icon_cache_exe_lock = -1;
+    }
+    return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+desktop_cache_update_free(void *data, void *ev)
+{
+    Efreet_Old_Cache *d;
+    int dangling = 0;
+
+    d = data;
+    if (d && (eina_list_data_find(old_desktop_caches, d) == d))
+    {
+        /*
+         * All users should now had the chance to update their pointers.
+         * Check whether we still have some dangling and print a warning.
+         * Programs might close their pointers later.
+         */
+        if (d->hash)
+        {
+            Eina_Iterator *it;
+            Eina_Hash_Tuple *tuple;
+
+            it = eina_hash_iterator_tuple_new(d->hash);
+            EINA_ITERATOR_FOREACH(it, tuple)
+            {
+                if (tuple->data == NON_EXISTING) continue;
+                WRN("%d:%s still in cache after update event!",
+                    ((Efreet_Desktop *)tuple->data)->ref, (char *)tuple->key);
+                dangling++;
+            }
+            eina_iterator_free(it);
+        }
+        if (dangling != 0)
+        {
+            WRN("There are still %i desktop files with old\n"
+                "dangling references to desktop files. This application\n"
+                "has not handled the EFREET_EVENT_DESKTOP_CACHE_UPDATE\n"
+                "fully and released its references. Please fix the application\n"
+                "so it does this.",
+                dangling);
+        }
+    }
+    free(ev);
+}
+
+static void
+icon_cache_update_free(void *data, void *ev)
+{
+    Efreet_Old_Cache *d;
+    Eina_List *l;
+
+    l = data;
+    EINA_LIST_FREE(l, d)
+    {
+        if (d->hash)
+            eina_hash_free(d->hash);
+        efreet_cache_close(d->ef);
+        free(d);
+    }
+    free(ev);
+}
+
+static void *
+hash_array_string_add(void *hash, const char *key, void *data)
+{
+    if (!hash)
+        hash = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
+    if (!hash)
+        return NULL;
+    eina_hash_add(hash, key, data);
+    return hash;
+}
diff --git a/src/lib/efreet_cache_private.h b/src/lib/efreet_cache_private.h
new file mode 100644 (file)
index 0000000..e281e61
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef EFREET_CACHE_PRIVATE_H
+#define EFREET_CACHE_PRIVATE_H
+
+#define EFREET_DESKTOP_CACHE_MAJOR 1
+#define EFREET_DESKTOP_CACHE_MINOR 0
+#define EFREET_DESKTOP_UTILS_CACHE_MAJOR 1
+#define EFREET_DESKTOP_UTILS_CACHE_MINOR 0
+
+#define EFREET_ICON_CACHE_MAJOR 1
+#define EFREET_ICON_CACHE_MINOR 0
+
+#define EFREET_CACHE_VERSION "__efreet//version"
+#define EFREET_CACHE_ICON_FALLBACK "__efreet_fallback"
+#define EFREET_CACHE_ICON_EXTENSIONS "__efreet//icon_extensions"
+#define EFREET_CACHE_ICON_EXTRA_DIRS "__efreet//icon_extra_dirs"
+#define EFREET_CACHE_DESKTOP_DIRS "__efreet//desktop_dirs"
+
+EAPI const char *efreet_desktop_util_cache_file(void);
+EAPI const char *efreet_desktop_cache_file(void);
+EAPI const char *efreet_icon_cache_file(const char *theme);
+EAPI const char *efreet_icon_theme_cache_file(void);
+
+EAPI Eet_Data_Descriptor *efreet_version_edd(void);
+EAPI Eet_Data_Descriptor *efreet_desktop_edd(void);
+EAPI Eet_Data_Descriptor *efreet_hash_array_string_edd(void);
+EAPI Eet_Data_Descriptor *efreet_hash_string_edd(void);
+EAPI Eet_Data_Descriptor *efreet_array_string_edd(void);
+EAPI Eet_Data_Descriptor *efreet_icon_theme_edd(Eina_Bool cache);
+EAPI Eet_Data_Descriptor *efreet_icon_edd(void);
+EAPI Eet_Data_Descriptor *efreet_icon_fallback_edd(void);
+
+typedef struct _Efreet_Cache_Icon_Theme Efreet_Cache_Icon_Theme;
+typedef struct _Efreet_Cache_Directory Efreet_Cache_Directory;
+typedef struct _Efreet_Cache_Desktop Efreet_Cache_Desktop;
+
+struct _Efreet_Cache_Icon_Theme
+{
+    Efreet_Icon_Theme theme;
+
+    long long last_cache_check; /**< Last time the cache was checked */
+
+    Eina_Hash *dirs;            /**< All possible icon paths for this theme */
+
+    const char *path;           /**< path to index.theme */
+
+    Eina_Bool hidden:1;         /**< Should this theme be hidden from users */
+    Eina_Bool valid:1;          /**< Have we seen an index for this theme */
+    Eina_Bool changed:1;        /**< Changed since last seen */
+};
+
+struct _Efreet_Cache_Directory
+{
+    long long modified_time;
+};
+
+struct _Efreet_Cache_Desktop
+{
+    Efreet_Desktop desktop;
+
+    double check_time; /**< Last time we check for disk modification */
+};
+
+#endif
diff --git a/src/lib/efreet_desktop.c b/src/lib/efreet_desktop.c
new file mode 100644 (file)
index 0000000..9293f94
--- /dev/null
@@ -0,0 +1,1085 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_desktop_log_dom
+int _efreet_desktop_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+#define DESKTOP_VERSION "1.0"
+
+/**
+ * The current desktop environment (e.g. "Enlightenment" or "Gnome")
+ */
+static const char *desktop_environment = NULL;
+
+/**
+ * A list of the desktop types available
+ */
+static Eina_List *efreet_desktop_types = NULL;
+
+static Eina_Hash *change_monitors = NULL;
+
+EAPI int EFREET_DESKTOP_TYPE_APPLICATION = 0;
+EAPI int EFREET_DESKTOP_TYPE_LINK = 0;
+EAPI int EFREET_DESKTOP_TYPE_DIRECTORY = 0;
+
+/**
+ * @internal
+ * Information about custom types
+ */
+typedef struct Efreet_Desktop_Type_Info Efreet_Desktop_Type_Info;
+struct Efreet_Desktop_Type_Info
+{
+    int id;
+    const char *type;
+    Efreet_Desktop_Type_Parse_Cb parse_func;
+    Efreet_Desktop_Type_Save_Cb save_func;
+    Efreet_Desktop_Type_Free_Cb free_func;
+};
+
+static int efreet_desktop_read(Efreet_Desktop *desktop);
+static Efreet_Desktop_Type_Info *efreet_desktop_type_parse(const char *type_str);
+static void efreet_desktop_type_info_free(Efreet_Desktop_Type_Info *info);
+static void *efreet_desktop_application_fields_parse(Efreet_Desktop *desktop,
+                                                    Efreet_Ini *ini);
+static void efreet_desktop_application_fields_save(Efreet_Desktop *desktop,
+                                                    Efreet_Ini *ini);
+static void *efreet_desktop_link_fields_parse(Efreet_Desktop *desktop,
+                                                Efreet_Ini *ini);
+static void efreet_desktop_link_fields_save(Efreet_Desktop *desktop,
+                                                Efreet_Ini *ini);
+static int efreet_desktop_generic_fields_parse(Efreet_Desktop *desktop,
+                                                Efreet_Ini *ini);
+static void efreet_desktop_generic_fields_save(Efreet_Desktop *desktop,
+                                                Efreet_Ini *ini);
+static Eina_Bool efreet_desktop_x_fields_parse(const Eina_Hash *hash,
+                                                const void *key,
+                                                void *data,
+                                                void *fdata);
+static Eina_Bool efreet_desktop_x_fields_save(const Eina_Hash *hash,
+                                                const void *key,
+                                                void *value,
+                                                void *fdata);
+static int efreet_desktop_environment_check(Efreet_Desktop *desktop);
+
+static void efreet_desktop_changes_listen(void);
+static void efreet_desktop_changes_listen_recursive(const char *path);
+static void efreet_desktop_changes_monitor_add(const char *path);
+static void efreet_desktop_changes_cb(void *data, Ecore_File_Monitor *em,
+                                             Ecore_File_Event event, const char *path);
+
+/**
+ * @internal
+ * @return Returns > 0 on success or 0 on failure
+ * @brief Initialize the Desktop parser subsystem
+ */
+int
+efreet_desktop_init(void)
+{
+    _efreet_desktop_log_dom = eina_log_domain_register
+      ("efreet_desktop", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_desktop_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_desktop");
+        return 0;
+    }
+
+#ifdef HAVE_EVIL
+    if (!evil_sockets_init())
+      {
+         ERR("Could not initialize Winsock system");
+         return 0;
+      }
+#endif
+
+    efreet_desktop_types = NULL;
+
+    EFREET_DESKTOP_TYPE_APPLICATION = efreet_desktop_type_add("Application",
+                                        efreet_desktop_application_fields_parse,
+                                        efreet_desktop_application_fields_save,
+                                        NULL);
+    EFREET_DESKTOP_TYPE_LINK = efreet_desktop_type_add("Link",
+                                    efreet_desktop_link_fields_parse,
+                                    efreet_desktop_link_fields_save, NULL);
+    EFREET_DESKTOP_TYPE_DIRECTORY = efreet_desktop_type_add("Directory", NULL,
+                                                                NULL, NULL);
+
+    efreet_desktop_changes_listen();
+    return 1;
+}
+
+/**
+ * @internal
+ * @returns the number of initializations left for this system
+ * @brief Attempts to shut down the subsystem if nothing else is using it
+ */
+void
+efreet_desktop_shutdown(void)
+{
+    Efreet_Desktop_Type_Info *info;
+
+    IF_RELEASE(desktop_environment);
+    EINA_LIST_FREE(efreet_desktop_types, info)
+        efreet_desktop_type_info_free(info);
+    IF_FREE_HASH(change_monitors);
+#ifdef HAVE_EVIL
+    evil_sockets_shutdown();
+#endif
+    eina_log_domain_unregister(_efreet_desktop_log_dom);
+    _efreet_desktop_log_dom = -1;
+}
+
+EAPI Efreet_Desktop *
+efreet_desktop_get(const char *file)
+{
+    Efreet_Desktop *desktop;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+    desktop = efreet_desktop_new(file);
+    if (!desktop) return NULL;
+
+    /* If we didn't find this file in the eet cache, add path to search path */
+    if (!desktop->eet)
+    {
+        /* Check whether the desktop type is a system type,
+         * and therefor known by the cache builder */
+        Efreet_Desktop_Type_Info *info;
+
+        info = eina_list_nth(efreet_desktop_types, desktop->type);
+        if (info && (
+                info->id == EFREET_DESKTOP_TYPE_APPLICATION ||
+                info->id == EFREET_DESKTOP_TYPE_LINK ||
+                info->id == EFREET_DESKTOP_TYPE_DIRECTORY
+                ))
+            efreet_cache_desktop_add(desktop);
+    }
+
+    return desktop;
+}
+
+EAPI int
+efreet_desktop_ref(Efreet_Desktop *desktop)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+    desktop->ref++;
+    return desktop->ref;
+}
+
+EAPI Efreet_Desktop *
+efreet_desktop_empty_new(const char *file)
+{
+    Efreet_Desktop *desktop;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+    desktop = NEW(Efreet_Desktop, 1);
+    if (!desktop) return NULL;
+
+    desktop->orig_path = strdup(file);
+    desktop->load_time = ecore_file_mod_time(file);
+
+    desktop->ref = 1;
+
+    return desktop;
+}
+
+EAPI Efreet_Desktop *
+efreet_desktop_new(const char *file)
+{
+    Efreet_Desktop *desktop = NULL;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+    desktop = efreet_cache_desktop_find(file);
+    if (desktop)
+    {
+        desktop->ref++;
+        if (!efreet_desktop_environment_check(desktop))
+        {
+            efreet_desktop_free(desktop);
+            return NULL;
+        }
+        return desktop;
+        efreet_desktop_free(desktop);
+    }
+    return efreet_desktop_uncached_new(file);
+}
+
+EAPI Efreet_Desktop *
+efreet_desktop_uncached_new(const char *file)
+{
+    Efreet_Desktop *desktop = NULL;
+    char rp[PATH_MAX];
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+    if (!realpath(file, rp)) return NULL;
+    if (!ecore_file_exists(rp)) return NULL;
+
+    desktop = NEW(Efreet_Desktop, 1);
+    if (!desktop) return NULL;
+    desktop->orig_path = strdup(rp);
+    desktop->ref = 1;
+    if (!efreet_desktop_read(desktop))
+    {
+        efreet_desktop_free(desktop);
+        return NULL;
+    }
+
+    return desktop;
+}
+
+EAPI int
+efreet_desktop_save(Efreet_Desktop *desktop)
+{
+    Efreet_Desktop_Type_Info *info;
+    Efreet_Ini *ini;
+    int ok = 1;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+
+    ini = efreet_ini_new(NULL);
+    if (!ini) return 0;
+    efreet_ini_section_add(ini, "Desktop Entry");
+    efreet_ini_section_set(ini, "Desktop Entry");
+
+    info = eina_list_nth(efreet_desktop_types, desktop->type);
+    if (info)
+    {
+        efreet_ini_string_set(ini, "Type", info->type);
+        if (info->save_func) info->save_func(desktop, ini);
+    }
+    else
+        ok = 0;
+
+    if (ok)
+    {
+        char *val;
+
+        if (desktop->only_show_in)
+        {
+            val = efreet_desktop_string_list_join(desktop->only_show_in);
+            if (val)
+            {
+                efreet_ini_string_set(ini, "OnlyShowIn", val);
+                FREE(val);
+            }
+        }
+        if (desktop->not_show_in)
+        {
+            val = efreet_desktop_string_list_join(desktop->not_show_in);
+            if (val)
+            {
+                efreet_ini_string_set(ini, "NotShowIn", val);
+                FREE(val);
+            }
+        }
+        efreet_desktop_generic_fields_save(desktop, ini);
+        /* When we save the file, it should be updated to the
+         * latest version that we support! */
+        efreet_ini_string_set(ini, "Version", DESKTOP_VERSION);
+
+        if (!efreet_ini_save(ini, desktop->orig_path)) ok = 0;
+    }
+    efreet_ini_free(ini);
+    return ok;
+}
+
+EAPI int
+efreet_desktop_save_as(Efreet_Desktop *desktop, const char *file)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
+
+    /* If we save data from eet as new, we will be in trouble */
+    if (desktop->eet) return 0;
+
+    IF_FREE(desktop->orig_path);
+    desktop->orig_path = strdup(file);
+    return efreet_desktop_save(desktop);
+}
+
+EAPI void
+efreet_desktop_free(Efreet_Desktop *desktop)
+{
+    if (!desktop) return;
+
+    desktop->ref--;
+    if (desktop->ref > 0) return;
+
+    if (desktop->eet)
+    {
+        efreet_cache_desktop_free(desktop);
+    }
+    else
+    {
+        IF_FREE(desktop->orig_path);
+
+        IF_FREE(desktop->version);
+        IF_FREE(desktop->name);
+        IF_FREE(desktop->generic_name);
+        IF_FREE(desktop->comment);
+        IF_FREE(desktop->icon);
+        IF_FREE(desktop->url);
+
+        IF_FREE(desktop->try_exec);
+        IF_FREE(desktop->exec);
+        IF_FREE(desktop->path);
+        IF_FREE(desktop->startup_wm_class);
+
+        IF_FREE_LIST(desktop->only_show_in, eina_stringshare_del);
+        IF_FREE_LIST(desktop->not_show_in, eina_stringshare_del);
+
+        IF_FREE_LIST(desktop->categories, eina_stringshare_del);
+        IF_FREE_LIST(desktop->mime_types, eina_stringshare_del);
+
+        IF_FREE_HASH(desktop->x);
+
+        if (desktop->type_data)
+        {
+            Efreet_Desktop_Type_Info *info;
+            info = eina_list_nth(efreet_desktop_types, desktop->type);
+            if (info->free_func)
+                info->free_func(desktop->type_data);
+        }
+        free(desktop);
+    }
+}
+
+EAPI void
+efreet_desktop_environment_set(const char *environment)
+{
+    if (desktop_environment) eina_stringshare_del(desktop_environment);
+    if (environment) desktop_environment = eina_stringshare_add(environment);
+    else desktop_environment = NULL;
+}
+
+EAPI const char *
+efreet_desktop_environment_get(void)
+{
+    return desktop_environment;
+}
+
+EAPI unsigned int
+efreet_desktop_category_count_get(Efreet_Desktop *desktop)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+    return eina_list_count(desktop->categories);
+}
+
+EAPI void
+efreet_desktop_category_add(Efreet_Desktop *desktop, const char *category)
+{
+    EINA_SAFETY_ON_NULL_RETURN(desktop);
+    EINA_SAFETY_ON_NULL_RETURN(category);
+
+    if (eina_list_search_unsorted(desktop->categories,
+                                  EINA_COMPARE_CB(strcmp), category)) return;
+
+    desktop->categories = eina_list_append(desktop->categories,
+                        (void *)eina_stringshare_add(category));
+}
+
+EAPI int
+efreet_desktop_category_del(Efreet_Desktop *desktop, const char *category)
+{
+    char *found = NULL;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+
+    if ((found = eina_list_search_unsorted(desktop->categories,
+                                           EINA_COMPARE_CB(strcmp), category)))
+    {
+        eina_stringshare_del(found);
+        desktop->categories = eina_list_remove(desktop->categories, found);
+
+        return 1;
+    }
+
+    return 0;
+}
+
+EAPI int
+efreet_desktop_type_add(const char *type, Efreet_Desktop_Type_Parse_Cb parse_func,
+                        Efreet_Desktop_Type_Save_Cb save_func,
+                        Efreet_Desktop_Type_Free_Cb free_func)
+{
+    int id;
+    Efreet_Desktop_Type_Info *info;
+
+    info = NEW(Efreet_Desktop_Type_Info, 1);
+    if (!info) return 0;
+
+    id = eina_list_count(efreet_desktop_types);
+
+    info->id = id;
+    info->type = eina_stringshare_add(type);
+    info->parse_func = parse_func;
+    info->save_func = save_func;
+    info->free_func = free_func;
+
+    efreet_desktop_types = eina_list_append(efreet_desktop_types, info);
+
+    return id;
+}
+
+EAPI int
+efreet_desktop_type_alias(int from_type, const char *alias)
+{
+    Efreet_Desktop_Type_Info *info;
+    info = eina_list_nth(efreet_desktop_types, from_type);
+    if (!info) return -1;
+
+    return efreet_desktop_type_add(alias, info->parse_func, info->save_func, info->free_func);
+}
+
+EAPI Eina_Bool
+efreet_desktop_x_field_set(Efreet_Desktop *desktop, const char *key, const char *data)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, EINA_FALSE);
+    EINA_SAFETY_ON_TRUE_RETURN_VAL(strncmp(key, "X-", 2), EINA_FALSE);
+
+    if (!desktop->x)
+        desktop->x = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
+
+    eina_hash_del_by_key(desktop->x, key);
+    eina_hash_add(desktop->x, key, eina_stringshare_add(data));
+
+    return EINA_TRUE;
+}
+
+EAPI const char *
+efreet_desktop_x_field_get(Efreet_Desktop *desktop, const char *key)
+{
+    const char *ret;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->x, NULL);
+    EINA_SAFETY_ON_TRUE_RETURN_VAL(strncmp(key, "X-", 2), NULL);
+
+    ret = eina_hash_find(desktop->x, key);
+    if (!ret)
+        return NULL;
+
+    return eina_stringshare_add(ret);
+}
+
+EAPI Eina_Bool
+efreet_desktop_x_field_del(Efreet_Desktop *desktop, const char *key)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, EINA_FALSE);
+    EINA_SAFETY_ON_TRUE_RETURN_VAL(strncmp(key, "X-", 2), EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->x, EINA_FALSE);
+
+    return eina_hash_del_by_key(desktop->x, key);
+}
+
+EAPI void *
+efreet_desktop_type_data_get(Efreet_Desktop *desktop)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
+    return desktop->type_data;
+}
+
+EAPI Eina_List *
+efreet_desktop_string_list_parse(const char *string)
+{
+    Eina_List *list = NULL;
+    char *tmp;
+    char *s, *p;
+    size_t len;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(string, NULL);
+
+    len = strlen(string) + 1;
+    tmp = alloca(len);
+    memcpy(tmp, string, len);
+    s = tmp;
+
+    while ((p = strchr(s, ';')))
+    {
+        if (p > tmp && *(p-1) == '\\') continue;
+        *p = '\0';
+        list = eina_list_append(list, (void *)eina_stringshare_add(s));
+        s = p + 1;
+    }
+    /* If this is true, the .desktop file does not follow the standard */
+    if (*s)
+    {
+#ifdef STRICT_SPEC
+        WRN("[Efreet]: Found a string list without ';' "
+                "at the end: %s", string);
+#endif
+        list = eina_list_append(list, (void *)eina_stringshare_add(s));
+    }
+
+    return list;
+}
+
+EAPI char *
+efreet_desktop_string_list_join(Eina_List *list)
+{
+    Eina_List *l;
+    const char *elem;
+    char *string;
+    size_t size, pos, len;
+
+    if (!list) return strdup("");
+
+    size = 1024;
+    string = malloc(size);
+    if (!string) return NULL;
+    pos = 0;
+
+    EINA_LIST_FOREACH(list, l, elem)
+    {
+        len = strlen(elem);
+        /* +1 for ';' */
+        if ((len + pos + 1) >= size)
+        {
+            char *tmp;
+            size = len + pos + 1024;
+            tmp = realloc(string, size);
+            if (!tmp)
+            {
+                free(string);
+                return NULL;
+            }
+            string = tmp;
+        }
+        strcpy(string + pos, elem);
+        pos += len;
+        strcpy(string + pos, ";");
+        pos += 1;
+    }
+    return string;
+}
+
+/**
+ * @internal
+ * @param desktop The desktop to fill
+ * @return Returns 1 on success, 0 on failure
+ * @brief initialize an Efreet_Desktop from the contents of @a file
+ */
+static int
+efreet_desktop_read(Efreet_Desktop *desktop)
+{
+    Efreet_Ini *ini;
+    int error = 0;
+    int ok;
+
+    ini = efreet_ini_new(desktop->orig_path);
+    if (!ini) return 0;
+    if (!ini->data)
+    {
+        efreet_ini_free(ini);
+        return 0;
+    }
+
+    ok = efreet_ini_section_set(ini, "Desktop Entry");
+    if (!ok) ok = efreet_ini_section_set(ini, "KDE Desktop Entry");
+    if (!ok)
+    {
+        ERR("efreet_desktop_new error: no Desktop Entry section");
+        error = 1;
+    }
+
+    if (!error)
+    {
+        Efreet_Desktop_Type_Info *info;
+
+        info = efreet_desktop_type_parse(efreet_ini_string_get(ini, "Type"));
+        if (info)
+        {
+            const char *val;
+
+            desktop->type = info->id;
+            val = efreet_ini_string_get(ini, "Version");
+            if (val) desktop->version = strdup(val);
+
+            if (info->parse_func)
+                desktop->type_data = info->parse_func(desktop, ini);
+        }
+        else
+            error = 1;
+    }
+
+    if (!error && !efreet_desktop_generic_fields_parse(desktop, ini)) error = 1;
+    if (!error && !efreet_desktop_environment_check(desktop)) error = 1;
+    if (!error)
+        eina_hash_foreach(ini->section, efreet_desktop_x_fields_parse, desktop);
+
+    efreet_ini_free(ini);
+
+    desktop->load_time = ecore_file_mod_time(desktop->orig_path);
+
+    if (error) return 0;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param type_str the type as a string
+ * @return the parsed type
+ * @brief parse the type string into an Efreet_Desktop_Type
+ */
+static Efreet_Desktop_Type_Info *
+efreet_desktop_type_parse(const char *type_str)
+{
+    Efreet_Desktop_Type_Info *info;
+    Eina_List *l;
+
+    if (!type_str) return NULL;
+
+    EINA_LIST_FOREACH(efreet_desktop_types, l, info)
+    {
+        if (!strcmp(info->type, type_str))
+            return info;
+    }
+
+    return NULL;
+}
+
+/**
+ * @internal
+ * @brief Free an Efreet Desktop_Type_Info struct
+ */
+static void
+efreet_desktop_type_info_free(Efreet_Desktop_Type_Info *info)
+{
+    if (!info) return;
+    IF_RELEASE(info->type);
+    free(info);
+}
+
+/**
+ * @internal
+ * @param desktop the Efreet_Desktop to store parsed fields in
+ * @param ini the Efreet_Ini to parse fields from
+ * @return No value
+ * @brief Parse application specific desktop fields
+ */
+static void *
+efreet_desktop_application_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini)
+{
+    const char *val;
+
+    val = efreet_ini_string_get(ini, "TryExec");
+    if (val) desktop->try_exec = strdup(val);
+
+    val = efreet_ini_string_get(ini, "Exec");
+    if (val) desktop->exec = strdup(val);
+
+    val = efreet_ini_string_get(ini, "Path");
+    if (val) desktop->path = strdup(val);
+
+    val = efreet_ini_string_get(ini, "StartupWMClass");
+    if (val) desktop->startup_wm_class = strdup(val);
+
+    val = efreet_ini_string_get(ini, "Categories");
+    if (val)
+      desktop->categories = efreet_desktop_string_list_parse(val);
+    val = efreet_ini_string_get(ini, "MimeType");
+    if (val) desktop->mime_types = efreet_desktop_string_list_parse(val);
+
+    desktop->terminal = efreet_ini_boolean_get(ini, "Terminal");
+    desktop->startup_notify = efreet_ini_boolean_get(ini, "StartupNotify");
+
+    return NULL;
+}
+
+/**
+ * @internal
+ * @param desktop the Efreet_Desktop to save fields from
+ * @param ini the Efreet_Ini to save fields to
+ * @return Returns no value
+ * @brief Save application specific desktop fields
+ */
+static void
+efreet_desktop_application_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini)
+{
+    char *val;
+
+    if (desktop->try_exec)
+        efreet_ini_string_set(ini, "TryExec", desktop->try_exec);
+
+    if (desktop->exec)
+        efreet_ini_string_set(ini, "Exec", desktop->exec);
+
+    if (desktop->path)
+        efreet_ini_string_set(ini, "Path", desktop->path);
+
+    if (desktop->startup_wm_class)
+        efreet_ini_string_set(ini, "StartupWMClass", desktop->startup_wm_class);
+
+    if (desktop->categories)
+    {
+        val = efreet_desktop_string_list_join(desktop->categories);
+        if (val)
+        {
+            efreet_ini_string_set(ini, "Categories", val);
+            FREE(val);
+        }
+    }
+
+    if (desktop->mime_types)
+    {
+        val = efreet_desktop_string_list_join(desktop->mime_types);
+        if (val)
+        {
+           efreet_ini_string_set(ini, "MimeType", val);
+           FREE(val);
+        }
+    }
+
+    efreet_ini_boolean_set(ini, "Terminal", desktop->terminal);
+    efreet_ini_boolean_set(ini, "StartupNotify", desktop->startup_notify);
+}
+
+/**
+ * @internal
+ * @param desktop the Efreet_Desktop to store parsed fields in
+ * @param ini the Efreet_Ini to parse fields from
+ * @return Returns no value
+ * @brief Parse link specific desktop fields
+ */
+static void *
+efreet_desktop_link_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini)
+{
+    const char *val;
+
+    val = efreet_ini_string_get(ini, "URL");
+    if (val) desktop->url = strdup(val);
+    return NULL;
+}
+
+/**
+ * @internal
+ * @param desktop the Efreet_Desktop to save fields from
+ * @param ini the Efreet_Ini to save fields in
+ * @return Returns no value
+ * @brief Save link specific desktop fields
+ */
+static void
+efreet_desktop_link_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini)
+{
+    if (desktop->url) efreet_ini_string_set(ini, "URL", desktop->url);
+}
+
+/**
+ * @internal
+ * @param desktop the Efreet_Desktop to store parsed fields in
+ * @param ini the Efreet_Ini to parse fields from
+ * @return 1 if parsed successfully, 0 otherwise
+ * @brief Parse desktop fields that all types can include
+ */
+static int
+efreet_desktop_generic_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini)
+{
+    const char *val;
+    const char *not_show_in = NULL, *only_show_in = NULL;
+
+    val = efreet_ini_localestring_get(ini, "Name");
+#ifndef STRICT_SPEC
+    if (!val) val = efreet_ini_localestring_get(ini, "_Name");
+#endif
+    if (val) desktop->name = strdup(val);
+    else
+    {
+        ERR("efreet_desktop_generic_fields_parse error: no Name or _Name fields");
+        return 0;
+    }
+
+    val = efreet_ini_localestring_get(ini, "GenericName");
+    if (val) desktop->generic_name = strdup(val);
+
+    val = efreet_ini_localestring_get(ini, "Comment");
+#ifndef STRICT_SPEC
+    if (!val) val = efreet_ini_localestring_get(ini, "_Comment");
+#endif
+    if (val) desktop->comment = strdup(val);
+
+    val = efreet_ini_localestring_get(ini, "Icon");
+    if (val) desktop->icon = strdup(val);
+
+    desktop->no_display = efreet_ini_boolean_get(ini, "NoDisplay");
+    desktop->hidden = efreet_ini_boolean_get(ini, "Hidden");
+
+    only_show_in = efreet_ini_string_get(ini, "OnlyShowIn");
+    not_show_in = efreet_ini_string_get(ini, "NotShowIn");
+    if (only_show_in && not_show_in)
+        WRN("Both OnlyShowIn and NotShowIn in %s, preferring OnlyShowIn", desktop->orig_path);
+    if (only_show_in) desktop->only_show_in = efreet_desktop_string_list_parse(only_show_in);
+    else if (not_show_in) desktop->not_show_in = efreet_desktop_string_list_parse(not_show_in);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param desktop the Efreet_Desktop to save fields from
+ * @param ini the Efreet_Ini to save fields to
+ * @return Returns nothing
+ * @brief Save desktop fields that all types can include
+ */
+static void
+efreet_desktop_generic_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini)
+{
+    const char *val;
+
+    if (desktop->name)
+    {
+        efreet_ini_localestring_set(ini, "Name", desktop->name);
+        val = efreet_ini_string_get(ini, "Name");
+        if (!val)
+            efreet_ini_string_set(ini, "Name", desktop->name);
+    }
+    if (desktop->generic_name)
+    {
+        efreet_ini_localestring_set(ini, "GenericName", desktop->generic_name);
+        val = efreet_ini_string_get(ini, "GenericName");
+        if (!val)
+            efreet_ini_string_set(ini, "GenericName", desktop->generic_name);
+    }
+    if (desktop->comment)
+    {
+        efreet_ini_localestring_set(ini, "Comment", desktop->comment);
+        val = efreet_ini_string_get(ini, "Comment");
+        if (!val)
+            efreet_ini_string_set(ini, "Comment", desktop->comment);
+    }
+    if (desktop->icon)
+    {
+        efreet_ini_localestring_set(ini, "Icon", desktop->icon);
+        val = efreet_ini_string_get(ini, "Icon");
+        if (!val)
+            efreet_ini_string_set(ini, "Icon", desktop->icon);
+    }
+
+    efreet_ini_boolean_set(ini, "NoDisplay", desktop->no_display);
+    efreet_ini_boolean_set(ini, "Hidden", desktop->hidden);
+
+    if (desktop->x) eina_hash_foreach(desktop->x, efreet_desktop_x_fields_save,
+                                        ini);
+}
+
+/**
+ * @internal
+ * @param node The node to work with
+ * @param desktop The desktop file to work with
+ * @return Returns always true, to be used in eina_hash_foreach()
+ * @brief Parses out an X- key from @a node and stores in @a desktop
+ */
+static Eina_Bool
+efreet_desktop_x_fields_parse(const Eina_Hash *hash __UNUSED__, const void *key, void *value, void *fdata)
+{
+    Efreet_Desktop * desktop = fdata;
+
+    if (!desktop) return EINA_TRUE;
+    if (strncmp(key, "X-", 2)) return EINA_TRUE;
+
+    if (!desktop->x)
+        desktop->x = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
+    eina_hash_del_by_key(desktop->x, key);
+    eina_hash_add(desktop->x, key, (void *)eina_stringshare_add(value));
+
+    return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @param node The node to work with
+ * @param ini The ini file to work with
+ * @return Returns no value
+ * @brief Stores an X- key from @a node and stores in @a ini
+ */
+static Eina_Bool
+efreet_desktop_x_fields_save(const Eina_Hash *hash __UNUSED__, const void *key, void *value, void *fdata)
+{
+    Efreet_Ini *ini = fdata;
+    efreet_ini_string_set(ini, key, value);
+
+    return EINA_TRUE;
+}
+
+
+/**
+ * @internal
+ * @param ini The Efreet_Ini to parse values from
+ * @return 1 if desktop should be included in current environement, 0 otherwise
+ * @brief Determines if a desktop should be included in the current environment,
+ * based on the values of the OnlyShowIn and NotShowIn fields
+ */
+static int
+efreet_desktop_environment_check(Efreet_Desktop *desktop)
+{
+    Eina_List *list;
+    int found = 0;
+    char *val;
+
+    if (!desktop_environment)
+    {
+        //if (desktop->only_show_in) return 0;
+        return 1;
+    }
+
+    if (desktop->only_show_in)
+    {
+        EINA_LIST_FOREACH(desktop->only_show_in, list, val)
+        {
+            if (!strcmp(val, desktop_environment))
+            {
+                found = 1;
+                break;
+            }
+        }
+        return found;
+    }
+
+
+    if (desktop->not_show_in)
+    {
+        EINA_LIST_FOREACH(desktop->not_show_in, list, val)
+        {
+            if (!strcmp(val, desktop_environment))
+            {
+                found = 1;
+                break;
+            }
+        }
+        return !found;
+    }
+
+    return 1;
+}
+
+static void
+efreet_desktop_changes_listen(void)
+{
+    Efreet_Cache_Array_String *arr;
+    Eina_List *dirs;
+    const char *path;
+
+    if (!efreet_cache_update) return;
+
+    change_monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
+    if (!change_monitors) return;
+
+    dirs = efreet_default_dirs_get(efreet_data_home_get(),
+                                   efreet_data_dirs_get(), "applications");
+
+    EINA_LIST_FREE(dirs, path)
+    {
+        if (ecore_file_is_dir(path))
+            efreet_desktop_changes_listen_recursive(path);
+        eina_stringshare_del(path);
+    }
+
+    arr = efreet_cache_desktop_dirs();
+    if (arr)
+    {
+        unsigned int i;
+
+        for (i = 0; i < arr->array_count; i++)
+            efreet_desktop_changes_monitor_add(arr->array[i]);
+        efreet_cache_array_string_free(arr);
+    }
+}
+
+static void
+efreet_desktop_changes_listen_recursive(const char *path)
+{
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *info;
+
+    efreet_desktop_changes_monitor_add(path);
+
+    it = eina_file_direct_ls(path);
+    if (!it) return;
+    EINA_ITERATOR_FOREACH(it, info)
+    {
+        if (ecore_file_is_dir(info->path)) efreet_desktop_changes_listen_recursive(info->path);
+    }
+    eina_iterator_free(it);
+}
+
+static void
+efreet_desktop_changes_monitor_add(const char *path)
+{
+    char rp[PATH_MAX];
+
+    if (!realpath(path, rp)) return;
+    if (eina_hash_find(change_monitors, rp)) return;
+    eina_hash_add(change_monitors, rp,
+                  ecore_file_monitor_add(rp,
+                                         efreet_desktop_changes_cb,
+                                         NULL));
+}
+
+static void
+efreet_desktop_changes_cb(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__,
+                                 Ecore_File_Event event, const char *path)
+{
+    const char *ext;
+
+    /* TODO: If we get a stale symlink, we need to rerun cache creation */
+    /* TODO: Check for desktop*.cache, as this will be created when app is installed */
+    /* TODO: Do efreet_cache_icon_update() when app is installed, as it has the same
+     *       symlink problem */
+    switch (event)
+    {
+        case ECORE_FILE_EVENT_NONE:
+            /* noop */
+            break;
+
+        case ECORE_FILE_EVENT_CREATED_FILE:
+        case ECORE_FILE_EVENT_DELETED_FILE:
+        case ECORE_FILE_EVENT_MODIFIED:
+        case ECORE_FILE_EVENT_CLOSED:
+            ext = strrchr(path, '.');
+            if (ext && (!strcmp(ext, ".desktop") || !strcmp(ext, ".directory")))
+                efreet_cache_desktop_update();
+            break;
+
+        case ECORE_FILE_EVENT_DELETED_SELF:
+        case ECORE_FILE_EVENT_DELETED_DIRECTORY:
+            eina_hash_del_by_key(change_monitors, path);
+            efreet_cache_desktop_update();
+            break;
+
+        case ECORE_FILE_EVENT_CREATED_DIRECTORY:
+            efreet_desktop_changes_monitor_add(path);
+            efreet_cache_desktop_update();
+            break;
+    }
+}
diff --git a/src/lib/efreet_desktop.h b/src/lib/efreet_desktop.h
new file mode 100644 (file)
index 0000000..f7dfe51
--- /dev/null
@@ -0,0 +1,370 @@
+#ifndef EFREET_DESKTOP_H
+#define EFREET_DESKTOP_H
+
+/**
+ * @file efreet_desktop.h
+ * @brief Contains the structures and methods used to support the
+ *        FDO desktop entry specificiation.
+ * @addtogroup Efreet_Desktop Efreet_Desktop: The FDO Desktop Entry
+ *                  Specification functions and structures
+ *
+ * @{
+ */
+
+
+/**
+ * @param desktop the desktop entry
+ * @param files an eina list of file names to execute, as either absolute paths,
+ * relative paths, or uris
+ * @param func a callback to call for each prepared command line
+ * @param data user data passed to the callback
+ * @return Returns the return value of @p func on success or NULL on failure
+ * @brief Get a command to use to execute a desktop entry.
+ */
+EAPI extern int EFREET_DESKTOP_TYPE_APPLICATION;
+EAPI extern int EFREET_DESKTOP_TYPE_LINK;
+EAPI extern int EFREET_DESKTOP_TYPE_DIRECTORY;
+
+/**
+ * Event id for cache update. All users of efreet_desktop_get must listen to
+ * this event and refetch. The old eet cache will be closed and mem will
+ * be invalidated.
+ */
+EAPI extern int EFREET_EVENT_DESKTOP_CACHE_UPDATE;
+/**
+ * Event id for cache build complete.
+ * @since 1.1.0
+ */
+EAPI extern int EFREET_EVENT_DESKTOP_CACHE_BUILD;
+
+/**
+ * Efreet_Desktop
+ */
+typedef struct _Efreet_Desktop Efreet_Desktop;
+
+/**
+ * A callback used with efreet_desktop_command_get()
+ */
+typedef void *(*Efreet_Desktop_Command_Cb) (void *data, Efreet_Desktop *desktop,
+                                            char *command, int remaining);
+
+/**
+ * A callback used to get download progress of remote uris
+ */
+typedef int (*Efreet_Desktop_Progress_Cb) (void *data, Efreet_Desktop *desktop,
+                                            char *uri, long int total, long int current);
+
+/**
+ * A callback used to parse data for custom types
+ */
+typedef void *(*Efreet_Desktop_Type_Parse_Cb) (Efreet_Desktop *desktop, Efreet_Ini *ini);
+
+/**
+ * A callback used to save data for custom types
+ */
+typedef void (*Efreet_Desktop_Type_Save_Cb) (Efreet_Desktop *desktop, Efreet_Ini *ini);
+
+/**
+ * A callback used to free data for custom types
+ */
+typedef void *(*Efreet_Desktop_Type_Free_Cb) (void *data);
+
+/**
+ * Efreet_Desktop
+ * @brief a parsed representation of a .desktop file
+ */
+struct _Efreet_Desktop
+{
+    int type;               /**< type of desktop file */
+
+    int ref;                /**< reference count - internal */
+
+    char *version;          /**< version of spec file conforms to */
+
+    char *orig_path;        /**< original path to .desktop file */
+    long long load_time;    /**< modified time of .desktop on disk */
+
+    char *name;             /**< Specific name of the application */
+    char *generic_name;     /**< Generic name of the application */
+    char *comment;          /**< Tooltip for the entry */
+    char *icon;             /**< Icon to display in file manager, menus, etc */
+    char *try_exec;         /**< Binary to determine if app is installed */
+    char *exec;             /**< Program to execute */
+    char *path;             /**< Working directory to run app in */
+    char *startup_wm_class; /**< If specified will map at least one window with
+                                the given string as it's WM class or WM name */
+    char *url;              /**< URL to access if type is EFREET_TYPE_LINK */
+
+    Eina_List  *only_show_in;   /**< list of environments that should
+                                                    display the icon */
+    Eina_List  *not_show_in;    /**< list of environments that shoudn't
+                                                    display the icon */
+    Eina_List  *categories;     /**< Categories in which item should be shown */
+    Eina_List  *mime_types;     /**< The mime types supppored by this app */
+
+    unsigned char no_display;        /**< Don't display this application in menus */
+    unsigned char hidden;            /**< User delete the item */
+    unsigned char terminal;          /**< Does the program run in a terminal */
+    unsigned char startup_notify;    /**< The starup notify settings of the app */
+    unsigned char eet:1;             /**< The desktop file is in eet cache */
+
+    Eina_Hash *x; /**< Keep track of all user extensions, keys that begin with X- */
+    void *type_data; /**< Type specific data for custom types */
+};
+
+
+/**
+ * @param file The file to get the Efreet_Desktop from
+ * @return Returns a reference to a cached Efreet_Desktop on success, NULL
+ * on failure
+ * @brief Gets a reference to an Efreet_Desktop structure representing the
+ * contents of @a file or NULL if @a file is not a valid .desktop file.
+ *
+ * By using efreet_desktop_get the Efreet_Desktop will be saved in an internal
+ * cache for quicker loading.
+ *
+ * Users of this command should listen to EFREET_EVENT_DESKTOP_CACHE_UPDATE
+ * event, if the application is to keep the reference. When the event fires
+ * the Efreet_Desktop struct should be invalidated and reloaded from a new
+ * cache file.
+ */
+EAPI Efreet_Desktop   *efreet_desktop_get(const char *file);
+
+/**
+ * @param desktop The Efreet_Desktop to ref
+ * @return Returns the new reference count
+ * @brief Increases reference count on desktop
+ */
+EAPI int               efreet_desktop_ref(Efreet_Desktop *desktop);
+
+/**
+ * @param file The file to create the Efreet_Desktop from
+ * @return Returns a new empty_Efreet_Desktop on success, NULL on failure
+ * @brief Creates a new empty Efreet_Desktop structure or NULL on failure
+ */
+EAPI Efreet_Desktop   *efreet_desktop_empty_new(const char *file);
+
+/**
+ * @param file The file to get the Efreet_Desktop from
+ * @return Returns a reference to a cached Efreet_Desktop on success, NULL
+ * on failure
+ * @brief Gets a reference to an Efreet_Desktop structure representing the
+ * contents of @a file or NULL if @a file is not a valid .desktop file.
+ *
+ * Users of this command should listen to EFREET_EVENT_DESKTOP_CACHE_UPDATE
+ * event, if the application is to keep the reference. When the event fires
+ * the Efreet_Desktop struct should be invalidated and reloaded from a new
+ * cache file.
+ */
+EAPI Efreet_Desktop   *efreet_desktop_new(const char *file);
+
+/**
+ * @param file The file to create the Efreet_Desktop from
+ * @return Returns a new Efreet_Desktop on success, NULL on failure
+ * @brief Creates a new Efreet_Desktop structure initialized from the
+ * contents of @a file or NULL on failure
+ *
+ * By using efreet_desktop_uncached_new the Efreet_Desktop structure will be
+ * read from disk, and not from any cache.
+ *
+ * Data in the structure is allocated with strdup, so use free and strdup to
+ * change values.
+ */
+EAPI Efreet_Desktop   *efreet_desktop_uncached_new(const char *file);
+
+/**
+ * @param desktop The Efreet_Desktop to work with
+ * @return Returns no value
+ * @brief Frees the Efreet_Desktop structure and all of it's data
+ */
+EAPI void              efreet_desktop_free(Efreet_Desktop *desktop);
+
+/**
+ * @def efreet_desktop_unref(desktop)
+ * Alias for efreet_desktop_free(desktop)
+ */
+#define efreet_desktop_unref(desktop) efreet_desktop_free((desktop))
+
+
+/**
+ * @param desktop The desktop file to save
+ * @return Returns 1 on success or 0 on failure
+ * @brief Saves any changes made to @a desktop back to the file on the
+ * filesystem
+ */
+EAPI int               efreet_desktop_save(Efreet_Desktop *desktop);
+
+/**
+ * @param desktop The desktop file to save
+ * @param file The filename to save as
+ * @return Returns 1 on success or 0 on failure
+ * @brief Saves @a desktop to @a file
+ *
+ * Please use efreet_desktop_uncached_new() on an existing file
+ * before using efreet_desktop_save_as()
+ */
+EAPI int               efreet_desktop_save_as(Efreet_Desktop *desktop,
+                                                const char *file);
+
+
+/**
+ * @param desktop The desktop file to work with
+ * @param files The files to be substituted into the exec line
+ * @param data The data pointer to pass
+ * @return Returns the Ecore_Exce for @a desktop
+ * @brief Parses the @a desktop exec line and returns an Ecore_Exe.
+ */
+EAPI void              efreet_desktop_exec(Efreet_Desktop *desktop,
+                                           Eina_List *files, void *data);
+
+
+/**
+ * @param environment the environment name
+ * @brief sets the global desktop environment name
+ */
+EAPI void              efreet_desktop_environment_set(const char *environment);
+
+/**
+ * @return environment the environment name
+ * @brief sets the global desktop environment name
+ */
+EAPI const char       *efreet_desktop_environment_get(void);
+
+/**
+ * @param desktop the desktop entry
+ * @param files an eina list of file names to execute, as either absolute paths,
+ * relative paths, or uris
+ * @param cb_command a callback to call for each prepared command line
+ * @param cb_prog a callback to get progress for the downloads
+ * @param data user data passed to the callback
+ * @return Returns 1 on success or 0 on failure
+ * @brief Get a command to use to execute a desktop entry, and receive progress
+ * updates for downloading of remote URI's passed in.
+ */
+EAPI void             *efreet_desktop_command_progress_get(Efreet_Desktop *desktop,
+                                         Eina_List *files,
+                                         Efreet_Desktop_Command_Cb cb_command,
+                                         Efreet_Desktop_Progress_Cb cb_prog,
+                                         void *data);
+EAPI void              *efreet_desktop_command_get(Efreet_Desktop *desktop,
+                                         Eina_List *files,
+                                         Efreet_Desktop_Command_Cb func,
+                                         void *data);
+
+/**
+ * @param desktop the desktop entry
+ * @param files an eina list of local files, as absolute paths, local paths, or file// uris (or NULL to get exec string with no files appended)
+ * @return Returns an eina list of exec strings
+ * @brief Get the command to use to execute a desktop entry
+ *
+ * The returned list and each of its elements must be freed.
+ */
+EAPI Eina_List *      efreet_desktop_command_local_get(Efreet_Desktop *desktop,
+                                         Eina_List *files);
+
+
+/**
+ * @param desktop The desktop to work with
+ * @return Returns the number of categories assigned to this desktop
+ * @brief Retrieves the number of categories the given @a desktop belongs
+ * too
+ */
+EAPI unsigned int      efreet_desktop_category_count_get(Efreet_Desktop *desktop);
+
+/**
+ * @param desktop the desktop
+ * @param category the category name
+ * @brief add a category to a desktop
+ */
+EAPI void              efreet_desktop_category_add(Efreet_Desktop *desktop,
+                                              const char *category);
+
+/**
+ * @param desktop the desktop
+ * @param category the category name
+ * @brief removes a category from a desktop
+ * @return 1 if the desktop had his category listed, 0 otherwise
+ */
+EAPI int               efreet_desktop_category_del(Efreet_Desktop *desktop,
+                                              const char *category);
+
+
+/**
+ * @param type The type to add to the list of matching types
+ * @param parse_func a function to parse out custom fields
+ * @param save_func a function to save data returned from @a parse_func
+ * @param free_func a function to free data returned from @a parse_func
+ * @return Returns the id of the new type
+ * @brief Adds the given type to the list of types in the system
+ */
+EAPI int               efreet_desktop_type_add(const char *type,
+                                    Efreet_Desktop_Type_Parse_Cb parse_func,
+                                    Efreet_Desktop_Type_Save_Cb save_func,
+                                    Efreet_Desktop_Type_Free_Cb free_func);
+
+/**
+ * @brief Add an alias for an existing desktop type.
+ * @param from_type the type to alias (e.g. EFREE_DESKTOP_TYPE_APPLICATION)
+ * @param alias the alias
+ * @return the new type id, or -1 if @p from_type was not valid
+ *
+ * This allows applications to add non-standard types that behave exactly as standard types.
+ */
+EAPI int               efreet_desktop_type_alias (int from_type,
+                                             const char *alias);
+
+/**
+ * @brief get type specific data for custom desktop types
+ * @param desktop the desktop
+ * @return type specific data, or NULL if there is none
+ */
+EAPI void             *efreet_desktop_type_data_get(Efreet_Desktop *desktop);
+
+
+/**
+ * @param string the raw string list
+ * @return an Eina_List of ecore string's
+ * @brief Parse ';' separate list of strings according to the desktop spec
+ */
+EAPI Eina_List        *efreet_desktop_string_list_parse(const char *string);
+
+/**
+ * @param list Eina_List with strings
+ * @return a raw string list
+ * @brief Create a ';' separate list of strings according to the desktop spec
+ */
+EAPI char             *efreet_desktop_string_list_join(Eina_List *list);
+
+
+/**
+ * @brief Set the value for a X- field (Non spec) in the structure
+ * @param desktop the desktop
+ * @param key the key name to set
+ * @param data the value to set
+ * @return EINA_TRUE on success
+ *
+ * The key has to start with "X-"
+ */
+EAPI Eina_Bool         efreet_desktop_x_field_set(Efreet_Desktop *desktop, const char *key, const char *data);
+
+/**
+ * @brief Get the value for a X- field (Non spec) in the structure
+ * @param desktop the desktop
+ * @param key the key
+ * @return The value referenced by the key, or NULL if the key does not exist
+ */
+EAPI const char *      efreet_desktop_x_field_get(Efreet_Desktop *desktop, const char *key);
+
+/**
+ * @brief Delete the key and value for a X- field (Non spec) in the structure
+ * @param desktop the desktop
+ * @param key the key
+ * @return EINA_TRUE if the key existed
+ */
+EAPI Eina_Bool         efreet_desktop_x_field_del(Efreet_Desktop *desktop, const char *key);
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_desktop_command.c b/src/lib/efreet_desktop_command.c
new file mode 100644 (file)
index 0000000..bf97f8f
--- /dev/null
@@ -0,0 +1,919 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <unistd.h>
+#include <ctype.h>
+
+#ifdef _WIN32
+# include <winsock2.h>
+#endif
+
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_desktop_log_dom
+extern int _efreet_desktop_log_dom;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+/**
+ * @internal
+ * The different types of commands in an Exec entry
+ */
+typedef enum Efreet_Desktop_Command_Flag
+{
+    EFREET_DESKTOP_EXEC_FLAG_FULLPATH = 0x0001,
+    EFREET_DESKTOP_EXEC_FLAG_URI      = 0x0002
+} Efreet_Desktop_Command_Flag;
+
+/**
+ * @internal
+ * Efreet_Desktop_Command
+ */
+typedef struct Efreet_Desktop_Command Efreet_Desktop_Command;
+
+/**
+ * @internal
+ * Holds information on a desktop Exec command entry
+ */
+struct Efreet_Desktop_Command
+{
+  Efreet_Desktop *desktop;
+  int num_pending;
+
+  Efreet_Desktop_Command_Flag flags;
+
+  Efreet_Desktop_Command_Cb cb_command;
+  Efreet_Desktop_Progress_Cb cb_progress;
+  void *data;
+
+  Eina_List *files; /**< list of Efreet_Desktop_Command_File */
+};
+
+/**
+ * @internal
+ * Efreet_Desktop_Command_File
+ */
+typedef struct Efreet_Desktop_Command_File Efreet_Desktop_Command_File;
+
+/**
+ * @internal
+ * Stores information on a file passed to the desktop Exec command
+ */
+struct Efreet_Desktop_Command_File
+{
+  Efreet_Desktop_Command *command;
+  char *dir;
+  char *file;
+  char *fullpath;
+  char *uri;
+
+  int pending;
+};
+
+static int efreet_desktop_command_file_id = 0;
+
+static void *efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop,
+                                            char *exec, int remaining);
+static int efreet_desktop_command_flags_get(Efreet_Desktop *desktop);
+static void *efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs);
+
+static Eina_List *efreet_desktop_command_build(Efreet_Desktop_Command *command);
+static void efreet_desktop_command_free(Efreet_Desktop_Command *command);
+static char *efreet_desktop_command_append_quoted(char *dest, int *size,
+                                                    int *len, char *src);
+static char *efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
+                                                    Efreet_Desktop_Command *command,
+                                                    char type);
+static char *efreet_desktop_command_append_single(char *dest, int *size, int *len,
+                                                Efreet_Desktop_Command_File *file,
+                                                char type);
+static char *efreet_desktop_command_append_icon(char *dest, int *size, int *len,
+                                                Efreet_Desktop *desktop);
+
+static Efreet_Desktop_Command_File *efreet_desktop_command_file_process(
+                                                    Efreet_Desktop_Command *command,
+                                                    const char *file);
+static const char *efreet_desktop_command_file_uri_process(const char *uri);
+static void efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file);
+
+static void efreet_desktop_cb_download_complete(void *data, const char *file,
+                                                                int status);
+static int efreet_desktop_cb_download_progress(void *data, const char *file,
+                                           long int dltotal, long int dlnow,
+                                           long int ultotal, long int ulnow);
+
+static char *efreet_desktop_command_path_absolute(const char *path);
+
+static char *efreet_string_append(char *dest, int *size,
+                                    int *len, const char *src);
+static char *efreet_string_append_char(char *dest, int *size,
+                                        int *len, char c);
+
+
+EAPI void
+efreet_desktop_exec(Efreet_Desktop *desktop, Eina_List *files, void *data)
+{
+    efreet_desktop_command_get(desktop, files, efreet_desktop_exec_cb, data);
+}
+
+EAPI void *
+efreet_desktop_command_get(Efreet_Desktop *desktop, Eina_List *files,
+                            Efreet_Desktop_Command_Cb func, void *data)
+{
+    return efreet_desktop_command_progress_get(desktop, files, func, NULL, data);
+}
+
+EAPI Eina_List *
+efreet_desktop_command_local_get(Efreet_Desktop *desktop, Eina_List *files)
+{
+    Efreet_Desktop_Command *command;
+    char *file;
+    Eina_List *execs, *l;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->exec, NULL);
+
+    command = NEW(Efreet_Desktop_Command, 1);
+    if (!command) return 0;
+
+    command->desktop = desktop;
+
+    command->flags = efreet_desktop_command_flags_get(desktop);
+    /* get the required info for each file passed in */
+    if (files)
+    {
+        EINA_LIST_FOREACH(files, l, file)
+        {
+            Efreet_Desktop_Command_File *dcf;
+
+            dcf = efreet_desktop_command_file_process(command, file);
+            if (!dcf) continue;
+            if (dcf->pending)
+            {
+                efreet_desktop_command_file_free(dcf);
+                continue;
+            }
+            command->files = eina_list_append(command->files, dcf);
+        }
+    }
+
+    execs = efreet_desktop_command_build(command);
+    efreet_desktop_command_free(command);
+
+    return execs;
+}
+
+EAPI void *
+efreet_desktop_command_progress_get(Efreet_Desktop *desktop, Eina_List *files,
+                                    Efreet_Desktop_Command_Cb cb_command,
+                                    Efreet_Desktop_Progress_Cb cb_progress,
+                                    void *data)
+{
+    Efreet_Desktop_Command *command;
+    Eina_List *l;
+    char *file;
+    void *ret = NULL;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop->exec, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(cb_command, NULL);
+
+    command = NEW(Efreet_Desktop_Command, 1);
+    if (!command) return NULL;
+
+    command->cb_command = cb_command;
+    command->cb_progress = cb_progress;
+    command->data = data;
+    command->desktop = desktop;
+
+    command->flags = efreet_desktop_command_flags_get(desktop);
+    /* get the required info for each file passed in */
+    if (files)
+    {
+        EINA_LIST_FOREACH(files, l, file)
+        {
+            Efreet_Desktop_Command_File *dcf;
+
+            dcf = efreet_desktop_command_file_process(command, file);
+            if (!dcf) continue;
+            command->files = eina_list_append(command->files, dcf);
+            command->num_pending += dcf->pending;
+        }
+    }
+
+    if (command->num_pending == 0)
+    {
+        Eina_List *execs;
+
+        execs = efreet_desktop_command_build(command);
+        if (execs)
+        {
+            ret = efreet_desktop_command_execs_process(command, execs);
+            eina_list_free(execs);
+        }
+        efreet_desktop_command_free(command);
+    }
+
+    return ret;
+}
+
+static void *
+efreet_desktop_exec_cb(void *data,
+                       Efreet_Desktop *desktop __UNUSED__,
+                       char *exec,
+                       int remaining __UNUSED__)
+{
+    ecore_exe_run(exec, data);
+    free(exec);
+
+    return NULL;
+}
+
+/**
+ * @internal
+ *
+ * @brief Determine which file related field codes are present in the Exec string of a .desktop
+ * @params desktop and Efreet Desktop
+ * @return a bitmask of file field codes present in exec string
+ */
+static int
+efreet_desktop_command_flags_get(Efreet_Desktop *desktop)
+{
+    int flags = 0;
+    const char *p;
+    /* first, determine which fields are present in the Exec string */
+    p = strchr(desktop->exec, '%');
+    while (p)
+    {
+        p++;
+        switch(*p)
+        {
+            case 'f':
+            case 'F':
+                flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
+                break;
+            case 'u':
+            case 'U':
+                flags |= EFREET_DESKTOP_EXEC_FLAG_URI;
+                break;
+            case '%':
+                p++;
+                break;
+            default:
+                break;
+        }
+
+        p = strchr(p, '%');
+    }
+#ifdef SLOPPY_SPEC
+    /* NON-SPEC!!! this is to work around LOTS of 'broken' .desktop files that
+     * do not specify %U/%u, %F/F etc. etc. at all. just a command. this is
+     * unlikely to be fixed in distributions etc. in the long run as gnome/kde
+     * seem to have workarounds too so no one notices.
+     */
+    if (!flags) flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
+#endif
+
+    return flags;
+}
+
+
+/**
+ * @internal
+ *
+ * @brief Call the command callback for each exec in the list
+ * @param command
+ * @param execs
+ */
+static void *
+efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs)
+{
+    Eina_List *l;
+    char *exec;
+    int num;
+    void *ret = NULL;
+
+    num = eina_list_count(execs);
+    EINA_LIST_FOREACH(execs, l, exec)
+    {
+        ret = command->cb_command(command->data, command->desktop, exec, --num);
+    }
+    return ret;
+}
+
+
+/**
+ * @brief Builds the actual exec string from the raw string and a list of
+ * processed filename information. The callback passed in to
+ * efreet_desktop_command_get is called for each exec string created.
+ *
+ * @param command the command to build
+ * @return a list of executable strings
+ */
+static Eina_List *
+efreet_desktop_command_build(Efreet_Desktop_Command *command)
+{
+    Eina_List *execs = NULL;
+    const Eina_List *l;
+    char *exec;
+
+    /* if the Exec field appends multiple, that will run the list to the end,
+     * causing this loop to only run once. otherwise, this loop will generate a
+     * command for each file in the list. if the list is empty, this
+     * will run once, removing any file field codes */
+    l = command->files;
+    do
+    {
+        const char *p;
+        int len = 0;
+        int size = PATH_MAX;
+        int file_added = 0;
+        Efreet_Desktop_Command_File *file = eina_list_data_get(l);
+       int single;
+
+        exec = malloc(size);
+        if (!exec) goto error;
+        p = command->desktop->exec;
+        len = 0;
+
+        single = 0;
+        while (*p)
+        {
+            if (len >= size - 1)
+            {
+                char *tmp;
+
+                size = len + 1024;
+                tmp = realloc(exec, size);
+                if (!tmp) goto error;
+                exec = tmp;
+            }
+
+            /* XXX handle fields inside quotes? */
+            if (*p == '%')
+            {
+                p++;
+                switch (*p)
+                {
+                    case 'f':
+                    case 'u':
+                    case 'd':
+                    case 'n':
+                        if (file)
+                        {
+                            exec = efreet_desktop_command_append_single(exec, &size,
+                                    &len, file, *p);
+                            if (!exec) goto error;
+                            file_added = 1;
+                            single = 1;
+                        }
+                        break;
+                    case 'F':
+                    case 'U':
+                    case 'D':
+                    case 'N':
+                        if (file)
+                        {
+                            exec = efreet_desktop_command_append_multiple(exec, &size,
+                                    &len, command, *p);
+                           fprintf(stderr, "EXE: '%s'\n", exec);
+                            if (!exec) goto error;
+                            file_added = 1;
+                        }
+                        break;
+                    case 'i':
+                        exec = efreet_desktop_command_append_icon(exec, &size, &len,
+                                command->desktop);
+                        if (!exec) goto error;
+                        break;
+                    case 'c':
+                        exec = efreet_desktop_command_append_quoted(exec, &size, &len,
+                                command->desktop->name);
+                        if (!exec) goto error;
+                        break;
+                    case 'k':
+                        exec = efreet_desktop_command_append_quoted(exec, &size, &len,
+                                command->desktop->orig_path);
+                        if (!exec) goto error;
+                        break;
+                    case 'v':
+                    case 'm':
+                        WRN("[Efreet]: Deprecated conversion char: '%c' in file '%s'",
+                                *p, command->desktop->orig_path);
+                        break;
+                    case '%':
+                        exec[len++] = *p;
+                        break;
+                    default:
+#ifdef STRICT_SPEC
+                        WRN("[Efreet_desktop]: Unknown conversion character: '%c'", *p);
+#endif
+                        break;
+                }
+            }
+            else exec[len++] = *p;
+            p++;
+        }
+
+#ifdef SLOPPY_SPEC
+        /* NON-SPEC!!! this is to work around LOTS of 'broken' .desktop files that
+         * do not specify %U/%u, %F/F etc. etc. at all. just a command. this is
+         * unlikely to be fixed in distributions etc. in the long run as gnome/kde
+         * seem to have workarounds too so no one notices.
+         */
+        if ((file) && (!file_added))
+        {
+            WRN("Efreet_desktop: %s\n"
+                "  command: %s\n"
+                "  has no file path/uri spec info for executing this app WITH a\n"
+                "  file/uri as a parameter. This is unlikely to be the intent.\n"
+                "  please check the .desktop file and fix it by adding a %%U or %%F\n"
+                "  or something appropriate.",
+                command->desktop->orig_path, command->desktop->exec);
+            if (len >= size - 1)
+            {
+                char *tmp;
+                size = len + 1024;
+                tmp = realloc(exec, size);
+                if (!tmp) goto error;
+                exec = tmp;
+            }
+            exec[len++] = ' ';
+            exec = efreet_desktop_command_append_multiple(exec, &size,
+                    &len, command, 'F');
+            if (!exec) goto error;
+            file_added = 1;
+        }
+#endif
+        exec[len++] = '\0';
+
+       if ((single) || (!execs))
+         {
+            execs = eina_list_append(execs, exec);
+            exec = NULL;
+         }
+
+        /* If no file was added, then the Exec field doesn't contain any file
+         * fields (fFuUdDnN). We only want to run the app once in this case. */
+        if (!file_added) break;
+    }
+    while ((l = eina_list_next(l)));
+
+    return execs;
+error:
+    IF_FREE(exec);
+    EINA_LIST_FREE(execs, exec)
+        free(exec);
+    return NULL;
+}
+
+static void
+efreet_desktop_command_free(Efreet_Desktop_Command *command)
+{
+    Efreet_Desktop_Command_File *dcf;
+
+    if (!command) return;
+
+    while (command->files)
+    {
+        dcf = eina_list_data_get(command->files);
+        efreet_desktop_command_file_free(dcf);
+        command->files = eina_list_remove_list(command->files,
+                                               command->files);
+    }
+    FREE(command);
+}
+
+static char *
+efreet_desktop_command_append_quoted(char *dest, int *size, int *len, char *src)
+{
+    if (!src) return dest;
+    dest = efreet_string_append(dest, size, len, "'");
+    if (!dest) return NULL;
+
+    /* single quotes in src need to be escaped */
+    if (strchr(src, '\''))
+    {
+        char *p;
+        p = src;
+        while (*p)
+        {
+            if (*p == '\'')
+            {
+                dest = efreet_string_append(dest, size, len, "\'\\\'");
+                if (!dest) return NULL;
+            }
+
+            dest = efreet_string_append_char(dest, size, len, *p);
+            if (!dest) return NULL;
+            p++;
+        }
+    }
+    else
+    {
+        dest = efreet_string_append(dest, size, len, src);
+        if (!dest) return NULL;
+    }
+
+    dest = efreet_string_append(dest, size, len, "'");
+    if (!dest) return NULL;
+
+    return dest;
+}
+
+static char *
+efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
+                                        Efreet_Desktop_Command *command,
+                                        char type)
+{
+    Efreet_Desktop_Command_File *file;
+    Eina_List *l;
+    int first = 1;
+
+    if (!command->files) return dest;
+
+    EINA_LIST_FOREACH(command->files, l, file)
+    {
+        if (first)
+            first = 0;
+        else
+        {
+            dest = efreet_string_append_char(dest, size, len, ' ');
+            if (!dest) return NULL;
+        }
+
+        dest = efreet_desktop_command_append_single(dest, size, len,
+                                                    file, tolower(type));
+        if (!dest) return NULL;
+    }
+
+    return dest;
+}
+
+static char *
+efreet_desktop_command_append_single(char *dest, int *size, int *len,
+                                        Efreet_Desktop_Command_File *file,
+                                        char type)
+{
+    char *str;
+    switch(type)
+    {
+        case 'f':
+            str = file->fullpath;
+            break;
+        case 'u':
+            str = file->uri;
+            break;
+        case 'd':
+            str = file->dir;
+            break;
+        case 'n':
+            str = file->file;
+            break;
+        default:
+            ERR("Invalid type passed to efreet_desktop_command_append_single:"
+                                                                " '%c'", type);
+            return dest;
+    }
+
+    if (!str) return dest;
+
+    dest = efreet_desktop_command_append_quoted(dest, size, len, str);
+    if (!dest) return NULL;
+
+    return dest;
+}
+
+static char *
+efreet_desktop_command_append_icon(char *dest, int *size, int *len,
+                                            Efreet_Desktop *desktop)
+{
+    if (!desktop->icon || !desktop->icon[0]) return dest;
+
+    dest = efreet_string_append(dest, size, len, "--icon ");
+    if (!dest) return NULL;
+    dest = efreet_desktop_command_append_quoted(dest, size, len, desktop->icon);
+    if (!dest) return NULL;
+
+    return dest;
+}
+
+/**
+ * @param command the Efreet_Desktop_Comand that this file is for
+ * @param file the filname as either an absolute path, relative path, or URI
+ */
+static Efreet_Desktop_Command_File *
+efreet_desktop_command_file_process(Efreet_Desktop_Command *command, const char *file)
+{
+    Efreet_Desktop_Command_File *f;
+    const char *uri, *base;
+    int nonlocal = 0;
+/*
+    DBG("FLAGS: %d, %d, %d, %d\n",
+        command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH ? 1 : 0,
+        command->flags & EFREET_DESKTOP_EXEC_FLAG_URI ? 1 : 0);
+*/
+    f = NEW(Efreet_Desktop_Command_File, 1);
+    if (!f) return NULL;
+
+    f->command = command;
+
+    /* handle uris */
+    if (!strncmp(file, "http://", 7) || !strncmp(file, "ftp://", 6))
+    {
+        uri = file;
+        base = ecore_file_file_get(file);
+
+        nonlocal = 1;
+    }
+    else if (!strncmp(file, "file:", 5))
+    {
+        file = efreet_desktop_command_file_uri_process(file);
+        if (!file)
+        {
+            efreet_desktop_command_file_free(f);
+            return NULL;
+        }
+    }
+
+    if (nonlocal)
+    {
+        /* process non-local uri */
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
+        {
+            char buf[PATH_MAX];
+
+            snprintf(buf, sizeof(buf), "/tmp/%d-%d-%s", getpid(),
+                            efreet_desktop_command_file_id++, base);
+            f->fullpath = strdup(buf);
+            f->pending = 1;
+
+            ecore_file_download(uri, f->fullpath, efreet_desktop_cb_download_complete,
+                                            efreet_desktop_cb_download_progress, f, NULL);
+        }
+
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
+            f->uri = strdup(uri);
+    }
+    else
+    {
+        char *absol = efreet_desktop_command_path_absolute(file);
+        if (!absol) goto error;
+        /* process local uri/path */
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
+            f->fullpath = strdup(absol);
+
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
+        {
+            const char *buf;
+            Efreet_Uri ef_uri;
+            ef_uri.protocol = "file";
+            ef_uri.hostname = "";
+            ef_uri.path = absol;
+            buf = efreet_uri_encode(&ef_uri);
+
+            f->uri = strdup(buf);
+
+            eina_stringshare_del(buf);
+        }
+
+        free(absol);
+    }
+#if 0
+    INF("  fullpath: %s", f->fullpath);
+    INF("  uri: %s", f->uri);
+    INF("  dir: %s", f->dir);
+    INF("  file: %s", f->file);
+#endif
+    return f;
+error:
+    IF_FREE(f);
+    return NULL;
+}
+
+/**
+ * @brief Find the local path portion of a file uri.
+ * @param uri a uri beginning with "file"
+ * @return the location of the path portion of the uri,
+ * or NULL if the file is not on this machine
+ */
+static const char *
+efreet_desktop_command_file_uri_process(const char *uri)
+{
+    const char *path = NULL;
+    int len = strlen(uri);
+
+    /* uri:foo/bar => relative path foo/bar*/
+    if (len >= 4 && uri[5] != '/')
+        path = uri + strlen("file:");
+
+    /* uri:/foo/bar => absolute path /foo/bar */
+    else if (len >= 5 && uri[6] != '/')
+        path = uri + strlen("file:");
+
+    /* uri://foo/bar => absolute path /bar on machine foo */
+    else if (len >= 6 && uri[7] != '/')
+    {
+        char *tmp, *p;
+        char hostname[PATH_MAX];
+        size_t len2;
+
+        len2 = strlen(uri + 7) + 1;
+        tmp = alloca(len2);
+        memcpy(tmp, uri + 7, len2);
+        p = strchr(tmp, '/');
+        if (p)
+        {
+            *p = '\0';
+            if (!strcmp(tmp, "localhost"))
+                path = uri + strlen("file://localhost");
+            else
+            {
+                int ret;
+
+                ret = gethostname(hostname, PATH_MAX);
+                if ((ret == 0) && !strcmp(tmp, hostname))
+                    path = uri + strlen("file://") + strlen(hostname);
+            }
+        }
+    }
+
+    /* uri:///foo/bar => absolute path /foo/bar on local machine */
+    else if (len >= 7)
+        path = uri + strlen("file://");
+
+    return path;
+}
+
+static void
+efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file)
+{
+    if (!file) return;
+
+    IF_FREE(file->fullpath);
+    IF_FREE(file->uri);
+    IF_FREE(file->dir);
+    IF_FREE(file->file);
+
+    FREE(file);
+}
+
+
+static void
+efreet_desktop_cb_download_complete(void *data, const char *file __UNUSED__,
+                                                        int status __UNUSED__)
+{
+    Efreet_Desktop_Command_File *f;
+
+    f = data;
+
+    /* XXX check status... error handling, etc */
+    f->pending = 0;
+    f->command->num_pending--;
+
+    if (f->command->num_pending <= 0)
+    {
+        Eina_List *execs;
+
+        execs = efreet_desktop_command_build(f->command);
+        if (execs)
+        {
+            /* TODO: Need to handle the return value from efreet_desktop_command_execs_process */
+            efreet_desktop_command_execs_process(f->command, execs);
+            eina_list_free(execs);
+        }
+        efreet_desktop_command_free(f->command);
+    }
+}
+
+static int
+efreet_desktop_cb_download_progress(void *data,
+                                    const char *file __UNUSED__,
+                                    long int dltotal, long int dlnow,
+                                    long int ultotal __UNUSED__,
+                                    long int ulnow __UNUSED__)
+{
+    Efreet_Desktop_Command_File *dcf;
+
+    dcf = data;
+    if (dcf->command->cb_progress)
+        return dcf->command->cb_progress(dcf->command->data,
+                                        dcf->command->desktop,
+                                        dcf->uri, dltotal, dlnow);
+
+    return 0;
+}
+
+/**
+ * @brief Build an absolute path from an absolute or relative one.
+ * @param path an absolute or relative path
+ * @return an allocated absolute path (must be freed)
+ */
+static char *
+efreet_desktop_command_path_absolute(const char *path)
+{
+    char *buf;
+    int size = PATH_MAX;
+    int len = 0;
+
+    /* relative url */
+    if (path[0] != '/')
+    {
+        if (!(buf = malloc(size))) return NULL;
+        if (!getcwd(buf, size))
+        {
+            FREE(buf);
+            return NULL;
+        }
+        len = strlen(buf);
+
+        if (buf[len-1] != '/') buf = efreet_string_append(buf, &size, &len, "/");
+        if (!buf) return NULL;
+        buf = efreet_string_append(buf, &size, &len, path);
+        if (!buf) return NULL;
+
+        return buf;
+    }
+
+    /* just dup an already absolute buffer */
+    return strdup(path);
+}
+
+/**
+ * Append a string to a buffer, reallocating as necessary.
+ */
+static char *
+efreet_string_append(char *dest, int *size, int *len, const char *src)
+{
+    int l;
+    int off = 0;
+
+    l = eina_strlcpy(dest + *len, src, *size - *len);
+
+    while (l > *size - *len)
+    {
+        char *tmp;
+        /* we successfully appended this much */
+        off += *size - *len - 1;
+        *len = *size - 1;
+        *size += 1024;
+        tmp = realloc(dest, *size);
+        if (!tmp)
+        {
+            free(dest);
+            return NULL;
+        }
+        dest = tmp;
+        *(dest + *len) = '\0';
+
+        l = eina_strlcpy(dest + *len, src + off, *size - *len);
+    }
+    *len += l;
+
+    return dest;
+}
+
+static char *
+efreet_string_append_char(char *dest, int *size, int *len, char c)
+{
+    if (*len >= *size - 1)
+    {
+        char *tmp;
+        *size += 1024;
+        tmp = realloc(dest, *size);
+        if (!tmp)
+        {
+            free(dest);
+            return NULL;
+        }
+        dest = tmp;
+    }
+
+    dest[(*len)++] = c;
+    dest[*len] = '\0';
+
+    return dest;
+}
+
diff --git a/src/lib/efreet_icon.c b/src/lib/efreet_icon.c
new file mode 100644 (file)
index 0000000..526e0ec
--- /dev/null
@@ -0,0 +1,910 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_icon_log_dom
+static int _efreet_icon_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static const char *efreet_icon_deprecated_user_dir = NULL;
+static const char *efreet_icon_user_dir = NULL;
+static Eina_List *efreet_icon_extensions = NULL;
+static Eina_List *efreet_extra_icon_dirs = NULL;
+
+static Eina_Hash *change_monitors = NULL;
+
+typedef struct Efreet_Icon_Cache Efreet_Icon_Cache;
+struct Efreet_Icon_Cache
+{
+    const char *key;
+    const char *path;
+    time_t lasttime;
+};
+
+static char *efreet_icon_remove_extension(const char *icon);
+
+static Efreet_Icon *efreet_icon_new(const char *path);
+static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
+
+static const char *efreet_icon_lookup_icon(Efreet_Cache_Icon *icon, unsigned int size);
+static const char *efreet_icon_list_lookup_icon(Efreet_Icon_Theme *theme, Eina_List *icons, unsigned int size);
+static int efreet_icon_size_match(Efreet_Cache_Icon_Element *elem, unsigned int size);
+static double efreet_icon_size_distance(Efreet_Cache_Icon_Element *elem, unsigned int size);
+static const char *efreet_icon_lookup_path(Efreet_Cache_Icon_Element *elem);
+static const char *efreet_icon_lookup_path_path(Efreet_Cache_Icon_Element *elem, const char *path);
+static const char *efreet_icon_fallback_lookup_path(Efreet_Cache_Fallback_Icon *icon);
+static const char *efreet_icon_fallback_lookup_path_path(Efreet_Cache_Fallback_Icon *icon,
+                                                               const char *path);
+
+static void efreet_icon_changes_listen(void);
+static void efreet_icon_changes_monitor_add(const char *path);
+static void efreet_icon_changes_cb(void *data, Ecore_File_Monitor *em,
+                                   Ecore_File_Event event, const char *path);
+
+
+/**
+ * @internal
+ * @return Returns 1 on success or 0 on failure
+ * @brief Initializes the icon system
+ */
+int
+efreet_icon_init(void)
+{
+    const char *default_exts[] = {".png", ".xpm", ".svg", NULL};
+    int i;
+
+    _efreet_icon_log_dom = eina_log_domain_register
+        ("efreet_icon", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_icon_log_dom < 0)
+        return 0;
+
+    /* setup the default extension list */
+    for (i = 0; default_exts[i]; i++)
+        efreet_icon_extensions = eina_list_append(efreet_icon_extensions, eina_stringshare_add(default_exts[i]));
+
+    efreet_icon_changes_listen();
+
+    efreet_extra_icon_dirs = NULL;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Shuts down the icon system
+ */
+void
+efreet_icon_shutdown(void)
+{
+    IF_RELEASE(efreet_icon_user_dir);
+    IF_RELEASE(efreet_icon_deprecated_user_dir);
+
+    IF_FREE_LIST(efreet_icon_extensions, eina_stringshare_del);
+    efreet_extra_icon_dirs = eina_list_free(efreet_extra_icon_dirs);
+
+    eina_log_domain_unregister(_efreet_icon_log_dom);
+    _efreet_icon_log_dom = -1;
+    IF_FREE_HASH(change_monitors);
+}
+
+EAPI const char *
+efreet_icon_deprecated_user_dir_get(void)
+{
+    const char *user;
+    char *tmp;
+    int len;
+
+    if (efreet_icon_deprecated_user_dir) return efreet_icon_deprecated_user_dir;
+
+    user = efreet_home_dir_get();
+    len = strlen(user) + strlen("/.icons") + 1;
+    tmp = alloca(len);
+    snprintf(tmp, len, "%s/.icons", user);
+
+    efreet_icon_deprecated_user_dir = eina_stringshare_add_length(tmp, len - 1);
+
+    return efreet_icon_deprecated_user_dir;
+}
+
+EAPI const char *
+efreet_icon_user_dir_get(void)
+{
+    const char *user;
+    char *tmp;
+    int len;
+
+    if (efreet_icon_user_dir) return efreet_icon_user_dir;
+
+    user = efreet_data_home_get();
+    len = strlen(user) + strlen("/icons") + 1;
+    tmp = alloca(len);
+    snprintf(tmp, len, "%s/icons", user);
+
+    efreet_icon_user_dir = eina_stringshare_add_length(tmp, len - 1);
+
+    return efreet_icon_user_dir;
+}
+
+EAPI void
+efreet_icon_extension_add(const char *ext)
+{
+    Eina_List *l;
+
+    EINA_SAFETY_ON_NULL_RETURN(ext);
+
+    ext = eina_stringshare_add(ext);
+
+    if ((l = eina_list_data_find_list(efreet_icon_extensions, ext)))
+    {
+        efreet_icon_extensions = eina_list_promote_list(efreet_icon_extensions, l);
+        eina_stringshare_del(ext);
+    }
+    else
+        efreet_icon_extensions = eina_list_prepend(efreet_icon_extensions, ext);
+}
+
+EAPI Eina_List **
+efreet_icon_extra_list_get(void)
+{
+    return &efreet_extra_icon_dirs;
+}
+
+EAPI Eina_List *
+efreet_icon_extensions_list_get(void)
+{
+    return efreet_icon_extensions;
+}
+
+EAPI Eina_List *
+efreet_icon_theme_list_get(void)
+{
+    return efreet_cache_icon_theme_list();
+}
+
+EAPI Efreet_Icon_Theme *
+efreet_icon_theme_find(const char *theme_name)
+{
+    if (!theme_name) return NULL;
+
+    return efreet_cache_icon_theme_find(theme_name);
+}
+
+/**
+ * @internal
+ * @param icon The icon name to strip extension
+ * @return Extension removed if in list of extensions, else untouched.
+ * @brief Removes extension from icon name if in list of extensions.
+ */
+static char *
+efreet_icon_remove_extension(const char *icon)
+{
+    Eina_List *l;
+    char *tmp = NULL, *ext = NULL;
+
+    if (!icon) return NULL;
+
+    tmp = strdup(icon);
+    ext = strrchr(tmp, '.');
+    if (ext)
+    {
+        const char *ext2;
+        EINA_LIST_FOREACH(efreet_icon_extensions, l, ext2)
+        {
+            if (!strcmp(ext, ext2))
+            {
+#ifdef STRICT_SPEC
+                WRN("[Efreet]: Requesting an icon with an extension: %s",
+                    icon);
+#endif
+                *ext = '\0';
+                break;
+            }
+        }
+    }
+
+    return tmp;
+}
+
+EAPI const char *
+efreet_icon_path_find(const char *theme_name, const char *icon, unsigned int size)
+{
+    char *tmp;
+    const char *value = NULL;
+    Efreet_Icon_Theme *theme;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(icon, NULL);
+
+    theme = efreet_icon_theme_find(theme_name);
+
+#ifdef SLOPPY_SPEC
+    tmp = efreet_icon_remove_extension(icon);
+    if (!tmp) return NULL;
+#else
+    tmp = icon;
+#endif
+
+    if (theme)
+    {
+        Efreet_Cache_Icon *cache;
+        cache = efreet_cache_icon_find(theme, tmp);
+        value = efreet_icon_lookup_icon(cache, size);
+        if (!value) INF("lookup for `%s` failed in theme `%s` with %p.", icon, theme_name, cache);
+    }
+
+    /* we didn't find the icon in the theme or in the inherited directories
+     * then just look for a non theme icon
+     */
+    if (!value)
+    {
+        Efreet_Cache_Fallback_Icon *cache;
+
+        cache = efreet_cache_icon_fallback_find(tmp);
+        value = efreet_icon_fallback_lookup_path(cache);
+        if (!value) INF("lookup for `%s` failed in fallback too with %p.", icon, cache);
+    }
+
+#ifdef SLOPPY_SPEC
+    FREE(tmp);
+#endif
+    return value;
+}
+
+EAPI const char *
+efreet_icon_list_find(const char *theme_name, Eina_List *icons,
+                      unsigned int size)
+{
+    Eina_List *l;
+    Eina_List *tmps = NULL;
+    const char *icon = NULL;
+    const char *value = NULL;
+    char *data;
+    Efreet_Icon_Theme *theme;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(icons, NULL);
+
+    theme = efreet_icon_theme_find(theme_name);
+
+#ifdef SLOPPY_SPEC
+    EINA_LIST_FOREACH(icons, l, icon)
+    {
+        data = efreet_icon_remove_extension(icon);
+        if (!data) return NULL;
+        tmps = eina_list_append(tmps, data);
+    }
+#else
+    tmps = icons;
+#endif
+
+    if (theme)
+    {
+        Eina_List *tmps2 = NULL;
+        Efreet_Cache_Icon *cache;
+
+        EINA_LIST_FOREACH(tmps, l, icon)
+        {
+            cache = efreet_cache_icon_find(theme, icon);
+            if (cache)
+            {
+                /* If the icon is in the asked for theme, return it */
+                if (!strcmp(cache->theme, theme->name.internal))
+                {
+                    value = efreet_icon_lookup_icon(cache, size);
+                    break;
+                }
+                else
+                    tmps2 = eina_list_append(tmps2, cache);
+            }
+        }
+        if (tmps2)
+        {
+            if (!value)
+                value = efreet_icon_list_lookup_icon(theme, tmps2, size);
+            eina_list_free(tmps2);
+        }
+    }
+
+    /* we didn't find the icons in the theme or in the inherited directories
+     * then just look for a non theme icon
+     */
+    if (!value)
+    {
+        Efreet_Cache_Fallback_Icon *cache;
+        EINA_LIST_FOREACH(tmps, l, icon)
+        {
+            cache = efreet_cache_icon_fallback_find(icon);
+            value = efreet_icon_fallback_lookup_path(cache);
+            if (value)
+                break;
+        }
+    }
+
+#ifdef SLOPPY_SPEC
+    EINA_LIST_FREE(tmps, data)
+        free(data);
+#endif
+
+    return value;
+}
+
+EAPI Efreet_Icon *
+efreet_icon_find(const char *theme_name, const char *icon, unsigned int size)
+{
+    const char *path;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(icon, NULL);
+
+    path = efreet_icon_path_find(theme_name, icon, size);
+    if (path)
+    {
+        Efreet_Icon *ic;
+
+        ic = efreet_icon_new(path);
+        return ic;
+    }
+
+    return NULL;
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Icon struct on success or NULL on failure
+ * @brief Creates a new Efreet_Icon struct
+ */
+static Efreet_Icon *
+efreet_icon_new(const char *path)
+{
+    Efreet_Icon *icon;
+    char *p;
+
+    icon = NEW(Efreet_Icon, 1);
+    if (!icon) return NULL;
+    icon->path = eina_stringshare_add(path);
+
+    /* load the .icon file if it's available */
+    p = strrchr(icon->path, '.');
+    if (p)
+    {
+        char ico_path[PATH_MAX];
+
+        *p = '\0';
+
+        snprintf(ico_path, sizeof(ico_path), "%s.icon", icon->path);
+        *p = '.';
+
+        if (ecore_file_exists(ico_path))
+            efreet_icon_populate(icon, ico_path);
+    }
+
+    if (!icon->name)
+    {
+        const char *file;
+
+        file = ecore_file_file_get(icon->path);
+        p = strrchr(icon->path, '.');
+        if (p) *p = '\0';
+        icon->name = eina_stringshare_add(file);
+        if (p) *p = '.';
+    }
+
+    return icon;
+}
+
+EAPI void
+efreet_icon_free(Efreet_Icon *icon)
+{
+    if (!icon) return;
+
+    icon->ref_count --;
+    if (icon->ref_count > 0) return;
+
+    IF_RELEASE(icon->path);
+    IF_RELEASE(icon->name);
+    IF_FREE_LIST(icon->attach_points, free);
+
+    FREE(icon);
+}
+
+/**
+ * @internal
+ * @param icon The icon to populate
+ * @param file The file to populate from
+ * @return Returns no value
+ * @brief Tries to populate the icon information from the given file
+ */
+static void
+efreet_icon_populate(Efreet_Icon *icon, const char *file)
+{
+    Efreet_Ini *ini;
+    const char *tmp;
+
+    ini = efreet_ini_new(file);
+    if (!ini) return;
+    if (!ini->data)
+    {
+        efreet_ini_free(ini);
+        return;
+    }
+
+    efreet_ini_section_set(ini, "Icon Data");
+    tmp = efreet_ini_localestring_get(ini, "DisplayName");
+    if (tmp) icon->name = eina_stringshare_add(tmp);
+
+    tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
+    if (tmp)
+    {
+        int points[4];
+        char *t, *s, *p;
+        int i;
+        size_t len;
+
+        len = strlen(tmp) + 1;
+        t = alloca(len);
+        memcpy(t, tmp, len);
+        s = t;
+        for (i = 0; i < 4; i++)
+        {
+            if (s)
+            {
+                p = strchr(s, ',');
+
+                if (p) *p = '\0';
+                points[i] = atoi(s);
+
+                if (p) s = ++p;
+                else s = NULL;
+            }
+            else
+            {
+                points[i] = 0;
+            }
+        }
+
+        icon->has_embedded_text_rectangle = 1;
+        icon->embedded_text_rectangle.x0 = points[0];
+        icon->embedded_text_rectangle.y0 = points[1];
+        icon->embedded_text_rectangle.x1 = points[2];
+        icon->embedded_text_rectangle.y1 = points[3];
+    }
+
+    tmp = efreet_ini_string_get(ini, "AttachPoints");
+    if (tmp)
+    {
+        char *t, *s, *p;
+        size_t len;
+
+        len = strlen(tmp) + 1;
+        t = alloca(len);
+        memcpy(t, tmp, len);
+        s = t;
+        while (s)
+        {
+            Efreet_Icon_Point *point;
+
+            p = strchr(s, ',');
+            /* If this happens there is something wrong with the .icon file */
+            if (!p) break;
+
+            point = NEW(Efreet_Icon_Point, 1);
+            if (!point) goto error;
+
+            *p = '\0';
+            point->x = atoi(s);
+
+            s = ++p;
+            p = strchr(s, '|');
+            if (p) *p = '\0';
+
+            point->y = atoi(s);
+
+            icon->attach_points = eina_list_append(icon->attach_points, point);
+
+            if (p) s = ++p;
+            else s = NULL;
+        }
+    }
+
+error:
+    efreet_ini_free(ini);
+}
+
+static const char *
+efreet_icon_lookup_icon(Efreet_Cache_Icon *icon, unsigned int size)
+{
+    const char *path = NULL;
+    double minimal_distance = INT_MAX;
+    unsigned int ret_size = 0;
+    unsigned int i;
+
+    if (!icon) return NULL;
+
+    /* search for allowed size == requested size */
+    for (i = 0; i < icon->icons_count; ++i)
+    {
+        if (!efreet_icon_size_match(icon->icons[i], size)) continue;
+        path = efreet_icon_lookup_path(icon->icons[i]);
+        if (path) return path;
+    }
+
+    /* search for any icon that matches */
+    for (i = 0; i < icon->icons_count; ++i)
+    {
+        const char *tmp = NULL;
+        double distance;
+
+        distance = efreet_icon_size_distance(icon->icons[i], size);
+        if (distance > minimal_distance) continue;
+        // prefer downsizing
+        if ((distance == minimal_distance) && (icon->icons[i]->normal < ret_size)) continue;
+
+        tmp = efreet_icon_lookup_path(icon->icons[i]);
+
+        if (tmp)
+        {
+            path = tmp;
+            minimal_distance = distance;
+            ret_size = icon->icons[i]->normal;
+        }
+    }
+
+    return path;
+}
+
+static const char *
+efreet_icon_list_lookup_icon(Efreet_Icon_Theme *theme, Eina_List *icons, unsigned int size)
+{
+    const char *value = NULL;
+    Efreet_Cache_Icon *cache;
+    Eina_List *l;
+
+    EINA_LIST_FOREACH(icons, l, cache)
+    {
+        if (!strcmp(cache->theme, theme->name.internal))
+        {
+            value = efreet_icon_lookup_icon(cache, size);
+            if (value) break;
+        }
+    }
+    if (value) return value;
+    if (theme->inherits)
+    {
+        const char *parent;
+        EINA_LIST_FOREACH(theme->inherits, l, parent)
+        {
+            Efreet_Icon_Theme *parent_theme;
+
+            parent_theme = efreet_icon_theme_find(parent);
+            if ((!parent_theme) || (parent_theme == theme)) continue;
+
+            value = efreet_icon_list_lookup_icon(parent_theme, icons, size);
+            if (value) break;
+        }
+    }
+    /* if this isn't the hicolor theme, and we have no other fallbacks
+     * check hicolor */
+    else if (strcmp(theme->name.internal, "hicolor"))
+    {
+        Efreet_Icon_Theme *parent_theme;
+
+        parent_theme = efreet_icon_theme_find("hicolor");
+        if (parent_theme)
+            value = efreet_icon_list_lookup_icon(parent_theme, icons, size);
+    }
+    return value;
+}
+
+static int
+efreet_icon_size_match(Efreet_Cache_Icon_Element *elem, unsigned int size)
+{
+    if (elem->type == EFREET_ICON_SIZE_TYPE_FIXED)
+        return (elem->normal == size);
+
+    if ((elem->type == EFREET_ICON_SIZE_TYPE_SCALABLE) ||
+        (elem->type == EFREET_ICON_SIZE_TYPE_THRESHOLD))
+        return ((elem->min < size) && (size < elem->max));
+
+    return 0;
+}
+
+static double
+efreet_icon_size_distance(Efreet_Cache_Icon_Element *elem, unsigned int size)
+{
+    if (elem->type == EFREET_ICON_SIZE_TYPE_FIXED)
+        return (abs(elem->normal - size));
+
+    if ((elem->type == EFREET_ICON_SIZE_TYPE_SCALABLE) ||
+        (elem->type == EFREET_ICON_SIZE_TYPE_THRESHOLD))
+    {
+#ifdef STRICT_SPEC
+        if (size < elem->min)
+            return (elem->min - size);
+        if (elem->max < size)
+            return (size - elem->max);
+#else
+        if (size < elem->min)
+            return (elem->min / (double)size);
+        if (elem->max < size)
+            return (size / (double)elem->max);
+#endif
+    }
+
+    return 0;
+}
+
+static const char *
+efreet_icon_lookup_path(Efreet_Cache_Icon_Element *elem)
+{
+    Eina_List *xdg_dirs, *l;
+    const char *path;
+    const char *dir;
+    char buf[PATH_MAX];
+
+    if (elem->paths_count == 1)
+    {
+        const char *pp, *ext;
+
+        pp = strrchr(elem->paths[0], '.');
+        if (!pp) return NULL;
+
+        EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
+            if (!strcmp(pp, ext))
+                return elem->paths[0];
+        return NULL;
+    }
+
+    path = efreet_icon_lookup_path_path(elem, efreet_icon_deprecated_user_dir_get());
+    if (path) return path;
+
+    path = efreet_icon_lookup_path_path(elem, efreet_icon_user_dir_get());
+    if (path) return path;
+
+#if 0
+    EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
+    {
+        path = efreet_icon_lookup_path_path(elem, dir);
+        if (path) return path;
+    }
+#endif
+
+    xdg_dirs = efreet_data_dirs_get();
+
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/icons", dir);
+
+        path = efreet_icon_lookup_path_path(elem, buf);
+        if (path) return path;
+    }
+
+    return NULL;
+}
+
+static const char *
+efreet_icon_lookup_path_path(Efreet_Cache_Icon_Element *elem, const char *path)
+{
+    Eina_List *ll;
+    const char *ext, *pp;
+    unsigned int i;
+    int len;
+
+    len = strlen(path);
+
+    for (i = 0; i < elem->paths_count; ++i)
+    {
+        if (strncmp(path, elem->paths[i], len)) continue;
+        pp = strrchr(elem->paths[i], '.');
+        if (!pp) continue;
+
+        EINA_LIST_FOREACH(efreet_icon_extensions, ll, ext)
+            if (!strcmp(pp, ext))
+                return elem->paths[i];
+    }
+
+    return NULL;
+}
+
+static const char *
+efreet_icon_fallback_lookup_path(Efreet_Cache_Fallback_Icon *icon)
+{
+    const char *path;
+    Eina_List *xdg_dirs, *l;
+    const char *dir;
+    char buf[PATH_MAX];
+
+    if (!icon) return NULL;
+
+    if (icon->icons_count == 1)
+    {
+        const char *pp, *ext;
+
+        pp = strrchr(icon->icons[0], '.');
+        if (!pp) return NULL;
+
+        EINA_LIST_FOREACH(efreet_icon_extensions, l, ext)
+            if (!strcmp(pp, ext))
+                return icon->icons[0];
+        return NULL;
+    }
+
+    path = efreet_icon_fallback_lookup_path_path(icon, efreet_icon_deprecated_user_dir_get());
+    if (path) return path;
+
+    path = efreet_icon_fallback_lookup_path_path(icon, efreet_icon_user_dir_get());
+    if (path) return path;
+
+    EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
+    {
+        path = efreet_icon_fallback_lookup_path_path(icon, dir);
+        if (path) return path;
+    }
+
+    xdg_dirs = efreet_data_dirs_get();
+
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/icons", dir);
+
+        path = efreet_icon_fallback_lookup_path_path(icon, buf);
+        if (path) return path;
+    }
+
+#ifndef STRICT_SPEC
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/pixmaps", dir);
+
+        path = efreet_icon_fallback_lookup_path_path(icon, buf);
+        if (path) return path;
+    }
+#endif
+
+    path = efreet_icon_fallback_lookup_path_path(icon, "/usr/share/pixmaps");
+    if (path) return path;
+
+    return NULL;
+}
+
+static const char *
+efreet_icon_fallback_lookup_path_path(Efreet_Cache_Fallback_Icon *icon, const char *path)
+{
+    Eina_List *ll;
+    const char *ext, *pp;
+    unsigned int i;
+    int len;
+
+    len = strlen(path);
+
+    for (i = 0; i < icon->icons_count; ++i)
+    {
+        if (strncmp(path, icon->icons[i], len)) continue;
+
+        pp = strrchr(icon->icons[i], '.');
+        if (!pp) continue;
+
+        EINA_LIST_FOREACH(efreet_icon_extensions, ll, ext)
+            if (!strcmp(pp, ext))
+                return icon->icons[i];
+    }
+
+    return NULL;
+}
+
+static void
+efreet_icon_changes_listen(void)
+{
+    Eina_List *l;
+    Eina_List *xdg_dirs;
+    char buf[PATH_MAX];
+    const char *dir;
+
+    if (!efreet_cache_update) return;
+
+    change_monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
+    if (!change_monitors) return;
+
+    efreet_icon_changes_monitor_add(efreet_icon_deprecated_user_dir_get());
+    efreet_icon_changes_monitor_add(efreet_icon_user_dir_get());
+    EINA_LIST_FOREACH(efreet_extra_icon_dirs, l, dir)
+        efreet_icon_changes_monitor_add(dir);
+
+    xdg_dirs = efreet_data_dirs_get();
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/icons", dir);
+        efreet_icon_changes_monitor_add(buf);
+    }
+
+#ifndef STRICT_SPEC
+    EINA_LIST_FOREACH(xdg_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/pixmaps", dir);
+        efreet_icon_changes_monitor_add(buf);
+    }
+#endif
+
+    efreet_icon_changes_monitor_add("/usr/share/pixmaps");
+}
+
+static void
+efreet_icon_changes_monitor_add(const char *path)
+{
+    char rp[PATH_MAX];
+
+    if (!realpath(path, rp)) return;
+    if (!ecore_file_is_dir(rp)) return;
+    if (eina_hash_find(change_monitors, rp)) return;
+    eina_hash_add(change_monitors, rp,
+                  ecore_file_monitor_add(rp,
+                                         efreet_icon_changes_cb,
+                                         NULL));
+
+    if (ecore_file_is_dir(rp))
+    {
+        Eina_Iterator *it;
+        const char *ent;
+
+        it = eina_file_ls(rp);
+        if (!it) return;
+        EINA_ITERATOR_FOREACH(it, ent)
+        {
+            if (!realpath(ent, rp)) continue;
+            if (!ecore_file_is_dir(rp)) continue;
+            eina_hash_add(change_monitors, rp,
+                          ecore_file_monitor_add(rp,
+                                                 efreet_icon_changes_cb,
+                                                 NULL));
+        }
+        eina_iterator_free(it);
+    }
+}
+
+static void
+efreet_icon_changes_cb(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__,
+                       Ecore_File_Event event, const char *path)
+{
+    /* TODO: If we get a stale symlink, we need to rerun cache creation */
+    switch (event)
+    {
+        case ECORE_FILE_EVENT_NONE:
+            /* noop */
+            break;
+
+        case ECORE_FILE_EVENT_CREATED_FILE:
+        case ECORE_FILE_EVENT_DELETED_FILE:
+        case ECORE_FILE_EVENT_MODIFIED:
+        case ECORE_FILE_EVENT_CLOSED:
+        case ECORE_FILE_EVENT_DELETED_DIRECTORY:
+        case ECORE_FILE_EVENT_CREATED_DIRECTORY:
+            efreet_cache_icon_update();
+            break;
+
+        case ECORE_FILE_EVENT_DELETED_SELF:
+            eina_hash_del_by_key(change_monitors, path);
+            efreet_cache_icon_update();
+            break;
+    }
+}
diff --git a/src/lib/efreet_icon.h b/src/lib/efreet_icon.h
new file mode 100644 (file)
index 0000000..e6454ad
--- /dev/null
@@ -0,0 +1,249 @@
+#ifndef EFREET_ICON_H
+#define EFREET_ICON_H
+
+/**
+ * @file efreet_icon.h
+ * @brief Contains the structures and methods used to support the FDO icon
+ * theme specificiation.
+ * @addtogroup Efreet_Icon Efreet_Icon: The FDO Icon Theme
+ *                  Specification functions and structures
+ *
+ * @{
+ */
+
+
+/**
+ * Event id for cache update.
+ */
+EAPI extern int EFREET_EVENT_ICON_CACHE_UPDATE;
+
+/**
+ * The possible contexts for an icon directory
+ */
+typedef enum Efreet_Icon_Theme_Context
+{
+    EFREET_ICON_THEME_CONTEXT_NONE,
+    EFREET_ICON_THEME_CONTEXT_ACTIONS,
+    EFREET_ICON_THEME_CONTEXT_DEVICES,
+    EFREET_ICON_THEME_CONTEXT_FILESYSTEMS,
+    EFREET_ICON_THEME_CONTEXT_MIMETYPES
+} Efreet_Icon_Theme_Context;
+
+/**
+ * The possible size types for an icon directory
+ */
+typedef enum Efreet_Icon_Size_Type
+{
+    EFREET_ICON_SIZE_TYPE_NONE,
+    EFREET_ICON_SIZE_TYPE_FIXED,
+    EFREET_ICON_SIZE_TYPE_SCALABLE,
+    EFREET_ICON_SIZE_TYPE_THRESHOLD
+} Efreet_Icon_Size_Type;
+
+/**
+ * Efreet_Icon_Theme
+ */
+typedef struct Efreet_Icon_Theme Efreet_Icon_Theme;
+
+/**
+ * Efreet_Icon_Theme
+ * @brief contains all of the known information about a given theme
+ */
+struct Efreet_Icon_Theme
+{
+    struct
+    {
+        const char *internal;   /**< The internal theme name */
+        const char *name;       /**< The user visible name */
+    } name;                     /**< The different names for the theme */
+
+    const char *comment;        /**< String describing the theme */
+    const char *example_icon;   /**< Icon to use as an example of the theme */
+
+    /* An icon theme can have multiple directories that store it's icons. We
+     * need to be able to find a search each one. */
+
+    Eina_List *paths;           /**< The paths */
+    Eina_List *inherits;        /**< Icon themes we inherit from */
+    Eina_List *directories;     /**< List of subdirectories for this theme */
+};
+
+/**
+ * Efreet_Icon_Theme_Directory
+ */
+typedef struct Efreet_Icon_Theme_Directory Efreet_Icon_Theme_Directory;
+
+/**
+ * Efreet_Icon_Theme_Directory
+ * @brief Contains all the information about a sub-directory of a theme
+ */
+struct Efreet_Icon_Theme_Directory
+{
+    const char *name;               /**< The directory name */
+    Efreet_Icon_Theme_Context context;  /**< The type of icons in the dir */
+    Efreet_Icon_Size_Type type;     /**< The size type for the icons */
+
+    struct
+    {
+        unsigned int normal;        /**< The size for this directory */
+        unsigned int min;           /**< The minimum size for this directory */
+        unsigned int max;           /**< The maximum size for this directory */
+        unsigned int threshold;     /**< Size difference threshold */
+    } size;                         /**< The size settings for the icon theme */
+};
+
+/**
+ * Efreet_Icon
+ */
+typedef struct Efreet_Icon Efreet_Icon;
+
+/**
+ * Efreet_Icon
+ * @brief Contains all the information about a given icon
+ */
+struct Efreet_Icon
+{
+    const char *path;       /**< Full path to the icon */
+    const char *name;       /**< Translated UTF8 string that can
+                                    be used for the icon name */
+
+    struct
+    {
+        int x0,             /**< x0 position */
+            y0,             /**< y0 position */
+            x1,             /**< x1 position */
+            y1;             /**< y1 position */
+    } embedded_text_rectangle;  /**< Rectangle where text can
+                                        be displayed on the icon */
+
+    Eina_List *attach_points; /**< List of points to be used as anchor
+                                        points for emblems/overlays */
+
+    unsigned int ref_count;    /**< References to this icon */
+    unsigned char has_embedded_text_rectangle:1; /**< Has the embedded
+                                                        rectangle set */
+};
+
+/**
+ * Efreet_Icon_Point
+ */
+typedef struct Efreet_Icon_Point Efreet_Icon_Point;
+
+/**
+ * Efreet_Icon_Point
+ * @brief Stores an x, y point.
+ */
+struct Efreet_Icon_Point
+{
+    int x;          /**< x coord */
+    int y;          /**< y coord */
+};
+
+/**
+ * @return Returns the user icon directory
+ * @brief Returns the user icon directory
+ */
+EAPI const char        *efreet_icon_user_dir_get(void);
+
+/**
+ * @return Returns the deprecated user icon directory
+ * @brief Returns the deprecated user icon directory
+ */
+EAPI const char        *efreet_icon_deprecated_user_dir_get(void);
+
+/**
+ * @param ext The extension to add to the list of checked extensions
+ * @return Returns no value.
+ * @brief Adds the given extension to the list of possible icon extensions
+ */
+EAPI void               efreet_icon_extension_add(const char *ext);
+
+
+/**
+ * @return Returns a list of strings that are paths to other icon directories
+ * @brief Gets the list of all extra directories to look for icons. These
+ * directories are used to look for icons after looking in the user icon dir
+ * and before looking in standard system directories. The order of search is
+ * from first to last directory in this list. the strings in the list should
+ * be created with eina_stringshare_add().
+ */
+EAPI Eina_List        **efreet_icon_extra_list_get(void);
+
+/**
+ * @return Returns a list of strings that are icon extensions to look for
+ * @brief Gets the list of all icon extensions to look for
+ */
+EAPI Eina_List         *efreet_icon_extensions_list_get(void);
+
+/**
+ * @return Returns a list of Efreet_Icon structs for all the non-hidden icon
+ * themes
+ * @brief Retrieves all of the non-hidden icon themes available on the system.
+ * The returned list must be freed. Do not free the list data.
+ */
+EAPI Eina_List         *efreet_icon_theme_list_get(void);
+
+/**
+ * @param theme_name The theme to look for
+ * @return Returns the icon theme related to the given theme name or NULL if
+ * none exists.
+ * @brief Tries to get the icon theme structure for the given theme name
+ */
+EAPI Efreet_Icon_Theme *efreet_icon_theme_find(const char *theme_name);
+
+/**
+ * @param theme_name The icon theme to look for
+ * @param icon The icon to look for
+ * @param size The icon size to look for
+ * @return Returns the Efreet_Icon structure representing this icon or NULL
+ * if the icon is not found
+ * @brief Retrieves all of the information about the given icon.
+ */
+EAPI Efreet_Icon       *efreet_icon_find(const char *theme_name,
+                                            const char *icon,
+                                            unsigned int size);
+
+/**
+ * @param theme_name The icon theme to look for
+ * @param icons List of icons to look for
+ * @param size; The icon size to look for
+ * @return Returns the path representing first found icon or
+ * NULL if none of the icons are found
+ * @brief Retrieves all of the information about the first found icon in
+ * the list.
+ * @note This function will search the given theme for all icons before falling
+ * back. This is useful when searching for mimetype icons.
+ *
+ * There is no guarantee for how long the pointer to the path will be valid.
+ * If the pointer is to be kept, the user must create a copy of the path.
+ */
+EAPI const char        *efreet_icon_list_find(const char *theme_name,
+                                                Eina_List *icons,
+                                                unsigned int size);
+
+/**
+ * @param theme_name The icon theme to look for
+ * @param icon The icon to look for
+ * @param size; The icon size to look for
+ * @return Returns the path to the given icon or NULL if none found
+ * @brief Retrives the path to the given icon.
+ *
+ * There is no guarantee for how long the pointer to the path will be valid.
+ * If the pointer is to be kept, the user must create a copy of the path.
+ */
+EAPI const char        *efreet_icon_path_find(const char *theme_name,
+                                                const char *icon,
+                                                unsigned int size);
+
+/**
+ * @param icon The Efreet_Icon to cleanup
+ * @return Returns no value.
+ * @brief Free's the given icon and all its internal data.
+ */
+EAPI void               efreet_icon_free(Efreet_Icon *icon);
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_ini.c b/src/lib/efreet_ini.c
new file mode 100644 (file)
index 0000000..375fda1
--- /dev/null
@@ -0,0 +1,629 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <ctype.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_ini_log_dom
+static int _efreet_ini_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static Eina_Hash *efreet_ini_parse(const char *file);
+static const char *efreet_ini_unescape(const char *str) EINA_ARG_NONNULL(1);
+static Eina_Bool
+efreet_ini_section_save(const Eina_Hash *hash, const void *key, void *data, void *fdata);
+static Eina_Bool
+efreet_ini_value_save(const Eina_Hash *hash, const void *key, void *data, void *fdata);
+
+/**
+ * @internal
+ * @return Returns > 0 on success or 0 on failure
+ * @brief Initialize the Ini parser subsystem
+ */
+int
+efreet_ini_init(void)
+{
+    _efreet_ini_log_dom = eina_log_domain_register
+      ("efreet_ini", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_ini_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_ini");
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * @internal
+ * @returns the number of initializations left for this system
+ * @brief Attempts to shut down the subsystem if nothing else is using it
+ */
+void
+efreet_ini_shutdown(void)
+{
+    eina_log_domain_unregister(_efreet_ini_log_dom);
+    _efreet_ini_log_dom = -1;
+}
+
+EAPI Efreet_Ini *
+efreet_ini_new(const char *file)
+{
+    Efreet_Ini *ini;
+
+    ini = NEW(Efreet_Ini, 1);
+    if (!ini) return NULL;
+
+    /* This can validly be NULL at the moment as _parse() will return NULL
+     * if the input file doesn't exist. Should we change _parse() to create
+     * the hash and only return NULL on failed parse? */
+    ini->data = efreet_ini_parse(file);
+
+    return ini;
+}
+
+/**
+ * @internal
+ * @param file The file to parse
+ * @return Returns an Eina_Hash with the contents of @a file, or NULL if the
+ *         file fails to parse or if the file doesn't exist
+ * @brief Parses the ini file @a file into an Eina_Hash
+ */
+static Eina_Hash *
+efreet_ini_parse(const char *file)
+{
+    const char *buffer, *line_start;
+    FILE *f;
+    Eina_Hash *data, *section = NULL;
+    struct stat file_stat;
+    int line_length, left;
+
+    if (!file) return NULL;
+
+    f = fopen(file, "rb");
+    if (!f) return NULL;
+
+    if (fstat(fileno(f), &file_stat) || (file_stat.st_size < 1))
+    {
+        fclose(f);
+        return NULL;
+    }
+    if (!S_ISREG(file_stat.st_mode)) /* if not a regular file - close */
+    {
+        fclose(f);
+        return NULL;
+    }
+
+    left = file_stat.st_size;
+    /* let's make mmap safe and just get 0 pages for IO erro */
+    eina_mmap_safety_enabled_set(EINA_TRUE);
+
+    buffer = mmap(NULL, left, PROT_READ, MAP_SHARED, fileno(f), 0);
+    if (buffer == MAP_FAILED)
+    {
+        fclose(f);
+        return NULL;
+    }
+
+    data = eina_hash_string_small_new(EINA_FREE_CB(eina_hash_free));
+
+    line_start = buffer;
+    while (left > 0)
+    {
+        int sep;
+
+        /* find the end of line */
+        for (line_length = 0;
+                (line_length < left) &&
+                (line_start[line_length] != '\n'); line_length++)
+            ;
+
+        /* check for all white space */
+        while (isspace(line_start[0]) && (line_length > 0))
+        {
+            line_start++;
+            line_length--;
+        }
+
+        /* skip empty lines and comments */
+        if ((line_length == 0) || (line_start[0] == '\r') ||
+                (line_start[0] == '\n') || (line_start[0] == '#') ||
+                (line_start[0] == '\0'))
+            goto next_line;
+
+        /* new section */
+        if (line_start[0] == '[')
+        {
+            int header_length;
+
+            /* find the ']' */
+            for (header_length = 1;
+                    (header_length < line_length) &&
+                    (line_start[header_length] != ']'); ++header_length)
+                ;
+
+            if (line_start[header_length] == ']')
+            {
+                const char *header;
+
+                header = alloca(header_length * sizeof(unsigned char));
+                if (!header) goto next_line;
+
+                memcpy((char*)header, line_start + 1, header_length - 1);
+                ((char*)header)[header_length - 1] = '\0';
+
+                section = eina_hash_string_small_new(EINA_FREE_CB(eina_stringshare_del));
+
+                eina_hash_del_by_key(data, header);
+//                if (old) INF("[efreet] Warning: duplicate section '%s' "
+  //                              "in file '%s'", header, file);
+
+                eina_hash_add(data, header, section);
+            }
+            else
+            {
+                /* invalid file - skip line? or refuse to parse file? */
+                /* just printf for now till we figure out what to do */
+//                ERR("Invalid file (%s) (missing ] on group name)", file);
+            }
+            goto next_line;
+        }
+
+        if (!section)
+        {
+            INF("Invalid file (%s) (missing section)", file);
+            goto error;
+        }
+
+        /* find for '=' */
+        for (sep = 0; (sep < line_length) && (line_start[sep] != '='); ++sep)
+            ;
+
+        if (sep < line_length && line_start[sep] == '=')
+        {
+            char *key, *value;
+            int key_end, value_start, value_end;
+
+            /* trim whitespace from end of key */
+            for (key_end = sep - 1;
+                    (key_end > 0) && isspace(line_start[key_end]); --key_end)
+                ;
+
+            if (!isspace(line_start[key_end])) key_end++;
+
+            /* trim whitespace from start of value */
+            for (value_start = sep + 1;
+                 (value_start < line_length) &&
+                 isspace(line_start[value_start]); ++value_start)
+                ;
+
+            /* trim \n off of end of value */
+            for (value_end = line_length;
+                 (value_end > value_start) &&
+                 ((line_start[value_end] == '\n') ||
+                  (line_start[value_end] == '\r')); --value_end)
+                ;
+
+            if (line_start[value_end] != '\n'
+                    && line_start[value_end] != '\r'
+                    && value_end < line_length)
+                value_end++;
+
+            /* make sure we have a key. blank values are allowed */
+            if (key_end <= 0)
+            {
+                /* invalid file... */
+//                INF("Invalid file (%s) (invalid key=value pair)", file);
+
+                goto next_line;
+            }
+
+            key = alloca(key_end + 1);
+            value = alloca(value_end - value_start + 1);
+            if (!key || !value) goto next_line;
+
+            memcpy(key, line_start, key_end);
+            key[key_end] = '\0';
+
+            memcpy(value, line_start + value_start,
+                    value_end - value_start);
+            value[value_end - value_start] = '\0';
+
+            eina_hash_del_by_key(section, key);
+            eina_hash_add(section, key, efreet_ini_unescape(value));
+        }
+        else
+        {
+            /* invalid file... */
+            INF("Invalid file (%s) (missing = from key=value pair)", file);
+            goto error;
+        }
+
+next_line:
+        left -= line_length + 1;
+        line_start += line_length + 1;
+    }
+    munmap((char*) buffer, file_stat.st_size);
+    fclose(f);
+
+#if 0
+    if (!eina_hash_population(data))
+    {
+        eina_hash_free(data);
+        return NULL;
+    }
+#endif
+    return data;
+error:
+    if (data) eina_hash_free(data);
+    return NULL;
+}
+
+EAPI void
+efreet_ini_free(Efreet_Ini *ini)
+{
+    if (!ini) return;
+
+    IF_FREE_HASH(ini->data);
+    FREE(ini);
+}
+
+EAPI int
+efreet_ini_save(Efreet_Ini *ini, const char *file)
+{
+    char *dir;
+    FILE *f;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->data, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
+
+    dir = ecore_file_dir_get(file);
+    if (!ecore_file_mkpath(dir))
+    {
+        free(dir);
+        return 0;
+    }
+    free(dir);
+    f = fopen(file, "wb");
+    if (!f) return 0;
+    eina_hash_foreach(ini->data, efreet_ini_section_save, f);
+    fclose(f);
+
+    return 1;
+}
+
+EAPI int
+efreet_ini_section_set(Efreet_Ini *ini, const char *section)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->data, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(section, 0);
+
+    ini->section = eina_hash_find(ini->data, section);
+    return (ini->section ? 1 : 0);
+}
+
+EAPI void
+efreet_ini_section_add(Efreet_Ini *ini, const char *section)
+{
+    Eina_Hash *hash;
+
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(section);
+
+    if (!ini->data)
+        ini->data = eina_hash_string_small_new(EINA_FREE_CB(eina_hash_free));
+    if (eina_hash_find(ini->data, section)) return;
+
+    hash = eina_hash_string_small_new(EINA_FREE_CB(eina_stringshare_del));
+    eina_hash_add(ini->data, section, hash);
+}
+
+EAPI const char *
+efreet_ini_string_get(Efreet_Ini *ini, const char *key)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->section, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
+
+    return eina_hash_find(ini->section, key);
+}
+
+EAPI void
+efreet_ini_string_set(Efreet_Ini *ini, const char *key, const char *value)
+{
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(ini->section);
+    EINA_SAFETY_ON_NULL_RETURN(key);
+
+    eina_hash_del_by_key(ini->section, key);
+    eina_hash_add(ini->section, key, eina_stringshare_add(value));
+}
+
+EAPI int
+efreet_ini_int_get(Efreet_Ini *ini, const char *key)
+{
+    const char *str;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, -1);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->section, -1);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(key, -1);
+
+    str = efreet_ini_string_get(ini, key);
+    if (str) return atoi(str);
+
+    return -1;
+}
+
+EAPI void
+efreet_ini_int_set(Efreet_Ini *ini, const char *key, int value)
+{
+    char str[12];
+
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(ini->section);
+    EINA_SAFETY_ON_NULL_RETURN(key);
+
+    snprintf(str, 12, "%d", value);
+    efreet_ini_string_set(ini, key, str);
+}
+
+EAPI double
+efreet_ini_double_get(Efreet_Ini *ini, const char *key)
+{
+    const char *str;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, -1);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->section, -1);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(key, -1);
+
+    str = efreet_ini_string_get(ini, key);
+    if (str) return atof(str);
+
+    return -1;
+}
+
+EAPI void
+efreet_ini_double_set(Efreet_Ini *ini, const char *key, double value)
+{
+    char str[512];
+    size_t len;
+
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(ini->section);
+    EINA_SAFETY_ON_NULL_RETURN(key);
+
+    snprintf(str, 512, "%.6f", value);
+    len = strlen(str) - 1;
+    /* Strip trailing zero's */
+    while (str[len] == '0' && str[len - 1] != '.') str[len--] = '\0';
+    efreet_ini_string_set(ini, key, str);
+}
+
+EAPI unsigned int
+efreet_ini_boolean_get(Efreet_Ini *ini, const char *key)
+{
+    const char *str;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->section, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(key, 0);
+
+    str = efreet_ini_string_get(ini, key);
+    if (str && !strcmp("true", str)) return 1;
+
+    return 0;
+}
+
+EAPI void
+efreet_ini_boolean_set(Efreet_Ini *ini, const char *key, unsigned int value)
+{
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(ini->section);
+    EINA_SAFETY_ON_NULL_RETURN(key);
+
+    if (value) efreet_ini_string_set(ini, key, "true");
+    else efreet_ini_string_set(ini, key, "false");
+}
+
+EAPI const char *
+efreet_ini_localestring_get(Efreet_Ini *ini, const char *key)
+{
+    const char *lang, *country, *modifier;
+    const char *val = NULL;
+    char *buf;
+    int maxlen = 5; /* _, @, [, ] and \0 */
+    int found = 0;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(ini->section, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
+
+    lang = efreet_lang_get();
+    country = efreet_lang_country_get();
+    modifier = efreet_lang_modifier_get();
+
+    maxlen += strlen(key);
+    if (lang) maxlen += strlen(lang);
+    if (country) maxlen += strlen(country);
+    if (modifier) maxlen += strlen(modifier);
+
+    buf = alloca(maxlen);
+
+    if (lang && modifier && country)
+    {
+        snprintf(buf, maxlen, "%s[%s_%s@%s]", key, lang, country, modifier);
+        val = efreet_ini_string_get(ini, buf);
+        if (val && (*val != '\0')) found = 1;
+    }
+
+    if (!found && lang && country)
+    {
+        snprintf(buf, maxlen, "%s[%s_%s]", key, lang, country);
+        val = efreet_ini_string_get(ini, buf);
+        if (val && (*val != '\0')) found = 1;
+    }
+
+    if (!found && lang && modifier)
+    {
+        snprintf(buf, maxlen, "%s[%s@%s]", key, lang, modifier);
+        val = efreet_ini_string_get(ini, buf);
+        if (val && (*val != '\0')) found = 1;
+    }
+
+    if (!found && lang)
+    {
+        snprintf(buf, maxlen, "%s[%s]", key, lang);
+        val = efreet_ini_string_get(ini, buf);
+        if (val && (*val != '\0')) found = 1;
+    }
+
+    if (!found)
+        val = efreet_ini_string_get(ini, key);
+
+    return val;
+}
+
+EAPI void
+efreet_ini_localestring_set(Efreet_Ini *ini, const char *key, const char *value)
+{
+    const char *lang, *country, *modifier;
+    char *buf;
+    int maxlen = 5; /* _, @, [, ] and \0 */
+
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(ini->section);
+    EINA_SAFETY_ON_NULL_RETURN(key);
+
+    lang = efreet_lang_get();
+    country = efreet_lang_country_get();
+    modifier = efreet_lang_modifier_get();
+
+    maxlen += strlen(key);
+    if (lang) maxlen += strlen(lang);
+    if (country) maxlen += strlen(country);
+    if (modifier) maxlen += strlen(modifier);
+
+    buf = alloca(maxlen);
+
+    if (lang && modifier && country)
+        snprintf(buf, maxlen, "%s[%s_%s@%s]", key, lang, country, modifier);
+    else if (lang && country)
+        snprintf(buf, maxlen, "%s[%s_%s]", key, lang, country);
+    else if (lang && modifier)
+        snprintf(buf, maxlen, "%s[%s@%s]", key, lang, modifier);
+    else if (lang)
+        snprintf(buf, maxlen, "%s[%s]", key, lang);
+    else
+        return;
+
+    efreet_ini_string_set(ini, buf, value);
+}
+
+EAPI void
+efreet_ini_key_unset(Efreet_Ini *ini, const char *key)
+{
+    EINA_SAFETY_ON_NULL_RETURN(ini);
+    EINA_SAFETY_ON_NULL_RETURN(ini->section);
+    EINA_SAFETY_ON_NULL_RETURN(key);
+
+    eina_hash_del_by_key(ini->section, key);
+}
+
+/**
+ * @param str The string to unescape
+ * @return An allocated unescaped string
+ * @brief Unescapes backslash escapes in a string
+ */
+static const char *
+efreet_ini_unescape(const char *str)
+{
+    char *buf, *dest;
+    const char *p;
+
+    if (!strchr(str, '\\')) return eina_stringshare_add(str);
+    buf = alloca(strlen(str) + 1);
+
+    p = str;
+    dest = buf;
+    while (*p)
+    {
+        if ((*p == '\\') && (p[1] != '\0'))
+        {
+            p++;
+            switch (*p)
+            {
+                case 's':
+                    *(dest++) = ' ';
+                    break;
+                case 'n':
+                    *(dest++) = '\n';
+                    break;
+                case 't':
+                    *(dest++) = '\t';
+                    break;
+                case 'r':
+                    *(dest++) = '\r';
+                    break;
+                case '\\':
+                    *(dest++) = '\\';
+                    break;
+                default:
+                    (*dest++) = '\\';
+                    (*dest++) = *p;
+            }
+        }
+        else
+            *(dest++) = *p;
+
+        p++;
+    }
+
+    *(dest) = '\0';
+    return eina_stringshare_add(buf);
+}
+
+static Eina_Bool
+efreet_ini_section_save(const Eina_Hash *hash __UNUSED__, const void *key, void *value, void *fdata)
+{
+    FILE *f = fdata;
+
+    fprintf(f, "[%s]\n", (char *)key);
+    eina_hash_foreach(value, efreet_ini_value_save, f);
+    return EINA_TRUE;
+}
+
+static Eina_Bool
+efreet_ini_value_save(const Eina_Hash *hash __UNUSED__, const void *key, void *value, void *fdata)
+{
+    FILE *f = fdata;
+
+    fprintf(f, "%s=%s\n", (char *)key, (char *)value);
+    return EINA_TRUE;
+}
diff --git a/src/lib/efreet_ini.h b/src/lib/efreet_ini.h
new file mode 100644 (file)
index 0000000..f5b8a9e
--- /dev/null
@@ -0,0 +1,181 @@
+#ifndef EFREET_INI_H
+#define EFREET_INI_H
+
+/**
+ * @internal
+ * @file efreet_ini.h
+ * @brief A simple and fast INI parser
+ * @addtogroup Efreet_Ini Efreet_Ini: An INI parser
+ *
+ * @{
+ */
+
+/**
+ * Efreet_Ini
+ */
+typedef struct Efreet_Ini Efreet_Ini;
+
+/**
+ * Efreet_Ini
+ * @brief Contains all the information about an ini file.
+ */
+struct Efreet_Ini
+{
+  Eina_Hash *data;     /**< Hash of string => (Hash of string => string) */
+  Eina_Hash *section;  /**< currently selected section */
+};
+
+
+/**
+ * @param file The file to parse
+ * @return Returns a new Efreet_Ini structure initialized with the contents
+ * of @a file, or NULL on memory allocation failure
+ * @brief Creates and initializes a new Ini structure with the contents of
+ * @a file, or NULL on failure
+ */
+EAPI Efreet_Ini  *efreet_ini_new(const char *file);
+
+/**
+ * @param ini The Efreet_Ini to work with
+ * @return Returns no value
+ * @brief Frees the given Efree_Ini structure.
+ */
+EAPI void         efreet_ini_free(Efreet_Ini *ini);
+
+/**
+ * @param ini The Efreet_Ini to work with
+ * @param file The file to load
+ * @return Returns no value
+ * @brief Saves the given Efree_Ini structure.
+ */
+EAPI int          efreet_ini_save(Efreet_Ini *ini, const char *path);
+
+
+/**
+ * @param ini The Efreet_Ini to work with
+ * @param section The section of the ini file we want to get values from
+ * @return Returns 1 if the section exists, otherwise 0
+ * @brief Sets the current working section of the ini file to @a section
+ */
+EAPI int          efreet_ini_section_set(Efreet_Ini *ini, const char *section);
+
+/**
+ * @param ini The Efreet_Ini to work with
+ * @param section The section of the ini file we want to add
+ * @return Returns no value
+ * @brief Adds a new working section of the ini file to @a section
+ */
+EAPI void         efreet_ini_section_add(Efreet_Ini *ini, const char *section);
+
+
+/**
+ * @param ini The Efree_Ini to work with
+ * @param key The key to lookup
+ * @return Returns the string associated with the given key or NULL if not
+ * found.
+ * @brief Retrieves the value for the given key or NULL if none found.
+ */
+EAPI const char  *efreet_ini_string_get(Efreet_Ini *ini, const char *key);
+
+/**
+ * @param ini The Efree_Ini to work with
+ * @param key The key to use
+ * @param value The value to set
+ * @return Returns no value
+ * @brief Sets the value for the given key
+ */
+EAPI void         efreet_ini_string_set(Efreet_Ini *ini, const char *key,
+                                                    const char *value);
+
+
+/**
+ * @param ini The ini struct to work with
+ * @param key The key to search for
+ * @return Returns the utf8 encoded string associated with @a key, or NULL
+ *         if none found
+ * @brief Retrieves the utf8 encoded string associated with @a key in the current locale or NULL if none found
+ */
+EAPI const char  *efreet_ini_localestring_get(Efreet_Ini *ini, const char *key);
+
+/**
+ * @param ini The ini struct to work with
+ * @param key The key to use
+ * @param value The value to set
+ * @return Returns no value
+ * @brief Sets the value for the given key
+ */
+EAPI void         efreet_ini_localestring_set(Efreet_Ini *ini, const char *key,
+                                                    const char *value);
+
+
+/**
+ * @param ini The ini struct to work with
+ * @param key The key to search for
+ * @return Returns 1 if the boolean is true, 0 otherwise
+ * @brief Retrieves the boolean value at key @a key from the ini @a ini
+ */
+EAPI unsigned int efreet_ini_boolean_get(Efreet_Ini *ini, const char *key);
+
+/**
+ * @param ini The ini struct to work with
+ * @param key The key to use
+ * @param value The value to set
+ * @return Returns no value
+ * @brief Sets the value for the given key
+ */
+EAPI void         efreet_ini_boolean_set(Efreet_Ini *ini, const char *key,
+                                                    unsigned int value);
+
+
+/**
+ * @param ini The Efree_Ini to work with
+ * @param key The key to lookup
+ * @return Returns the integer associated with the given key or -1 if not
+ * found.
+ * @brief Retrieves the value for the given key or -1 if none found.
+ */
+EAPI int          efreet_ini_int_get(Efreet_Ini *ini, const char *key);
+
+/**
+ * @param ini The Efree_Ini to work with
+ * @param key The key to use
+ * @param value The value to set
+ * @return Returns no value
+ * @brief Sets the value for the given key
+ */
+EAPI void         efreet_ini_int_set(Efreet_Ini *ini, const char *key, int value);
+
+
+/**
+ * @param ini The Efree_Ini to work with
+ * @param key The key to lookup
+ * @return Returns the double associated with the given key or -1 if not
+ * found.
+ * @brief Retrieves the value for the given key or -1 if none found.
+ */
+EAPI double       efreet_ini_double_get(Efreet_Ini *ini, const char *key);
+
+/**
+ * @param ini The Efree_Ini to work with
+ * @param key The key to use
+ * @param value The value to set
+ * @return Returns no value
+ * @brief Sets the value for the given key
+ */
+EAPI void         efreet_ini_double_set(Efreet_Ini *ini, const char *key,
+                                                    double value);
+
+
+/**
+ * @param ini The ini struct to work with
+ * @param key The key to remove
+ * @return Returns no value
+ * @brief Remove the given key from the ini struct
+ */
+EAPI void         efreet_ini_key_unset(Efreet_Ini *ini, const char *key);
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_menu.c b/src/lib/efreet_menu.c
new file mode 100644 (file)
index 0000000..e612ba1
--- /dev/null
@@ -0,0 +1,3887 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_menu_log_dom
+static int _efreet_menu_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_xml.h"
+
+typedef struct Efreet_Menu_Move Efreet_Menu_Move;
+
+struct Efreet_Menu_Move
+{
+    const char *old_name;     /**< The menu path to move from */
+    const char *new_name;     /**< The menu path to move too */
+};
+
+typedef struct Efreet_Menu_Internal Efreet_Menu_Internal;
+
+struct Efreet_Menu_Internal
+{
+    struct
+    {
+        const char *path;         /**< The base file path */
+        const char *name;         /**< The filename for this menu */
+    } file;                 /**< The menu file information */
+
+    struct
+    {
+        const char *internal;     /**< The menu name */
+        const char *name;         /**< Name to use in the menus */
+    } name;                       /**< The names for this menu */
+
+    Efreet_Desktop *directory; /**< The directory */
+    Eina_List *directories;  /**< All the directories set in the menu file */
+
+    Efreet_Menu_Move *current_move; /**< The current move */
+
+    Eina_List *app_dirs;           /**< .desktop application directories */
+
+    Eina_List *app_pool;           /**< application pool */
+    Eina_List *applications;       /**< applications in this menu */
+
+    Eina_List *directory_dirs;    /**< .directory file directories */
+    Eina_Hash *directory_cache;    /**< .directory dirs */
+
+    Eina_List *moves;              /**< List of moves to be handled by the menu */
+    Eina_List *filters;            /**< Include and Exclude filters */
+
+    Efreet_Menu_Internal *parent;   /**< Our parent menu */
+    Eina_List *sub_menus;          /**< Our sub menus */
+
+    Eina_List *layout;             /**< This menus layout */
+    Eina_List *default_layout;     /**< Default layout */
+    signed char show_empty;    /**< Whether to show empty menus */
+    signed char in_line;       /**< Whether this meny can be inlined */
+    signed char inline_limit;  /**< Number of elements which triggers inline */
+    signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
+    signed char inline_alias;  /**< Whether we should use the menu name when inlining */
+
+    unsigned char seen_allocated:1;     /**< have we set the only_unallocated */
+    unsigned char only_unallocated:1;   /**< Show only unallocated .desktops */
+
+    unsigned char seen_deleted:1;       /**< Have we seen the deleted item yet */
+    unsigned char deleted:1;            /**< The menu is deleted */
+};
+
+typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir;
+
+struct Efreet_Menu_App_Dir
+{
+    const char *path;           /**< directory path */
+    const char *prefix;         /**< If it's legacy it can have a prefix */
+    unsigned int legacy:1;      /**< is this a legacy dir */
+};
+
+enum Efreet_Menu_Filter_Op_Type
+{
+    EFREET_MENU_FILTER_OP_OR,
+    EFREET_MENU_FILTER_OP_AND,
+    EFREET_MENU_FILTER_OP_NOT
+};
+
+typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type;
+
+enum Efreet_Menu_Filter_Type
+{
+    EFREET_MENU_FILTER_INCLUDE,
+    EFREET_MENU_FILTER_EXCLUDE
+};
+
+typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type;
+
+typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op;
+
+struct Efreet_Menu_Filter_Op
+{
+    Efreet_Menu_Filter_Op_Type type; /**< The type of operation */
+    Eina_List *categories;          /**< The categories this op applies too */
+    Eina_List *filenames;           /**< The filenames this op applies too */
+
+    Eina_List *filters;             /**< Child filters */
+
+    unsigned char all:1;             /**< Applies to all .desktop files */
+};
+
+typedef struct Efreet_Menu_Filter Efreet_Menu_Filter;
+
+struct Efreet_Menu_Filter
+{
+    Efreet_Menu_Filter_Type type;   /**< The type of filter */
+    Efreet_Menu_Filter_Op *op;      /**< The filter operations */
+};
+
+enum Efreet_Menu_Layout_Type
+{
+    EFREET_MENU_LAYOUT_MENUNAME,
+    EFREET_MENU_LAYOUT_FILENAME,
+    EFREET_MENU_LAYOUT_SEPARATOR,
+    EFREET_MENU_LAYOUT_MERGE
+};
+
+typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type;
+
+typedef struct Efreet_Menu_Layout Efreet_Menu_Layout;
+
+struct Efreet_Menu_Layout
+{
+    Efreet_Menu_Layout_Type  type;   /**< The type of layout */
+    const char *name;                /**< The name of the element */
+
+    /* The items below are for Menuname Layout elements */
+    signed char show_empty;    /**< Whether to show empty menus */
+    signed char in_line;       /**< Whether this meny can be inlined */
+    signed char inline_limit;  /**< Number of elements which triggers inline */
+    signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
+    signed char inline_alias;  /**< Whether we should use the menu name when inlining */
+};
+
+typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop;
+
+struct Efreet_Menu_Desktop
+{
+    Efreet_Desktop *desktop;   /**< The desktop we refer too */
+    const char *id;            /**< The desktop file id */
+    unsigned char allocated:1; /**< If this desktop has been allocated */
+};
+
+static const char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */
+Eina_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */
+static const char *efreet_tag_menu = NULL;
+static const char *efreet_menu_file = NULL; /**< A menu file set explicityl as default */
+
+static Eina_Hash *efreet_merged_menus = NULL;
+static Eina_Hash *efreet_merged_dirs = NULL;
+
+static Eina_Hash *efreet_menu_handle_cbs = NULL;
+static Eina_Hash *efreet_menu_filter_cbs = NULL;
+static Eina_Hash *efreet_menu_move_cbs = NULL;
+static Eina_Hash *efreet_menu_layout_cbs = NULL;
+
+static const char *efreet_menu_prefix_get(void);
+
+static Efreet_Menu_Internal *efreet_menu_by_name_find(Efreet_Menu_Internal *internal,
+                                                    const char *name,
+                                                    Efreet_Menu_Internal **parent);
+static int efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name);
+static int efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name);
+
+static int efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal);
+static int efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop);
+
+static int efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old);
+
+static int efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated);
+static int efreet_menu_process_dirs(Efreet_Menu_Internal *internal);
+static int efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal);
+static int efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal,
+                                        const char *path,
+                                        const char *id,
+                                        int legacy);
+static int efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal);
+static int efreet_menu_directory_dir_scan(const char *path,
+                                            const char *relative_path,
+                                            Eina_Hash *cache);
+static Efreet_Desktop *efreet_menu_directory_get(Efreet_Menu_Internal *internal,
+                                                    const char *path);
+static void efreet_menu_process_filters(Efreet_Menu_Internal *internal,
+                                            unsigned int only_unallocated);
+static Eina_List *efreet_menu_process_app_pool(Eina_List *pool,
+                                               Eina_List *applications,
+                                               Eina_Hash *matches,
+                                               Efreet_Menu_Filter *filter,
+                                               unsigned int only_unallocated);
+static int efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op,
+                                        Efreet_Menu_Desktop *md);
+static int efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op,
+                                            Efreet_Menu_Desktop *md);
+static int efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op,
+                                            Efreet_Menu_Desktop *md);
+static int efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op,
+                                            Efreet_Menu_Desktop *md);
+
+static Efreet_Menu *efreet_menu_layout_menu(Efreet_Menu_Internal *internal);
+static Efreet_Menu *efreet_menu_layout_desktop(Efreet_Menu_Desktop *md);
+static void efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
+                                            Efreet_Menu_Layout *layout);
+static int efreet_menu_layout_is_empty(Efreet_Menu *entry);
+
+static Efreet_Menu_Internal *efreet_menu_internal_new(void);
+static void efreet_menu_internal_free(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_directories_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_move_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_filter_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_layout_list(Efreet_Menu_Internal *internal);
+static void efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal);
+static const char *efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix);
+
+static Efreet_Menu_App_Dir *efreet_menu_app_dir_new(void);
+static void efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir);
+
+static Efreet_Menu_Move *efreet_menu_move_new(void);
+static void efreet_menu_move_free(Efreet_Menu_Move *move);
+
+static Efreet_Menu_Filter *efreet_menu_filter_new(void);
+static void efreet_menu_filter_free(Efreet_Menu_Filter *filter);
+
+static Efreet_Menu_Layout *efreet_menu_layout_new(void);
+static void efreet_menu_layout_free(Efreet_Menu_Layout *layout);
+
+static Efreet_Menu_Filter_Op *efreet_menu_filter_op_new(void);
+static void efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op);
+
+static Efreet_Menu_Desktop *efreet_menu_desktop_new(void);
+static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md);
+
+static Efreet_Menu *efreet_menu_entry_new(void);
+
+static int efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml);
+static int efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+
+static int efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static Efreet_Menu_Internal *efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
+                                                Efreet_Menu_Internal *parent,
+                                                const char *legacy_dir,
+                                                const char *prefix);
+static int efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+static int efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+
+static int efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
+                                                    Efreet_Menu_Filter_Type type);
+static int efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+static int efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
+                                                      Efreet_Menu_Filter_Op_Type type);
+
+static int efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+static int efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+static int efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+static int efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+
+static int efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
+static int efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
+
+static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b);
+
+static void efreet_menu_resolve_moves(Efreet_Menu_Internal *internal);
+static void efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src);
+
+static int efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b);
+static int efreet_menu_cb_md_compare(const Efreet_Menu_Desktop *a, const Efreet_Menu_Desktop *b);
+
+static int efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent);
+static int efreet_menu_save_indent(FILE *f, int indent);
+
+static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path);
+
+int
+efreet_menu_init(void)
+{
+    int i;
+
+    struct
+    {
+        const char *key;
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+    } menu_cbs[] = {
+        {"Menu", efreet_menu_handle_sub_menu},
+        {"AppDir", efreet_menu_handle_app_dir},
+        {"DefaultAppDirs", efreet_menu_handle_default_app_dirs},
+        {"DirectoryDir", efreet_menu_handle_directory_dir},
+        {"DefaultDirectoryDirs", efreet_menu_handle_default_directory_dirs},
+        {"Name", efreet_menu_handle_name},
+        {"Directory", efreet_menu_handle_directory},
+        {"OnlyUnallocated", efreet_menu_handle_only_unallocated},
+        {"NotOnlyUnallocated", efreet_menu_handle_not_only_unallocated},
+        {"Deleted", efreet_menu_handle_deleted},
+        {"NotDeleted", efreet_menu_handle_not_deleted},
+        {"Include", efreet_menu_handle_include},
+        {"Exclude", efreet_menu_handle_exclude},
+        {"MergeFile", efreet_menu_handle_merge_file},
+        {"MergeDir", efreet_menu_handle_merge_dir},
+        {"DefaultMergeDirs", efreet_menu_handle_default_merge_dirs},
+        {"LegacyDir", efreet_menu_handle_legacy_dir},
+        {"KDELegacyDirs", efreet_menu_handle_kde_legacy_dirs},
+        {"Move", efreet_menu_handle_move},
+        {"Layout", efreet_menu_handle_layout},
+        {"DefaultLayout", efreet_menu_handle_default_layout},
+        {NULL, NULL}
+    };
+
+    struct
+    {
+        const char *key;
+        int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+    } filter_cbs[] = {
+        {"Filename", efreet_menu_handle_filename},
+        {"Category", efreet_menu_handle_category},
+        {"All", efreet_menu_handle_all},
+        {"And", efreet_menu_handle_and},
+        {"Or", efreet_menu_handle_or},
+        {"Not", efreet_menu_handle_not},
+        {NULL, NULL}
+    };
+
+    struct
+    {
+        const char *key;
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+    } move_cbs[] = {
+        {"Old", efreet_menu_handle_old},
+        {"New", efreet_menu_handle_new},
+        {NULL, NULL}
+    };
+
+    struct
+    {
+        const char *key;
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+    } layout_cbs[] = {
+        {"Menuname", efreet_menu_handle_layout_menuname},
+        {"Filename", efreet_menu_handle_layout_filename},
+        {"Separator", efreet_menu_handle_layout_separator},
+        {"Merge", efreet_menu_handle_layout_merge},
+        {NULL, NULL}
+    };
+
+    _efreet_menu_log_dom = eina_log_domain_register
+      ("efreet_menu", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_menu_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_menu");
+        return 0;
+    }
+
+    efreet_menu_handle_cbs = eina_hash_string_superfast_new(NULL);
+    efreet_menu_filter_cbs = eina_hash_string_superfast_new(NULL);
+    efreet_menu_move_cbs = eina_hash_string_superfast_new(NULL);
+    efreet_menu_layout_cbs = eina_hash_string_superfast_new(NULL);
+    if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
+            || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
+    {
+        eina_log_domain_unregister(_efreet_menu_log_dom);
+        _efreet_menu_log_dom = -1;
+        return 0;
+    }
+
+    /* set Menu into it's own so we can check the XML is valid before trying
+     * to handle it */
+    efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
+
+    for (i = 0; menu_cbs[i].key; i++)
+    {
+        eina_hash_del(efreet_menu_handle_cbs,
+                        menu_cbs[i].key,
+                        NULL);
+        eina_hash_add(efreet_menu_handle_cbs,
+                        menu_cbs[i].key,
+                        menu_cbs[i].cb);
+    }
+    for (i = 0; filter_cbs[i].key; i++)
+    {
+        eina_hash_del(efreet_menu_filter_cbs,
+                        filter_cbs[i].key,
+                        NULL);
+        eina_hash_add(efreet_menu_filter_cbs,
+                        filter_cbs[i].key,
+                        filter_cbs[i].cb);
+    }
+    for (i = 0; move_cbs[i].key; i++)
+    {
+        eina_hash_del(efreet_menu_move_cbs,
+                        move_cbs[i].key,
+                        NULL);
+        eina_hash_add(efreet_menu_move_cbs,
+                        move_cbs[i].key,
+                        move_cbs[i].cb);
+    }
+    for (i = 0; layout_cbs[i].key; i++)
+    {
+        eina_hash_del(efreet_menu_layout_cbs,
+                        layout_cbs[i].key,
+                        NULL);
+        eina_hash_add(efreet_menu_layout_cbs,
+                        layout_cbs[i].key,
+                        layout_cbs[i].cb);
+    }
+    return 1;
+}
+
+EAPI int
+efreet_menu_kde_legacy_init(void)
+{
+    FILE *f;
+    char buf[PATH_MAX];
+    char *p, *s;
+
+    IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
+
+    f = popen("kde-config --path apps", "r");
+    if (!f) return 0;
+
+    /* XXX if the return from kde-config is a line longer than PATH_MAX,
+     * this won't be correct (increase buffer and get the rest...) */
+    if (!fgets(buf, sizeof(buf), f))
+    {
+        ERR("Error initializing KDE legacy information");
+        return 0;
+    }
+    s = buf;
+
+    p = strchr(s, ':');
+    while (p)
+    {
+        *p = '\0';
+        efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
+                            (void *)eina_stringshare_add(s));
+        s = p + 1;
+        p = strchr(s, ':');
+    }
+
+    if (*s)
+        efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
+                            (void *)eina_stringshare_add(s));
+
+    pclose(f);
+    return 1;
+}
+
+void
+efreet_menu_shutdown(void)
+{
+    IF_RELEASE(efreet_menu_file);
+
+    IF_FREE_HASH(efreet_menu_handle_cbs);
+    IF_FREE_HASH(efreet_menu_filter_cbs);
+    IF_FREE_HASH(efreet_menu_move_cbs);
+    IF_FREE_HASH(efreet_menu_layout_cbs);
+
+    IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
+
+    IF_FREE_HASH(efreet_merged_menus);
+    IF_FREE_HASH(efreet_merged_dirs);
+
+    IF_RELEASE(efreet_tag_menu);
+
+    eina_log_domain_unregister(_efreet_menu_log_dom);
+    _efreet_menu_log_dom = -1;
+}
+
+EAPI Efreet_Menu *
+efreet_menu_new(const char *name)
+{
+    Efreet_Menu *menu;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
+
+    menu = efreet_menu_entry_new();
+    menu->type = EFREET_MENU_ENTRY_MENU;
+    menu->name = eina_stringshare_add(name);
+    return menu;
+}
+
+EAPI void
+efreet_menu_file_set(const char *file)
+{
+    IF_RELEASE(efreet_menu_file);
+    efreet_menu_file = NULL;
+    if (file) efreet_menu_file = eina_stringshare_add(file);
+}
+
+EAPI Efreet_Menu *
+efreet_menu_get(void)
+{
+    char menu[PATH_MAX];
+    const char *dir;
+    Eina_List *config_dirs, *l;
+
+#ifndef STRICT_SPEC
+    /* prefer user set menu */
+    if (efreet_menu_file)
+    {
+        if (ecore_file_exists(efreet_menu_file))
+        return efreet_menu_parse(efreet_menu_file);
+    }
+#endif
+
+    /* check the users config directory first */
+    snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
+                        efreet_config_home_get(), efreet_menu_prefix_get());
+    if (ecore_file_exists(menu))
+        return efreet_menu_parse(menu);
+
+    /* fallback to the XDG_CONFIG_DIRS */
+    config_dirs = efreet_config_dirs_get();
+    EINA_LIST_FOREACH(config_dirs, l, dir)
+    {
+        snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
+                                    dir, efreet_menu_prefix_get());
+        if (ecore_file_exists(menu))
+            return efreet_menu_parse(menu);
+    }
+
+    return NULL;
+}
+
+EAPI Efreet_Menu *
+efreet_menu_parse(const char *path)
+{
+    Efreet_Xml *xml;
+    Efreet_Menu_Internal *internal = NULL;
+    Efreet_Menu *entry = NULL;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
+
+    xml = efreet_xml_new(path);
+    if (!xml) return NULL;
+
+    /* make sure we've got a <Menu> to start with */
+    if (xml->tag != efreet_tag_menu)
+    {
+        WRN("Efreet_menu: Menu file didn't start with <Menu> tag.");
+        efreet_xml_del(xml);
+        return NULL;
+    }
+
+    IF_FREE_HASH(efreet_merged_menus);
+    efreet_merged_menus = eina_hash_string_superfast_new(NULL);
+
+    IF_FREE_HASH(efreet_merged_dirs);
+    efreet_merged_dirs = eina_hash_string_superfast_new(NULL);
+
+    /* split apart the filename and the path */
+    internal = efreet_menu_internal_new();
+    if (!internal) return NULL;
+
+    /* Set default values */
+    internal->show_empty = 0;
+    internal->in_line = 0;
+    internal->inline_limit = 4;
+    internal->inline_header = 1;
+    internal->inline_alias = 0;
+
+    efreet_menu_path_set(internal, path);
+    if (!efreet_menu_handle_menu(internal, xml))
+    {
+        efreet_xml_del(xml);
+        efreet_menu_internal_free(internal);
+        return NULL;
+    }
+    efreet_xml_del(xml);
+
+    efreet_menu_resolve_moves(internal);
+
+    if (!efreet_menu_process_dirs(internal))
+    {
+        efreet_menu_internal_free(internal);
+        return NULL;
+    }
+
+    /* handle all .desktops */
+    if (!efreet_menu_process(internal, 0))
+    {
+        efreet_menu_internal_free(internal);
+        return NULL;
+    }
+
+    /* handle menus with only unallocated .desktops */
+    if (!efreet_menu_process(internal, 1))
+    {
+        efreet_menu_internal_free(internal);
+        return NULL;
+    }
+
+    /* layout menu */
+    entry = efreet_menu_layout_menu(internal);
+    efreet_menu_internal_free(internal);
+    return entry;
+}
+
+EAPI int
+efreet_menu_save(Efreet_Menu *menu, const char *path)
+{
+    FILE *f;
+    int ret;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(path, 0);
+
+    f = fopen(path, "w");
+    if (!f) return 0;
+    fprintf(f, "<?xml version=\"1.0\"?>\n");
+    fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
+                "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
+    ret = efreet_menu_save_menu(menu, f, 0);
+    fclose(f);
+    return ret;
+}
+
+static int
+efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
+{
+    Eina_List *l;
+
+    efreet_menu_save_indent(f, indent);
+    fprintf(f, "<Menu>\n");
+    if (menu->name)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<Name>%s</Name>\n", menu->name);
+    }
+
+    if (indent == 0)
+    {
+        /* Only save these for the root element */
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<DefaultAppDirs/>\n");
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<DefaultDirectoryDirs/>\n");
+    }
+
+    if (menu->desktop)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
+    }
+
+    if (menu->entries)
+    {
+        Efreet_Menu *entry;
+        int has_desktop = 0, has_menu = 0;
+
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<Layout>\n");
+        EINA_LIST_FOREACH(menu->entries, l, entry)
+        {
+            if (entry->type == EFREET_MENU_ENTRY_MENU)
+            {
+                efreet_menu_save_indent(f, indent + 2);
+                fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
+                has_menu = 1;
+            }
+            else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
+            {
+                efreet_menu_save_indent(f, indent + 2);
+                fprintf(f, "<Filename>%s</Filename>\n", entry->id);
+                has_desktop = 1;
+            }
+            else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
+            {
+                efreet_menu_save_indent(f, indent + 2);
+                fprintf(f, "<Separator/>\n");
+            }
+        }
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "</Layout>\n");
+
+        if (has_desktop)
+        {
+            efreet_menu_save_indent(f, indent + 1);
+            fprintf(f, "<Include>\n");
+            EINA_LIST_FOREACH(menu->entries, l, entry)
+            {
+                if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
+                {
+                    efreet_menu_save_indent(f, indent + 2);
+                    fprintf(f, "<Filename>%s</Filename>\n", entry->id);
+                }
+            }
+            efreet_menu_save_indent(f, indent + 1);
+            fprintf(f, "</Include>\n");
+        }
+
+        if (has_menu)
+        {
+            EINA_LIST_FOREACH(menu->entries, l, entry)
+            {
+                if (entry->type == EFREET_MENU_ENTRY_MENU)
+                    efreet_menu_save_menu(entry, f, indent + 1);
+            }
+        }
+    }
+    efreet_menu_save_indent(f, indent);
+    fprintf(f, "</Menu>\n");
+    return 1;
+}
+
+static int
+efreet_menu_save_indent(FILE *f, int indent)
+{
+    int i;
+
+    for (i = 0; i < indent; i++)
+        fprintf(f, "  ");
+    return 1;
+}
+
+EAPI int
+efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
+{
+    Efreet_Menu *entry;
+    const char *id;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+
+    id = efreet_util_path_to_file_id(desktop->orig_path);
+    if (!id) return 0;
+
+    entry = efreet_menu_entry_new();
+    entry->type = EFREET_MENU_ENTRY_DESKTOP;
+    entry->id = eina_stringshare_add(id);
+    entry->name = eina_stringshare_add(desktop->name);
+    if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
+    efreet_desktop_ref(desktop);
+    entry->desktop = desktop;
+
+    if (pos < 0 || (unsigned int)pos >= eina_list_count(menu->entries))
+        menu->entries = eina_list_append(menu->entries, entry);
+    else
+    {
+        menu->entries = eina_list_append_relative(menu->entries, entry,
+                                                  eina_list_nth(menu->entries, pos));
+    }
+    return 1;
+}
+
+EAPI int
+efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
+{
+    Efreet_Menu *entry;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
+
+    entry = eina_list_search_unsorted(menu->entries,
+                                      EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
+                            desktop);
+    if (entry)
+    {
+        menu->entries = eina_list_remove(menu->entries, entry);
+        efreet_menu_free(entry);
+        return 1;
+    }
+    return 0;
+}
+
+EAPI void
+efreet_menu_dump(Efreet_Menu *menu, const char *indent)
+{
+    Eina_List *l;
+
+    EINA_SAFETY_ON_NULL_RETURN(menu);
+    EINA_SAFETY_ON_NULL_RETURN(indent);
+
+    INF("%s%s: ", indent, menu->name);
+    INF("%s", (menu->icon ? menu->icon : "No icon"));
+
+    /* XXX dump the rest of the menu info */
+
+    if (menu->entries)
+    {
+        Efreet_Menu *entry;
+        char *new_indent;
+        size_t len;
+
+        len = strlen(indent) + 3;
+        new_indent = alloca(len);
+        snprintf(new_indent, len, "%s  ", indent);
+
+        EINA_LIST_FOREACH(menu->entries, l, entry)
+        {
+            if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
+                INF("%s|---", new_indent);
+            else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
+                INF("%s|-%s", new_indent, entry->name);
+            else if (entry->type == EFREET_MENU_ENTRY_MENU)
+                efreet_menu_dump(entry, new_indent);
+            else if (entry->type == EFREET_MENU_ENTRY_HEADER)
+                INF("%s|---%s", new_indent, entry->name);
+        }
+    }
+}
+
+/**
+ * @internal
+ * @param user_dir The user directory to work with
+ * @param system_dirs The system directories to work with
+ * @param suffix The path suffix to add
+ * @return Returns the list of directories
+ * @brief Creates the list of directories based on the user
+ * dir, system dirs and given suffix.
+ *
+ * Needs EAPI because of helper binaries
+ */
+EAPI Eina_List *
+efreet_default_dirs_get(const char *user_dir, Eina_List *system_dirs,
+                                                    const char *suffix)
+{
+    const char *xdg_dir;
+    char dir[PATH_MAX];
+    Eina_List *list = NULL;
+    Eina_List *l;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(user_dir, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(suffix, NULL);
+
+    snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
+    list = eina_list_append(list, eina_stringshare_add(dir));
+
+    EINA_LIST_FOREACH(system_dirs, l, xdg_dir)
+    {
+        snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
+        list = eina_list_append(list, eina_stringshare_add(dir));
+    }
+
+    return list;
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_Internal struct
+ * @brief Allocates and initializes a new Efreet_Menu_Internal structure
+ */
+static Efreet_Menu_Internal *
+efreet_menu_internal_new(void)
+{
+    Efreet_Menu_Internal *internal;
+
+    internal = NEW(Efreet_Menu_Internal, 1);
+    if (!internal) return NULL;
+    internal->show_empty = -1;
+    internal->in_line = -1;
+    internal->inline_limit = -1;
+    internal->inline_header = -1;
+    internal->inline_alias = -1;
+
+    return internal;
+}
+
+/**
+ * @param menu The menu to free
+ * @return Returns no value
+ * @brief Frees up the given menu structure
+ */
+void
+efreet_menu_internal_free(Efreet_Menu_Internal *internal)
+{
+    if (!internal) return;
+
+    IF_RELEASE(internal->file.path);
+    IF_RELEASE(internal->file.name);
+
+    IF_RELEASE(internal->name.internal);
+    internal->name.name = NULL;
+
+    internal->applications = eina_list_free(internal->applications);
+
+    IF_FREE_LIST(internal->directories, eina_stringshare_del);
+    IF_FREE_LIST(internal->app_dirs, efreet_menu_app_dir_free);
+    IF_FREE_LIST(internal->app_pool, efreet_menu_desktop_free);
+    IF_FREE_LIST(internal->directory_dirs, eina_stringshare_del);
+    IF_FREE_HASH(internal->directory_cache);
+
+    IF_FREE_LIST(internal->moves, efreet_menu_move_free);
+    IF_FREE_LIST(internal->filters, efreet_menu_filter_free);
+
+    IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
+
+    IF_FREE_LIST(internal->layout, efreet_menu_layout_free);
+    IF_FREE_LIST(internal->default_layout, efreet_menu_layout_free);
+
+    FREE(internal);
+}
+
+/**
+ * @internal
+ * @return Returns the XDG_MENU_PREFIX env variable or "" if none set
+ * @brief Retrieves the XDG_MENU_PREFIX or "" if not set.
+ */
+static const char *
+efreet_menu_prefix_get(void)
+{
+    if (efreet_menu_prefix) return efreet_menu_prefix;
+
+    efreet_menu_prefix = getenv("XDG_MENU_PREFIX");
+    if (!efreet_menu_prefix) efreet_menu_prefix = "";
+
+    return efreet_menu_prefix;
+}
+
+/**
+ * @internal
+ * @param menu The menu to populate
+ * @param xml The xml dom tree to populate from
+ * @return Returns 1 if this XML tree is valid, 0 otherwise
+ * @brief Populates the given menu from the given xml structure
+ *
+ * We walk the Menu children backwards. The reason for this is so that we
+ * can deal with all the things that make us select the 'last' element
+ * (MergeFile, Directory, etc). We'll see the last one first and can deal
+ * with it right away.
+ */
+static int
+efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml)
+{
+    Efreet_Xml *child;
+    Eina_List *l;
+    int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+
+    EINA_LIST_REVERSE_FOREACH(xml->children, l, child)
+    {
+        cb = eina_hash_find(efreet_menu_handle_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(internal, child))
+                return 0;
+        }
+        else
+        {
+            WRN("Unknown XML tag: %s", child->tag);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent Menu
+ * @param xml The xml that defines the menu
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the sub-menu nodes of the XML file
+ */
+static int
+efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Efreet_Menu_Internal *internal, *match;
+
+    efreet_menu_create_sub_menu_list(parent);
+
+    internal = efreet_menu_internal_new();
+    if (!internal) return 0;
+    internal->file.path = eina_stringshare_add(parent->file.path);
+    if (!efreet_menu_handle_menu(internal, xml))
+    {
+        efreet_menu_internal_free(internal);
+        return 0;
+    }
+
+    /* if this menu already exists we just take this one and stick it on the
+     * start of the existing one */
+    if ((match = eina_list_search_unsorted(parent->sub_menus,
+                                           EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
+                                           internal)))
+    {
+
+        efreet_menu_concatenate(match, internal);
+        efreet_menu_internal_free(internal);
+    }
+    else
+        parent->sub_menus = eina_list_prepend(parent->sub_menus, internal);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the AppDir tag
+ */
+static int
+efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    const char *path;
+    Efreet_Menu_App_Dir *app_dir;
+
+    if (!parent || !xml) return 0;
+
+    efreet_menu_create_app_dirs_list(parent);
+    path = efreet_menu_path_get(parent, xml->text);
+    if (!path) return 0;
+
+    /* we've already got this guy in our list we can skip it */
+    if (eina_list_search_unsorted(parent->app_dirs,
+                                  EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
+                                  path))
+    {
+        eina_stringshare_del(path);
+        return 1;
+    }
+
+    app_dir = efreet_menu_app_dir_new();
+    app_dir->path = path;
+
+    parent->app_dirs = eina_list_prepend(parent->app_dirs, app_dir);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml UNUSED
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the DefaultAppDirs
+ */
+static int
+efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
+{
+    Eina_List *prepend = NULL;
+    Eina_List *dirs;
+    char *dir;
+
+    if (!parent) return 0;
+
+    efreet_menu_create_app_dirs_list(parent);
+    dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
+                                                                    "applications");
+    EINA_LIST_FREE(dirs, dir)
+    {
+        if (!eina_list_search_unsorted(parent->app_dirs,
+                                       EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
+                                       dir))
+        {
+            Efreet_Menu_App_Dir *app_dir;
+
+            app_dir = efreet_menu_app_dir_new();
+            app_dir->path = eina_stringshare_ref(dir);
+
+            prepend = eina_list_append(prepend, app_dir);
+        }
+
+        eina_stringshare_del(dir);
+    }
+    parent->app_dirs = eina_list_merge(prepend, parent->app_dirs);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the DirectoryDir tag
+ */
+static int
+efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    const char *path;
+
+    if (!parent || !xml) return 0;
+
+    efreet_menu_create_directory_dirs_list(parent);
+    path = efreet_menu_path_get(parent, xml->text);
+    if (!path) return 0;
+
+    /* we've already got this guy in our list we can skip it */
+    if (eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), path))
+    {
+        eina_stringshare_del(path);
+        return 1;
+    }
+
+    parent->directory_dirs = eina_list_prepend(parent->directory_dirs, path);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml UNUSED
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the DefaultDirectoryDirs tag
+ */
+static int
+efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
+{
+    Eina_List *dirs;
+    char *dir;
+
+    if (!parent) return 0;
+
+    efreet_menu_create_directory_dirs_list(parent);
+    dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
+                                                            "desktop-directories");
+    EINA_LIST_FREE(dirs, dir)
+    {
+        if (!eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), dir))
+            parent->directory_dirs = eina_list_prepend(parent->directory_dirs, eina_stringshare_ref(dir));
+        eina_stringshare_del(dir);
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent Menu
+ * @param xml The xml to work with
+ * @return Returns 1 on success or 0 on failure
+ * @brief Sets the menu name from the given XML fragment.
+ */
+static int
+efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    /* not allowed to have two Name settings in a menu */
+    if (parent->name.internal)
+    {
+        INF("efreet_menu_handle_name() setting second name into menu");
+        return 0;
+    }
+    /* ignore the name if it is empty */
+    if (!xml->text) return 1;
+
+    /* ignore the name if it contains a / */
+    if (strchr(xml->text, '/')) return 1;
+
+    parent->name.internal = eina_stringshare_add(xml->text);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Directory tag
+ *
+ * This just adds the given directory path to a list which we'll walk once
+ * we've traversed the entire menu into memory.
+ */
+static int
+efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    if (!parent || !xml) return 0;
+
+    efreet_menu_create_directories_list(parent);
+    parent->directories = eina_list_prepend(parent->directories, eina_stringshare_add(xml->text));
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the OnlyUnallocated tag
+ */
+static int
+efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    if (!parent || !xml) return 0;
+
+    /* a later instance has been seen so we can ignore this one */
+    if (parent->seen_allocated) return 1;
+
+    parent->seen_allocated = 1;
+    parent->only_unallocated = 1;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the NotOnlyUnallocated tag
+ */
+static int
+efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    if (!parent || !xml) return 0;
+
+    /* a later instance has been seen so we can ignore this one */
+    if (parent->seen_allocated) return 1;
+
+    parent->seen_allocated = 1;
+    parent->only_unallocated = 0;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Deleted tag
+ */
+static int
+efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    if (!parent || !xml) return 0;
+
+    /* a later instance has been seen so we can ignore this one */
+    if (parent->seen_deleted) return 1;
+
+    parent->seen_deleted = 1;
+    parent->deleted = 1;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the NotDeleted tag
+ */
+static int
+efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    if (!parent || !xml) return 0;
+
+    /* a later instance has been seen so we can ignore this one */
+    if (parent->seen_deleted) return 1;
+
+    parent->seen_deleted = 1;
+    parent->deleted = 0;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The XML tree to work with
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles parsing the Include tag and all subtags
+ */
+static int
+efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    return efreet_menu_handle_filter(parent, xml,
+                                EFREET_MENU_FILTER_INCLUDE);
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Exclude tag and all subtags
+ */
+static int
+efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    return efreet_menu_handle_filter(parent, xml,
+                                EFREET_MENU_FILTER_EXCLUDE);
+}
+
+/**
+ * @internal
+ * @param op The filter operation
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Filename tag
+ */
+static int
+efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    if (!op || !xml) return 0;
+
+    op->filenames = eina_list_append(op->filenames, eina_stringshare_add(xml->text));
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param op The filter operation
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Category tag
+ */
+static int
+efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    if (!op || !xml) return 0;
+
+
+    op->categories = eina_list_append(op->categories, eina_stringshare_add(xml->text));
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param op The filter operation
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the All tag and all subtags
+ */
+static int
+efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    if (!op || !xml) return 0;
+
+    op->all = 1;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param op The filter operation
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the And tag and all subtags
+ */
+static int
+efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    if (!op || !xml) return 0;
+
+    return efreet_menu_handle_filter_child_op(op, xml,
+                            EFREET_MENU_FILTER_OP_AND);
+}
+
+/**
+ * @internal
+ * @param op The filter operation
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Or tag and all subtags
+ */
+static int
+efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    if (!op || !xml) return 0;
+
+    return efreet_menu_handle_filter_child_op(op, xml,
+                            EFREET_MENU_FILTER_OP_OR);
+}
+
+/**
+ * @internal
+ * @param op The filter operation
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Not tag and all subtags
+ */
+static int
+efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    if (!op || !xml) return 0;
+
+    return efreet_menu_handle_filter_child_op(op, xml,
+                            EFREET_MENU_FILTER_OP_NOT);
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the MergeFile tag
+ */
+static int
+efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Eina_List *l;
+    const char *path = NULL;
+    const char *attr = NULL;
+    int is_path = 1;
+    int ret = 1;
+
+    if (!parent || !xml) return 0;
+
+    /* check to see if this is a path or parent type */
+    attr = efreet_xml_attribute_get(xml, "type");
+    if (attr && !strcmp(attr, "parent"))
+        is_path = 0;
+
+    /* we're given a path */
+    if (is_path)
+        path = efreet_menu_path_get(parent, xml->text);
+
+    /* need to find the next menu with the same name as ours in the config
+     * dir after ours (if we're in a config dir) */
+    else
+    {
+        Eina_List *search_dirs;
+        const char *dir, *p;
+
+        if (!parent->file.path)
+        {
+            INF("efreet_menu_handle_merge_file() missing menu path ...");
+            return 0;
+        }
+
+        search_dirs = efreet_config_dirs_get();
+
+        /* we need to find the next menu with the same name in the directory
+         * after the on the the menu was found in. to do that we first check
+         * if it's in the config_home_directory() if so we need to search
+         * all of the dirs. If it isn't in the config home directory then we
+         * scan the search dirs and look for it. The search_dirs list will
+         * be left at the next pointer so we can start looking for the menu
+         * from that point */
+
+        dir = efreet_config_home_get();
+        if (strncmp(dir, parent->file.path, eina_stringshare_strlen(dir)))
+        {
+            EINA_LIST_FOREACH(search_dirs, l, dir)
+            {
+                if (!strncmp(dir, parent->file.path, eina_stringshare_strlen(dir)))
+                    break;
+            }
+        }
+
+        if (!dir)
+        {
+            INF("efreet_menu_handle_merge_file() failed to find "
+                    "menu parent directory");
+            return 0;
+        }
+
+        /* the parent file path may have more path then just the base
+         * directory so we need to append that as well */
+        p = parent->file.path + eina_stringshare_strlen(dir);
+
+        /* whatever dirs are left in the search dir we need to look for the
+         * menu with the same relative filename */
+        EINA_LIST_FOREACH(search_dirs, l, dir)
+        {
+            char file[PATH_MAX];
+
+            snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""),
+                                                        parent->file.name);
+            if (ecore_file_exists(file))
+            {
+                path = eina_stringshare_add(file);
+                break;
+            }
+        }
+    }
+
+    /* nothing to do if no file found */
+    if (!path) return 1;
+
+    if (!efreet_menu_merge(parent, xml, path))
+        ret = 0;
+
+    eina_stringshare_del(path);
+
+    return ret;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu to merge into
+ * @param xml The XML to be merged
+ * @param path The path to the .menu file to merge
+ */
+static int
+efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
+{
+    Efreet_Xml *merge_xml;
+    Efreet_Menu_Internal *internal;
+    char rp[PATH_MAX];
+
+    if (!parent || !xml || !path) return 0;
+
+    /* do nothing if the file doesn't exist */
+    if (!ecore_file_exists(path)) return 1;
+
+    if (!realpath(path, rp))
+    {
+        INF("efreet_menu_merge() unable to get real path for %s", path);
+        return 0;
+    }
+
+    /* don't merge the same path twice */
+    if (eina_hash_find(efreet_merged_menus, rp))
+    {
+        return 1;
+    }
+
+    eina_hash_add(efreet_merged_menus, rp, (void *)1);
+
+    merge_xml = efreet_xml_new(rp);
+
+    if (!merge_xml)
+    {
+        INF("efreet_menu_merge() failed to read in the "
+                "merge file (%s)", rp);
+        return 0;
+    }
+
+    internal = efreet_menu_internal_new();
+    if (!internal) return 0;
+    efreet_menu_path_set(internal, path);
+    efreet_menu_handle_menu(internal, merge_xml);
+    efreet_menu_concatenate(parent, internal);
+    efreet_menu_internal_free(internal);
+
+    efreet_xml_del(merge_xml);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the MergeDir tag
+ */
+static int
+efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    const char *path;
+    int ret;
+
+    if (!parent || !xml || !xml->text) return 0;
+
+    path = efreet_menu_path_get(parent, xml->text);
+    if (!path) return 1;
+    if (!ecore_file_exists(path))
+    {
+        eina_stringshare_del(path);
+        return 1;
+    }
+
+    ret = efreet_menu_merge_dir(parent, xml, path);
+    eina_stringshare_del(path);
+
+    return ret;
+}
+
+/**
+ * @internal
+ * @param parent the parent menu of the merge
+ * @param xml The xml tree
+ * @param path The path to the merge directory
+ * @return Returns 1 on success or 0 on failure
+ * @brief Find all of the .menu files in the given directory and merge them
+ * into the @a parent menu.
+ */
+static int
+efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
+{
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *info;
+
+    if (!parent || !xml || !path) return 0;
+
+    /* check to see if we've merged this directory already */
+    if (eina_hash_find(efreet_merged_dirs, path)) return 1;
+    eina_hash_add(efreet_merged_dirs, path, (void *)1);
+
+    it = eina_file_direct_ls(path);
+    if (!it) return 1;
+
+    EINA_ITERATOR_FOREACH(it, info)
+    {
+        char *p;
+
+        p = strrchr(info->path + info->name_start, '.');
+        if (!p) continue;
+        if (strcmp(p, ".menu")) continue;
+
+        if (!efreet_menu_merge(parent, xml, info->path))
+        {
+            eina_iterator_free(it);
+            return 0;
+        }
+    }
+    eina_iterator_free(it);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the DefaultMergeDirs tag
+ */
+static int
+efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Eina_List *dirs;
+    char path[PATH_MAX], *p, *pp;
+#ifndef STRICT_SPEC
+    char parent_path[PATH_MAX];
+#endif
+    const char *prefix;
+
+    if (!parent || !xml) return 0;
+
+    prefix = efreet_menu_prefix_get();
+    if (!strcmp(prefix, "gnome-") &&
+            (!strcmp(parent->file.name, "gnome-applications.menu")))
+    {
+        p = alloca(sizeof("applications"));
+        memcpy(p, "applications", sizeof("applications"));
+    }
+    else if ((!strcmp(prefix, "kde-") &&
+            (!strcmp(parent->file.name, "kde-applications.menu"))))
+    {
+        p = alloca(sizeof("applications"));
+        memcpy(p, "applications", sizeof("applications"));
+    }
+    else
+    {
+        char *s;
+        size_t len;
+
+        len = strlen(parent->file.name) + 1;
+        p = alloca(len);
+        memcpy(p, parent->file.name, len);
+        s = strrchr(p, '.');
+        if (s) *s = '\0';
+    }
+    snprintf(path, sizeof(path), "menus/%s-merged", p);
+
+    dirs = efreet_default_dirs_get(efreet_config_home_get(),
+                                    efreet_config_dirs_get(), path);
+
+    EINA_LIST_FREE(dirs, pp)
+    {
+        efreet_menu_merge_dir(parent, xml, pp);
+        eina_stringshare_del(pp);
+    }
+#ifndef STRICT_SPEC
+    /* Also check the path of the parent file */
+    snprintf(parent_path, sizeof(parent_path), "%s/%s", parent->file.path, path);
+    efreet_menu_merge_dir(parent, xml, parent_path);
+#endif
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the LegacyDir tag
+ */
+static int
+efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Efreet_Menu_Internal *legacy;
+
+    if (!parent || !xml) return 0;
+
+    legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text,
+                                efreet_xml_attribute_get(xml, "prefix"));
+    if (legacy)
+    {
+        efreet_menu_concatenate(parent, legacy);
+        efreet_menu_internal_free(legacy);
+    }
+
+    return 1;
+
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param legacy_dir The legacy directory path
+ * @param prefix The legacy directory prefix if one set
+ * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy
+ * @brief Handles the process of merging @a legacy_dir into @a parent menu
+ */
+static Efreet_Menu_Internal *
+efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
+                                        Efreet_Menu_Internal *parent,
+                                        const char *legacy_dir,
+                                        const char *prefix)
+{
+    const char *path;
+    Efreet_Menu_Internal *legacy_internal;
+    Efreet_Menu_Filter *filter;
+    Efreet_Menu_App_Dir *app_dir;
+    int count = 0;
+    Eina_Iterator *it;
+
+    if (!parent || !legacy_dir) return 0;
+
+    path = efreet_menu_path_get(parent, legacy_dir);
+
+    /* nothing to do if the legacy path doesn't exist */
+    if (!path || !ecore_file_exists(path))
+    {
+        eina_stringshare_del(path);
+        return NULL;
+    }
+
+    legacy_internal = efreet_menu_internal_new();
+    if (!legacy_internal)
+        return NULL;
+    legacy_internal->name.internal = eina_stringshare_add(ecore_file_file_get(path));
+
+    /* add the legacy dir as an app dir */
+    app_dir = efreet_menu_app_dir_new();
+    app_dir->path = eina_stringshare_add(path);
+    app_dir->legacy = 1;
+    if (prefix && !strchr(prefix, '/')) app_dir->prefix = eina_stringshare_add(prefix);
+
+    efreet_menu_create_app_dirs_list(legacy_internal);
+    legacy_internal->app_dirs = eina_list_append(legacy_internal->app_dirs, app_dir);
+#ifndef STRICT_SPEC
+    if (root)
+    {
+        /* XXX This seems wrong, but it makes efreet pass the fdo tests */
+        app_dir = efreet_menu_app_dir_new();
+        app_dir->path = eina_stringshare_add(path);
+        app_dir->legacy = 1;
+        if (prefix && !strchr(prefix, '/')) app_dir->prefix = eina_stringshare_add(prefix);
+        root->app_dirs = eina_list_append(root->app_dirs, app_dir);
+    }
+#endif
+
+    /* add the legacy dir as a directory dir */
+    efreet_menu_create_directory_dirs_list(legacy_internal);
+    legacy_internal->directory_dirs = eina_list_append(legacy_internal->directory_dirs, eina_stringshare_add(path));
+
+    /* setup a filter for all the conforming .desktop files in the legacy
+     * dir */
+    filter = efreet_menu_filter_new();
+    if (!filter)
+    {
+        efreet_menu_internal_free(legacy_internal);
+        return NULL;
+    }
+    filter->type = EFREET_MENU_FILTER_INCLUDE;
+
+    filter->op->type = EFREET_MENU_FILTER_OP_OR;
+
+    efreet_menu_create_filter_list(legacy_internal);
+    legacy_internal->filters = eina_list_append(legacy_internal->filters, filter);
+
+    it = eina_file_direct_ls(path);
+    if (it)
+    {
+        Eina_File_Direct_Info *info;
+
+        EINA_ITERATOR_FOREACH(it, info)
+        {
+            Efreet_Desktop *desktop = NULL;
+            char buf[PATH_MAX];
+            char *exten;
+            const char *fname;
+
+            fname = info->path + info->name_start;
+            /* recurse into sub directories */
+            if (ecore_file_is_dir(info->path))
+            {
+                Efreet_Menu_Internal *ret;
+
+                ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
+                        legacy_internal, info->path, prefix);
+                if (!ret)
+                {
+                    efreet_menu_internal_free(legacy_internal);
+                    eina_stringshare_del(path);
+                    eina_iterator_free(it);
+                    return NULL;
+                }
+
+                efreet_menu_create_sub_menu_list(legacy_internal);
+                legacy_internal->sub_menus = eina_list_prepend(legacy_internal->sub_menus, ret);
+
+                continue;
+            }
+
+            if (!strcmp(fname, ".directory"))
+            {
+                legacy_internal->directory = efreet_desktop_get(info->path);
+                if (legacy_internal->directory
+                        && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
+                {
+                    efreet_desktop_free(legacy_internal->directory);
+                    legacy_internal->directory = NULL;
+                }
+                continue;
+            }
+
+            exten = strrchr(fname, '.');
+
+            if (exten && !strcmp(exten, ".desktop"))
+                desktop = efreet_desktop_get(info->path);
+
+            if (!desktop) continue;
+
+            /* if the .desktop has categories it isn't legacy */
+            if (efreet_desktop_category_count_get(desktop) != 0)
+            {
+                efreet_desktop_free(desktop);
+                continue;
+            }
+
+            /* XXX: This will disappear when the .desktop is free'd */
+            efreet_desktop_category_add(desktop, "Legacy");
+
+            if (prefix)
+            {
+                snprintf(buf, sizeof(buf), "%s%s", prefix, fname);
+                filter->op->filenames = eina_list_append(filter->op->filenames, eina_stringshare_add(buf));
+            }
+            else
+                filter->op->filenames = eina_list_append(filter->op->filenames, eina_stringshare_add(fname));
+
+            count++;
+            efreet_desktop_free(desktop);
+        }
+        eina_iterator_free(it);
+    }
+
+    eina_stringshare_del(path);
+    return legacy_internal;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml UNUSED
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the KDELegacyDirs tag
+ */
+static int
+efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
+{
+    Eina_List *l;
+    const char *dir;
+
+    if (!parent) return 0;
+
+    if (!efreet_menu_kde_legacy_dirs) return 1;
+
+    /* XXX if one _helper() call succeeds, we return success. should this be flipped?
+     * (return fail if on of them failed) */
+    EINA_LIST_FOREACH(efreet_menu_kde_legacy_dirs, l, dir)
+    {
+        Efreet_Menu_Internal *kde;
+
+        kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde");
+        if (kde)
+        {
+            efreet_menu_concatenate(parent, kde);
+            efreet_menu_internal_free(kde);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Move tag and all subtags
+ */
+static int
+efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Efreet_Xml *child;
+    Eina_List *l;
+
+    if (!parent || !xml) return 0;
+
+    efreet_menu_create_move_list(parent);
+
+    EINA_LIST_FOREACH(xml->children, l, child)
+    {
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+
+        cb = eina_hash_find(efreet_menu_move_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(parent, child))
+                return 0;
+        }
+        else
+        {
+            INF("efreet_menu_handle_move() unknown tag found "
+                    "in Move (%s)", child->tag);
+            return 0;
+        }
+    }
+
+    parent->current_move = NULL;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Old tag
+ */
+static int
+efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Efreet_Menu_Move *move;
+
+    if (!parent || !xml || !xml->text) return 0;
+
+    if (parent->current_move)
+    {
+        INF("efreet_menu_handle_old() saw second <Old> "
+                "before seeing <New>");
+        return 0;
+    }
+
+    /* If we already moved this menu, remove the old move */
+    /* XXX This seems wrong, but it makes efreet pass the fdo tests */
+#ifndef STRICT_SPEC
+    move = eina_list_search_unsorted(parent->moves,
+                                     EINA_COMPARE_CB(efreet_menu_cb_move_compare),
+                                     xml->text);
+    if (move)
+    {
+        efreet_menu_move_free(move);
+        parent->moves = eina_list_remove(parent->moves, move);
+    }
+#endif
+
+    move = efreet_menu_move_new();
+    move->old_name = eina_stringshare_add(xml->text);
+
+    parent->current_move = move;
+    parent->moves = eina_list_append(parent->moves, move);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the New tag
+ */
+static int
+efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    if (!parent || !xml || !xml->text) return 0;
+
+    if (!parent->current_move)
+    {
+        INF("efreet_menu_handle_new() saw New before seeing Old");
+        return 0;
+    }
+
+    parent->current_move->new_name = eina_stringshare_add(xml->text);
+    parent->current_move = NULL;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the Layout tag and all subtags
+ */
+static int
+efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    Efreet_Xml *child;
+    Eina_List *l;
+
+    if (!parent || !xml) return 0;
+
+    /* We use the last existing layout */
+    if (parent->layout) return 1;
+
+    efreet_menu_create_layout_list(parent);
+
+    EINA_LIST_FOREACH(xml->children, l, child)
+    {
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+
+        cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(parent, child, 0))
+                return 0;
+        }
+        else
+        {
+            INF("efreet_menu_handle_move() unknown tag found "
+                    "in Layout (%s)", child->tag);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The xml tree
+ * @return Returns 1 on success or 0 on failure
+ * @brief Handles the DefaultLayout tag
+ */
+static int
+efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
+{
+    const char *val;
+    Efreet_Xml *child;
+    Eina_List *l;
+
+    if (!parent || !xml) return 0;
+
+    /* We use the last existing layout */
+    if (parent->default_layout) return 1;
+
+    val = efreet_xml_attribute_get(xml, "show_empty");
+    if (val) parent->show_empty = !strcmp(val, "true");
+
+    val = efreet_xml_attribute_get(xml, "inline");
+    if (val) parent->in_line = !strcmp(val, "true");
+
+    val = efreet_xml_attribute_get(xml, "inline_limit");
+    if (val) parent->inline_limit = atoi(val);
+
+    val = efreet_xml_attribute_get(xml, "inline_header");
+    if (val) parent->inline_header = !strcmp(val, "true");
+
+    val = efreet_xml_attribute_get(xml, "inline_alias");
+    if (val) parent->inline_alias = !strcmp(val, "true");
+
+    efreet_menu_create_default_layout_list(parent);
+
+    EINA_LIST_FOREACH(xml->children, l, child)
+    {
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+
+        cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(parent, child, 1))
+                return 0;
+        }
+        else
+        {
+            INF("efreet_menu_handle_move() unknown tag found in "
+                    "DefaultLayout (%s)", child->tag);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int
+efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
+{
+    Efreet_Menu_Layout *layout;
+    const char *val;
+
+    if (!parent || !xml) return 0;
+
+    if (!xml->text)
+    {
+        INF("efreet_menu_handle_layout_menuname() The Menuname tag in "
+                "layout needs a filename.");
+        return 0;
+    }
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_MENUNAME;
+    layout->name = eina_stringshare_add(xml->text);
+
+    val = efreet_xml_attribute_get(xml, "show_empty");
+    if (val) layout->show_empty = !strcmp(val, "true");
+
+    val = efreet_xml_attribute_get(xml, "inline");
+    if (val) layout->in_line = !strcmp(val, "true");
+
+    val = efreet_xml_attribute_get(xml, "inline_limit");
+    if (val) layout->inline_limit = atoi(val);
+
+    val = efreet_xml_attribute_get(xml, "inline_header");
+    if (val) layout->inline_header = !strcmp(val, "true");
+
+    val = efreet_xml_attribute_get(xml, "inline_alias");
+    if (val) layout->inline_alias = !strcmp(val, "true");
+
+    if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
+    else parent->layout = eina_list_append(parent->layout, layout);
+
+    return 1;
+}
+
+static int
+efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
+{
+    Efreet_Menu_Layout *layout;
+
+    if (!parent || !xml) return 0;
+
+    if (!xml->text)
+    {
+        INF("efreet_menu_handle_layout_filename() The Filename tag in "
+                "layout needs a filename.");
+        return 0;
+    }
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_FILENAME;
+    layout->name = eina_stringshare_add(xml->text);
+
+    if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
+    else parent->layout = eina_list_append(parent->layout, layout);
+
+    return 1;
+}
+
+static int
+efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
+{
+    Efreet_Menu_Layout *layout;
+
+    if (!parent || !xml) return 0;
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_SEPARATOR;
+    if (def)
+        parent->default_layout = eina_list_append(parent->default_layout, layout);
+    else
+        parent->layout = eina_list_append(parent->layout, layout);
+    return 1;
+}
+
+static int
+efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
+{
+    Efreet_Menu_Layout *layout;
+    const char *attr;
+
+    if (!parent || !xml) return 0;
+
+    attr = efreet_xml_attribute_get(xml, "type");
+    if (!attr)
+    {
+        INF("efreet_menu_handle_layout_merge() The Merge tag in layout "
+                "needs a type attribute.");
+        return 0;
+    }
+
+    if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
+    {
+        INF("efreet_menu_handle_layout_merge() The type attribute for "
+                "the Merge tag contains an unknown value (%s).", attr);
+        return 0;
+    }
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_MERGE;
+    layout->name = eina_stringshare_add(attr);
+
+    if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
+    else parent->layout = eina_list_append(parent->layout, layout);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param parent The parent menu
+ * @param xml The XML tree to parse
+ * @param type The type of filter
+ * @return Returns 1 on success or 0 on failure
+ * @brief Parses the given XML tree and adds the filter to the parent menu
+ */
+static int
+efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
+                                        Efreet_Menu_Filter_Type type)
+{
+    Efreet_Menu_Filter *filter;
+
+    efreet_menu_create_filter_list(parent);
+
+    /* filters have a default or relationship */
+    filter = efreet_menu_filter_new();
+    if (!filter) return 0;
+    filter->type = type;
+    filter->op->type = EFREET_MENU_FILTER_OP_OR;
+
+    if (!efreet_menu_handle_filter_op(filter->op, xml))
+    {
+        efreet_menu_filter_free(filter);
+        return 0;
+    }
+
+    parent->filters = eina_list_prepend(parent->filters, filter);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param op The operation to work with
+ * @param xml The XML tree representing this operation
+ * @return Returns 1 on success or 0 on failure
+ * @brief Parses the given XML tree and populates the operation
+ */
+static int
+efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
+{
+    Efreet_Xml *child;
+    Eina_List *l;
+
+    EINA_LIST_FOREACH(xml->children, l, child)
+    {
+        int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+
+        cb = eina_hash_find(efreet_menu_filter_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(op, child))
+                return 0;
+        }
+        else
+        {
+            INF("efreet_menu_handle_filter_op() unknown tag in filter (%s)", child->tag);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_Filter on success or NULL on failure
+ * @brief Creates and initializes an Efreet_Menu_Filter object
+ */
+static Efreet_Menu_Filter *
+efreet_menu_filter_new(void)
+{
+    Efreet_Menu_Filter *filter;
+
+    filter = NEW(Efreet_Menu_Filter, 1);
+    if (!filter) return NULL;
+    filter->op = efreet_menu_filter_op_new();
+    if (!filter->op)
+    {
+        FREE(filter);
+        return NULL;
+    }
+
+    return filter;
+}
+
+/**
+ * @internal
+ * @param filter The filter to work with
+ * @return Returns no data
+ * @brief Frees the given filter and all data
+ */
+static void
+efreet_menu_filter_free(Efreet_Menu_Filter *filter)
+{
+    if (!filter) return;
+
+    if (filter->op) efreet_menu_filter_op_free(filter->op);
+    filter->op = NULL;
+
+    FREE(filter);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_Layout on success or NULL on failure
+ * @brief Creates and initializes an Efreet_Menu_Layout object
+ */
+static Efreet_Menu_Layout *
+efreet_menu_layout_new(void)
+{
+    Efreet_Menu_Layout *layout;
+
+    layout = NEW(Efreet_Menu_Layout, 1);
+    layout->show_empty = -1;
+    layout->in_line = -1;
+    layout->inline_limit = -1;
+    layout->inline_header = -1;
+    layout->inline_alias = -1;
+
+    return layout;
+}
+
+/**
+ * @internal
+ * @param filter The filter to work with
+ * @return Returns no data
+ * @brief Frees the given filter and all data
+ */
+static void
+efreet_menu_layout_free(Efreet_Menu_Layout *layout)
+{
+    if (!layout) return;
+
+    IF_RELEASE(layout->name);
+    FREE(layout);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
+ * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
+ */
+static Efreet_Menu_Filter_Op *
+efreet_menu_filter_op_new(void)
+{
+    Efreet_Menu_Filter_Op *op;
+
+    op = NEW(Efreet_Menu_Filter_Op, 1);
+
+    return op;
+}
+
+/**
+ * @internal
+ * @param op The operation to work with
+ * @return Returns no value.
+ * @brief Frees the given operation and all sub data
+ */
+static void
+efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
+{
+    if (!op) return;
+
+    IF_FREE_LIST(op->categories, eina_stringshare_del);
+    IF_FREE_LIST(op->filenames, eina_stringshare_del);
+    IF_FREE_LIST(op->filters, efreet_menu_filter_op_free);
+
+    FREE(op);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
+ * @brief Creates and returns an Efreet_Menu_Desktop
+ */
+static Efreet_Menu_Desktop *
+efreet_menu_desktop_new(void)
+{
+    Efreet_Menu_Desktop *md;
+
+    md = NEW(Efreet_Menu_Desktop, 1);
+
+    return md;
+}
+
+/**
+ * @internal
+ * @param md The Efreet_Menu_Desktop to free
+ * @return Returns no value
+ * @brief Frees the given structure
+ */
+static void
+efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
+{
+    IF_RELEASE(md->id);
+    if (md->desktop) efreet_desktop_free(md->desktop);
+    FREE(md);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu on success or NULL on failure
+ * @brief Creates and returns an Efreet_Menu
+ */
+static Efreet_Menu *
+efreet_menu_entry_new(void)
+{
+    Efreet_Menu *entry;
+
+    entry = NEW(Efreet_Menu, 1);
+
+    return entry;
+}
+
+EAPI void
+efreet_menu_free(Efreet_Menu *entry)
+{
+    Efreet_Menu *sub;
+
+    if (!entry) return;
+
+    IF_RELEASE(entry->name);
+    IF_RELEASE(entry->icon);
+    EINA_LIST_FREE(entry->entries, sub)
+        efreet_menu_free(sub);
+    IF_RELEASE(entry->id);
+    if (entry->desktop) efreet_desktop_free(entry->desktop);
+    FREE(entry);
+}
+
+/**
+ * @internal
+ * @param op The op to add a child too
+ * @param xml The XML tree of the child
+ * @param type The type of child to add
+ * @return Returns 1 on success or 0 on failure
+ * @brief Parses the given XML tree and populates a new child operation.
+ */
+static int
+efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
+                                                Efreet_Menu_Filter_Op_Type type)
+{
+    Efreet_Menu_Filter_Op *child_op;
+
+    child_op = efreet_menu_filter_op_new();
+    child_op->type = type;
+
+    if (!efreet_menu_handle_filter_op(child_op, xml))
+    {
+        efreet_menu_filter_op_free(child_op);
+        return 0;
+    }
+
+    op->filters = eina_list_append(op->filters, child_op);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param menu The menu to work with
+ * @param only_unallocated Do we only look for unallocated items?
+ * @return Returns 1 if we've successfully processed the menu, 0 otherwise
+ * @brief Handles the processing of the menu data to retrieve the .desktop
+ * files for the menu
+ */
+static int
+efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
+{
+    Eina_List *l;
+
+    /* a menu _MUST_ have a name */
+    if (!internal->name.internal || (internal->name.internal[0] == '\0'))
+        return 0;
+
+    /* handle filtering out .desktop files as needed. This deals with all
+     * .desktop files */
+    efreet_menu_process_filters(internal, only_unallocated);
+
+    if (internal->sub_menus)
+    {
+        Efreet_Menu_Internal *sub_internal;
+
+        EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
+        {
+            sub_internal->parent = internal;
+            efreet_menu_process(sub_internal, only_unallocated);
+        }
+    }
+
+    return 1;
+}
+
+/* This will walk through all of the app dirs and load all the .desktop
+ * files into the cache for the menu. The .desktop files will have their
+ * allocated flag set to 0 */
+static int
+efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
+{
+    Eina_List *l;
+
+    /* Scan application directories for .desktop files */
+    if (!efreet_menu_app_dirs_process(internal))
+        return 0;
+
+    /* Scan directory directories for .directory file */
+    if (!efreet_menu_directory_dirs_process(internal))
+        return 0;
+
+    if (internal->sub_menus)
+    {
+        Efreet_Menu_Internal *sub_internal;
+
+        EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
+        {
+            sub_internal->parent = internal;
+            efreet_menu_process_dirs(sub_internal);
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param menu the menu to process
+ * @param only_unallocated Only handle menus that deal with unallocated items
+ * @return Returns no value
+ * @brief Handles the processing of the filters attached to the given menu.
+ *
+ * For each include filter we'll add the items to our applications array. Each
+ * exclude filter will remove items from the applications array
+ */
+static void
+efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
+{
+    Efreet_Menu_Filter *filter;
+    Efreet_Menu_Desktop *md;
+    Eina_List *l, *ll;
+
+    int included = 0;
+
+    /* nothing to do if we're checking the other option */
+    if (only_unallocated != internal->only_unallocated) return;
+
+    internal->applications = eina_list_free(internal->applications);
+
+    if (!internal->filters) return;
+
+    EINA_LIST_FOREACH(internal->filters, l, filter)
+    {
+        /* skip excludes until we get an include */
+        if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
+            continue;
+        included = 1;
+
+        if (filter->type == EFREET_MENU_FILTER_INCLUDE)
+        {
+            Eina_Hash *matches;
+
+            matches = eina_hash_string_superfast_new(NULL);
+            internal->applications = efreet_menu_process_app_pool(internal->app_pool, internal->applications,
+                                        matches, filter, internal->only_unallocated);
+            if (internal->parent)
+            {
+                Efreet_Menu_Internal *parent;
+
+                parent = internal->parent;
+                do {
+                    internal->applications = efreet_menu_process_app_pool(parent->app_pool,
+                                                internal->applications, matches, filter,
+                                                internal->only_unallocated);
+                } while ((parent = parent->parent));
+            }
+            eina_hash_free(matches);
+        }
+        else
+        {
+            /* check each item in our menu so far and see if it's excluded */
+            l = internal->applications;
+            while ((md = eina_list_data_get(l)))
+            {
+                ll = eina_list_next(l);
+                if (efreet_menu_filter_matches(filter->op, md))
+                    internal->applications = eina_list_remove_list(internal->applications, l);
+                l = ll;
+            }
+        }
+    }
+
+    /* sort the menu applications. we do this in process filters so it will only
+     * be done once per menu.*/
+    if (eina_list_count(internal->applications))
+    {
+        Eina_List           *l2;
+
+        EINA_LIST_FOREACH_SAFE(internal->applications, l, l2, md)
+        {
+            if (md->desktop->no_display)
+                internal->applications = eina_list_remove_list(internal->applications, l);
+        }
+        internal->applications = eina_list_sort(internal->applications,
+                                                eina_list_count(internal->applications),
+                                                EINA_COMPARE_CB(efreet_menu_cb_md_compare));
+    }
+}
+
+/**
+ * @internal
+ * @param pool The app pool to iterate
+ * @param applications The list of applications to append too
+ * @param matches The hash of previously matched ids
+ * @param filter The menu filter to run on the pool items
+ * @param only_unallocated Do we check only unallocated pool items?
+ * @return Returns no value.
+ * @brief This will iterate the items in @a pool and append them to @a
+ * applications if they match the @a filter given and aren't previoulsy entered
+ * in @a matches. If @a only_unallocated is set we'll only only at the
+ * .desktop files that haven't been previoulsy matched
+ */
+static Eina_List *
+efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
+                                Eina_Hash *matches, Efreet_Menu_Filter *filter,
+                                unsigned int only_unallocated)
+{
+    Efreet_Menu_Desktop *md;
+    Eina_List *l;
+
+    EINA_LIST_FOREACH(pool, l, md)
+    {
+        if (eina_hash_find(matches, md->id)) continue;
+        if (only_unallocated && md->allocated) continue;
+        if (efreet_menu_filter_matches(filter->op, md))
+        {
+            applications = eina_list_append(applications, md);
+            eina_hash_direct_add(matches, (void *)md->id, md);
+            md->allocated = 1;
+        }
+    }
+    return applications;
+}
+
+/**
+ * @internal
+ * @param op The filter operation to execute
+ * @param md The desktop to run the filter on
+ * @return Returns 1 if this desktop matches the given filter, 0 otherwise
+ * @brief This will execute the given @a filter on the given desktop
+ */
+static int
+efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
+{
+    if (op->type == EFREET_MENU_FILTER_OP_OR)
+        return efreet_menu_filter_or_matches(op, md);
+
+    if (op->type == EFREET_MENU_FILTER_OP_AND)
+        return efreet_menu_filter_and_matches(op, md);
+
+    if (op->type == EFREET_MENU_FILTER_OP_NOT)
+        return efreet_menu_filter_not_matches(op, md);
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param op The filter operation to execute
+ * @param md The desktop to execute on
+ * @return Returns 1 if the desktop matches, 0 otherwise
+ * @brief Executes the OR operation, @a op, on the desktop, @a md.
+ */
+static int
+efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
+{
+    Efreet_Menu_Filter_Op *child;
+    Eina_List *l;
+    char *t;
+
+    if (op->all) return 1;
+
+    if (op->categories && md->desktop->categories)
+    {
+        EINA_LIST_FOREACH(op->categories, l, t)
+        {
+            if (eina_list_search_unsorted(md->desktop->categories,
+                                          EINA_COMPARE_CB(strcmp), t))
+                return 1;
+        }
+    }
+
+    if (op->filenames)
+    {
+        EINA_LIST_FOREACH(op->filenames, l, t)
+            if (t == md->id) return 1;
+    }
+
+    if (op->filters)
+    {
+        EINA_LIST_FOREACH(op->filters, l, child)
+        {
+            if (efreet_menu_filter_matches(child, md))
+                return 1;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param op The filter operation to execute
+ * @param md The desktop to execute on
+ * @return Returns 1 if the desktop matches, 0 otherwise
+ * @brief Executes the AND operation, @a op, on the desktop, @a md.
+ */
+static int
+efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
+{
+    Efreet_Menu_Filter_Op *child;
+    Eina_List *l;
+    char *t;
+
+    if (op->categories)
+    {
+        if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
+            return 0;
+
+        EINA_LIST_FOREACH(op->categories, l, t)
+        {
+            if (!eina_list_search_unsorted(md->desktop->categories,
+                                           EINA_COMPARE_CB(strcmp), t))
+                return 0;
+        }
+    }
+
+    if (op->filenames)
+    {
+        EINA_LIST_FOREACH(op->filenames, l, t)
+        {
+            if (t != md->id) return 0;
+        }
+    }
+
+    if (op->filters)
+    {
+        EINA_LIST_FOREACH(op->filters, l, child)
+        {
+            if (!efreet_menu_filter_matches(child, md))
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param op The filter operation to execute
+ * @param md The desktop to execute on
+ * @return Returns 1 if the desktop matches, 0 otherwise
+ * @brief Executes the NOT operation, @a op, on the desktop, @a md.
+ */
+static int
+efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
+{
+    Efreet_Menu_Filter_Op *child;
+    Eina_List *l;
+    char *t;
+
+    /* !all means no desktops match */
+    if (op->all) return 0;
+
+    if (op->categories)
+    {
+        if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
+            return 1;
+
+        EINA_LIST_FOREACH(op->categories, l, t)
+        {
+            if (eina_list_search_unsorted(md->desktop->categories,
+                                          EINA_COMPARE_CB(strcmp), t))
+                return 0;
+        }
+    }
+
+    if (op->filenames)
+    {
+        EINA_LIST_FOREACH(op->filenames, l, t)
+        {
+            if (t == md->id) return 0;
+        }
+    }
+
+    if (op->filters)
+    {
+        EINA_LIST_FOREACH(op->filters, l, child)
+        {
+            if (efreet_menu_filter_matches(child, md))
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param dest The destination menu
+ * @param src The source menu
+ * @return Returns no value
+ * @brief Takes the child elements of the menu @a src and puts then on the
+ * _start_ of the menu @a dest.
+ */
+static void
+efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
+{
+    Efreet_Menu_Internal *submenu;
+
+    if (!dest || !src) return;
+
+    if (!dest->directory && src->directory)
+    {
+        dest->directory = src->directory;
+        src->directory = NULL;
+    }
+
+    if (!dest->seen_allocated && src->seen_allocated)
+    {
+        dest->only_unallocated = src->only_unallocated;
+        dest->seen_allocated = 1;
+    }
+
+    if (!dest->seen_deleted && src->seen_deleted)
+    {
+        dest->deleted = src->deleted;
+        dest->seen_deleted = 1;
+    }
+
+    if (src->directories)
+    {
+        efreet_menu_create_directories_list(dest);
+        dest->directories = eina_list_merge(src->directories, dest->directories);
+        src->directories = NULL;
+    }
+
+    if (src->app_dirs)
+    {
+        efreet_menu_create_app_dirs_list(dest);
+        dest->app_dirs = eina_list_merge(src->app_dirs, dest->app_dirs);
+        src->app_dirs = NULL;
+    }
+
+    if (src->directory_dirs)
+    {
+        efreet_menu_create_directory_dirs_list(dest);
+        dest->directory_dirs = eina_list_merge(src->directory_dirs, dest->directory_dirs);
+        src->directory_dirs = NULL;
+    }
+
+    if (src->moves)
+    {
+        efreet_menu_create_move_list(dest);
+        dest->moves = eina_list_merge(src->moves, dest->moves);
+        src->moves = NULL;
+    }
+
+    if (src->filters)
+    {
+        efreet_menu_create_filter_list(dest);
+        dest->filters = eina_list_merge(src->filters, dest->filters);
+        src->filters = NULL;
+    }
+
+    if (src->sub_menus)
+    {
+        efreet_menu_create_sub_menu_list(dest);
+
+        while ((submenu = eina_list_data_get(eina_list_last(src->sub_menus))))
+        {
+            Efreet_Menu_Internal *match;
+
+            src->sub_menus = eina_list_remove(src->sub_menus, submenu);
+            /* if this menu is in the list already we just add to that */
+            if ((match = eina_list_search_unsorted(dest->sub_menus,
+                                                   EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
+                                                   submenu)))
+            {
+                efreet_menu_concatenate(match, submenu);
+                efreet_menu_internal_free(submenu);
+            }
+            else
+                dest->sub_menus = eina_list_prepend(dest->sub_menus, submenu);
+        }
+    }
+}
+
+/**
+ * @internal
+ * @param menu The menu to work with
+ * @return Returns no value
+ * @brief Handles any \<Move\> commands in the menus
+ */
+static void
+efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
+{
+    Efreet_Menu_Internal *child;
+    Efreet_Menu_Move *move;
+    Eina_List *l;
+
+    /* child moves are handled before parent moves */
+    if (internal->sub_menus)
+    {
+        EINA_LIST_FOREACH(internal->sub_menus, l, child)
+            efreet_menu_resolve_moves(child);
+    }
+
+    /* nothing to do if this menu has no moves */
+    if (!internal->moves) return;
+
+    EINA_LIST_FOREACH(internal->moves, l, move)
+    {
+        Efreet_Menu_Internal *origin, *dest, *parent;
+
+        /* if the origin path doesn't exist we do nothing */
+        origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
+        if (!origin) continue;
+
+        /* remove the origin menu from the parent */
+        parent->sub_menus = eina_list_remove(parent->sub_menus, origin);
+
+        /* if the destination path doesn't exist we just rename the origin
+         * menu and append to the parents list of children */
+        dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
+        if (!dest)
+        {
+            char *path, *tmp, *t;
+            size_t len;
+
+            /* if the dest path has /'s in it then we need to add menus to
+             * fill out the paths */
+            len = strlen(move->new_name) + 1;
+            t = alloca(len);
+            memcpy(t, move->new_name, len);
+            tmp = t;
+            path = strchr(tmp, '/');
+            while (path)
+            {
+                Efreet_Menu_Internal *ancestor;
+
+                *path = '\0';
+
+                ancestor = efreet_menu_internal_new();
+                if (!ancestor) goto error;
+                ancestor->name.internal = eina_stringshare_add(tmp);
+
+                efreet_menu_create_sub_menu_list(parent);
+                parent->sub_menus = eina_list_append(parent->sub_menus, ancestor);
+
+                parent = ancestor;
+                tmp = ++path;
+                path = strchr(tmp, '/');
+            }
+
+            IF_RELEASE(origin->name.internal);
+            origin->name.internal = eina_stringshare_add(tmp);
+
+            efreet_menu_create_sub_menu_list(parent);
+            parent->sub_menus = eina_list_append(parent->sub_menus, origin);
+        }
+        else
+        {
+            efreet_menu_concatenate(dest, origin);
+            efreet_menu_internal_free(origin);
+        }
+    }
+error:
+    IF_FREE_LIST(internal->moves, efreet_menu_move_free);
+}
+
+/**
+ * @internal
+ * @param menu The menu to start searching from
+ * @param name The menu name to find
+ * @param parent The parent of the found menu
+ * @return Returns the menu with the given @a name or NULL if none found
+ * @brief Searches the menu tree starting at @a menu looking for a menu with
+ * @a name.
+ */
+static Efreet_Menu_Internal *
+efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
+{
+    char *part, *tmp, *ptr;
+    size_t len;
+
+    if (parent) *parent = internal;
+
+    /* find the correct parent menu */
+    len = strlen(name) + 1;
+    tmp = alloca(len);
+    memcpy(tmp, name, len);
+    ptr = tmp;
+    part = strchr(ptr, '/');
+    while (part)
+    {
+        *part = '\0';
+
+        if (!(internal = eina_list_search_unsorted(internal->sub_menus,
+                                                   EINA_COMPARE_CB(efreet_menu_cb_compare_names),
+                                                   ptr)))
+        {
+            return NULL;
+        }
+
+        ptr = ++part;
+        part = strchr(ptr, '/');
+    }
+
+    if (parent) *parent = internal;
+
+    /* find the menu in the parent list */
+    if (!(internal = eina_list_search_unsorted(internal->sub_menus,
+                                               EINA_COMPARE_CB(efreet_menu_cb_compare_names),
+                                               ptr)))
+    {
+        return NULL;
+    }
+
+    return internal;
+}
+
+static void
+efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
+{
+    char *tmp, *p;
+    size_t len;
+
+    len = strlen(path) + 1;
+    tmp = alloca(len);
+    memcpy(tmp, path, len);
+    p = strrchr(tmp, '/');
+    if (p)
+    {
+        *p = '\0';
+        p++;
+
+        internal->file.path = eina_stringshare_add(tmp);
+        internal->file.name = eina_stringshare_add(p);
+    }
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
+ * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
+ */
+static Efreet_Menu_Move *
+efreet_menu_move_new(void)
+{
+    Efreet_Menu_Move *move;
+
+    move = NEW(Efreet_Menu_Move, 1);
+
+    return move;
+}
+
+/**
+ * @internal
+ * @param move The Efreet_Menu_Move to free
+ * @return Returns no value.
+ * @brief Frees the given move structure
+ */
+static void
+efreet_menu_move_free(Efreet_Menu_Move *move)
+{
+    if (!move) return;
+
+    IF_RELEASE(move->old_name);
+    IF_RELEASE(move->new_name);
+
+    FREE(move);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
+ * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
+ */
+static Efreet_Menu_App_Dir *
+efreet_menu_app_dir_new(void)
+{
+    Efreet_Menu_App_Dir *dir;
+
+    dir = NEW(Efreet_Menu_App_Dir, 1);
+
+    return dir;
+}
+
+/**
+ * @internal
+ * @param dir The Efreet_Menu_App_Dir to free
+ * @return Returns no value.
+ * @brief Frees the given dir structure
+ */
+static void
+efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
+{
+    if (!dir) return;
+
+    IF_RELEASE(dir->path);
+    IF_RELEASE(dir->prefix);
+    FREE(dir);
+}
+
+/**
+ * @internal
+ * @param a The app dir to compare too
+ * @param b The path to compare too
+ * @return Returns 0 if the strings are equals, != 0 otherwise
+ * @brief Compares the too strings
+ */
+static int
+efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
+{
+    if (!a->path || !b) return 1;
+    if (a->path == b) return 0;
+    return strcmp(a->path, b);
+}
+
+static void
+efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->sub_menus) return;
+
+    internal->sub_menus = NULL;
+}
+
+static void
+efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->app_dirs) return;
+
+    internal->app_dirs = NULL;
+}
+
+static void
+efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->directory_dirs) return;
+
+    internal->directory_dirs = NULL;
+}
+
+static void
+efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->moves) return;
+
+    internal->moves = NULL;
+}
+
+static void
+efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->filters) return;
+
+    internal->filters = NULL;
+}
+
+static void
+efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->layout) return;
+
+    internal->layout = NULL;
+}
+
+static void
+efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->default_layout) return;
+
+    internal->default_layout = NULL;
+}
+
+static void
+efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->directories) return;
+
+    internal->directories = NULL;
+}
+
+static const char *
+efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
+{
+    char path[PATH_MAX];
+    size_t len;
+
+    /* see if we've got an absolute or relative path */
+    if (suffix[0] == '/')
+        snprintf(path, sizeof(path), "%s", suffix);
+
+    else
+    {
+        if (!internal->file.path)
+        {
+            INF("efreet_menu_handle_app_dir() missing menu path ...");
+            return NULL;
+        }
+        snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
+    }
+
+    len = strlen(path);
+    while (path[len] == '/') path[len--] = '\0';
+
+    return eina_stringshare_add(path);
+}
+
+static int
+efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
+{
+    if (!a->name.internal || !b->name.internal) return 1;
+    if (a->name.internal == b->name.internal) return 0;
+    return strcmp(a->name.internal, b->name.internal);
+}
+
+static int
+efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
+{
+    Efreet_Menu_App_Dir *app_dir;
+    Efreet_Menu_Desktop *md;
+    Eina_List *l;
+
+    EINA_LIST_FREE(internal->app_pool, md)
+        efreet_menu_desktop_free(md);
+
+    EINA_LIST_FOREACH(internal->app_dirs, l, app_dir)
+        efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
+
+    return 1;
+}
+
+static int
+efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
+{
+    Efreet_Desktop *desktop;
+    Efreet_Menu_Desktop *menu_desktop;
+    char buf2[PATH_MAX];
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *info;
+
+    it = eina_file_direct_ls(path);
+    if (!it) return 1;
+
+    EINA_ITERATOR_FOREACH(it, info)
+    {
+        const char *fname;
+
+        fname = info->path + info->name_start;
+        if (id)
+            snprintf(buf2, sizeof(buf2), "%s-%s", id, fname);
+        else
+            strcpy(buf2, fname);
+
+        if (ecore_file_is_dir(info->path))
+        {
+            if (!legacy)
+                efreet_menu_app_dir_scan(internal, info->path, buf2, legacy);
+        }
+        else
+        {
+            const char *ext;
+
+            ext = strrchr(fname, '.');
+
+            if (!ext || strcmp(ext, ".desktop")) continue;
+            desktop = efreet_desktop_get(info->path);
+
+            if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
+            {
+                if (desktop) efreet_desktop_free(desktop);
+                continue;
+            }
+            /* Don't add two files with the same id in the app pool */
+            if (eina_list_search_unsorted(internal->app_pool,
+                                          EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids),
+                                          buf2))
+            {
+                if (desktop) efreet_desktop_free(desktop);
+                continue;
+            }
+
+            menu_desktop = efreet_menu_desktop_new();
+            menu_desktop->desktop = desktop;
+            menu_desktop->id = eina_stringshare_add(buf2);
+            internal->app_pool = eina_list_prepend(internal->app_pool, menu_desktop);
+        }
+    }
+    eina_iterator_free(it);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param menu The menu to work with
+ * @return Returns 1 on success or 0 on failure
+ * @brief Process the directory dirs in @a menu
+ */
+static int
+efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
+{
+    const char *path;
+    Eina_List *l;
+
+    if (internal->directory_dirs)
+    {
+        internal->directory_cache =
+            eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
+
+        EINA_LIST_REVERSE_FOREACH(internal->directory_dirs, l, path)
+            efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
+    }
+
+    if (internal->directories)
+    {
+        EINA_LIST_REVERSE_FOREACH(internal->directories, l, path)
+        {
+            internal->directory = efreet_menu_directory_get(internal, path);
+            if (internal->directory) break;
+        }
+    }
+    if (!internal->directory)
+        internal->name.name = internal->name.internal;
+    else
+        internal->name.name = internal->directory->name;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param path The path to scan
+ * @param relative_path The relative portion of the path
+ * @param cache The cache to populate
+ * @return Returns 1 on success or 0 on failure
+ * @brief Scans the given directory dir for .directory files and adds the
+ * applications to the cache
+ */
+static int
+efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
+                                Eina_Hash *cache)
+{
+    Efreet_Desktop *desktop;
+    char buf2[PATH_MAX];
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *info;
+    char *ext;
+
+    it = eina_file_direct_ls(path);
+    if (!it) return 1;
+
+    EINA_ITERATOR_FOREACH(it, info)
+    {
+        const char *fname;
+
+        fname = info->path + info->name_start;
+        if (relative_path)
+            snprintf(buf2, sizeof(buf2), "%s/%s", relative_path, fname);
+        else
+            strcpy(buf2, fname);
+
+        if (ecore_file_is_dir(info->path))
+            efreet_menu_directory_dir_scan(info->path, buf2, cache);
+
+        else
+        {
+            ext = strrchr(fname, '.');
+            if (!ext || strcmp(ext, ".directory")) continue;
+
+            desktop = efreet_desktop_get(info->path);
+            if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
+            {
+                efreet_desktop_free(desktop);
+                continue;
+            }
+
+            eina_hash_del(cache, buf2, NULL);
+            eina_hash_add(cache, buf2, desktop);
+        }
+    }
+    eina_iterator_free(it);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param menu The menu to work with
+ * @param path The path to work with
+ * @return Returns the desktop file for this path or NULL if none exists
+ * @brief Finds the desktop file for the given path.
+ */
+static Efreet_Desktop *
+efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
+{
+    Efreet_Desktop *dir;
+
+    if (internal->directory_cache)
+    {
+        dir = eina_hash_find(internal->directory_cache, path);
+        if (dir) return dir;
+    }
+
+    if (internal->parent)
+        return efreet_menu_directory_get(internal->parent, path);
+
+    return NULL;
+}
+
+/**
+ * @internal
+ * @param a The first desktop
+ * @param b The second desktop
+ * @return Returns the comparison of the desktop files
+ * @brief Compares the desktop files.
+ */
+static int
+efreet_menu_cb_md_compare(const Efreet_Menu_Desktop *a, const Efreet_Menu_Desktop *b)
+{
+#ifdef STRICT_SPEC
+    return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
+#else
+    if (a->desktop->name == b->desktop->name) return 0;
+    return strcasecmp(a->desktop->name, b->desktop->name);
+#endif
+}
+
+static int
+efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
+{
+    if (internal->name.internal == name) return 0;
+    return strcmp(internal->name.internal, name);
+}
+
+static int
+efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
+{
+    if (md->id == name) return 0;
+    return strcmp(md->id, name);
+}
+
+static Efreet_Menu *
+efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
+{
+    Efreet_Menu *entry;
+    Eina_List *layout = NULL;
+    Eina_List *l;
+
+    if (internal->parent)
+    {
+        /* Copy default layout rules */
+        if (internal->show_empty == -1)    internal->show_empty = internal->parent->show_empty;
+        if (internal->in_line == -1)       internal->in_line = internal->parent->in_line;
+        if (internal->inline_limit == -1)  internal->inline_limit = internal->parent->inline_limit;
+        if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
+        if (internal->inline_alias == -1)  internal->inline_alias = internal->parent->inline_alias;
+    }
+
+    if (internal->layout)
+        layout = internal->layout;
+
+    else if (internal->parent)
+    {
+        Efreet_Menu_Internal *parent;
+        parent = internal->parent;
+        do
+        {
+            layout = parent->default_layout;
+            parent = parent->parent;
+        } while (!layout && parent);
+    }
+
+    /* init entry */
+    entry = efreet_menu_entry_new();
+    entry->type = EFREET_MENU_ENTRY_MENU;
+    entry->id = eina_stringshare_add(internal->name.internal);
+    entry->name = eina_stringshare_add(internal->name.name);
+    if (internal->directory)
+    {
+        entry->icon = eina_stringshare_add(internal->directory->icon);
+        efreet_desktop_ref(internal->directory);
+        entry->desktop = internal->directory;
+    }
+    entry->entries = NULL;
+
+#if 1 //STRICT_SPEC
+    if (internal->sub_menus)
+    {
+        internal->sub_menus = eina_list_sort(internal->sub_menus,
+                                             0,
+                                             EINA_COMPARE_CB(efreet_menu_cb_menu_compare));
+    }
+#endif
+
+    if (layout)
+    {
+        Efreet_Menu_Layout *lay;
+
+        EINA_LIST_FOREACH(layout, l, lay)
+            efreet_menu_layout_entries_get(entry, internal, lay);
+    }
+    else
+    {
+        /* Default layout, first menus, then desktop */
+        if (internal->sub_menus)
+        {
+            Efreet_Menu_Internal *sub;
+
+            EINA_LIST_FOREACH(internal->sub_menus, l, sub)
+            {
+                Efreet_Menu *sub_entry;
+                if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
+                sub_entry = efreet_menu_layout_menu(sub);
+                /* Don't show empty menus */
+                if (!sub_entry->entries)
+                {
+                    efreet_menu_free(sub_entry);
+                    continue;
+                }
+                entry->entries = eina_list_append(entry->entries, sub_entry);
+            }
+        }
+
+        if (internal->applications)
+        {
+            Efreet_Menu_Desktop *md;
+
+            EINA_LIST_FOREACH(internal->applications, l, md)
+            {
+                Efreet_Menu *sub_entry;
+                sub_entry = efreet_menu_layout_desktop(md);
+                entry->entries = eina_list_append(entry->entries, sub_entry);
+            }
+        }
+    }
+
+    /* Don't keep this list around if it is empty */
+
+    return entry;
+}
+
+static Efreet_Menu *
+efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
+{
+    Efreet_Menu *entry;
+
+    /* init entry */
+    entry = efreet_menu_entry_new();
+    entry->type = EFREET_MENU_ENTRY_DESKTOP;
+    entry->id = eina_stringshare_add(md->id);
+    entry->name = eina_stringshare_add(md->desktop->name);
+    if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
+    efreet_desktop_ref(md->desktop);
+    entry->desktop = md->desktop;
+
+    return entry;
+}
+
+static void
+efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
+        Efreet_Menu_Layout *layout)
+{
+    Efreet_Menu *sub_entry;
+
+    if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
+    {
+        Efreet_Menu_Internal *sub;
+
+        /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
+        int show_empty, in_line, inline_limit, inline_header, inline_alias;
+
+        if (layout->show_empty == -1) show_empty = internal->show_empty;
+        else show_empty = layout->show_empty;
+
+        if (layout->in_line == -1) in_line = internal->in_line;
+        else in_line = layout->in_line;
+
+        if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
+        else inline_limit = layout->inline_limit;
+
+        if (layout->inline_header == -1) inline_header = internal->inline_header;
+        else inline_header = layout->inline_header;
+
+        if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
+        else inline_alias = layout->inline_alias;
+
+        sub = eina_list_search_unsorted(internal->sub_menus,
+                                        EINA_COMPARE_CB(efreet_menu_cb_compare_names), layout->name);
+        if (sub)
+        {
+            if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
+            {
+                sub_entry = efreet_menu_layout_menu(sub);
+                if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
+                    efreet_menu_free(sub_entry);
+                else if (in_line &&
+                        ((inline_limit == 0) ||
+                         (!sub_entry->entries ||
+                          (inline_limit > 0 && eina_list_count(sub_entry->entries) <= (unsigned int)inline_limit))))
+                {
+                    /* Inline */
+                    if (!sub_entry->entries)
+                    {
+                        /* Can't inline an empty submenu */
+                        entry->entries = eina_list_append(entry->entries, sub_entry);
+                    }
+                    else if (inline_alias && (eina_list_count(sub_entry->entries) == 1))
+                    {
+                        Efreet_Menu *tmp;
+
+                        tmp = eina_list_data_get(sub_entry->entries);
+                        sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
+                        IF_RELEASE(tmp->name);
+                        tmp->name = sub_entry->name;
+                        sub_entry->name = NULL;
+                        IF_RELEASE(tmp->icon);
+                        tmp->icon = sub_entry->icon;
+                        sub_entry->icon = NULL;
+                        entry->entries = eina_list_append(entry->entries, tmp);
+                        efreet_menu_free(sub_entry);
+                    }
+                    else
+                    {
+                        Efreet_Menu *tmp;
+
+                        if (inline_header)
+                        {
+                            tmp = efreet_menu_entry_new();
+                            tmp->type = EFREET_MENU_ENTRY_HEADER;
+                            tmp->name = sub_entry->name;
+                            sub_entry->name = NULL;
+                            tmp->icon = sub_entry->icon;
+                            sub_entry->icon = NULL;
+                            entry->entries = eina_list_append(entry->entries, tmp);
+                        }
+                        while ((tmp = eina_list_data_get(sub_entry->entries)))
+                        {
+                            sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
+                            entry->entries = eina_list_append(entry->entries, tmp);
+                        }
+                        efreet_menu_free(sub_entry);
+                    }
+                }
+                else
+                    entry->entries = eina_list_append(entry->entries, sub_entry);
+            }
+            internal->sub_menus = eina_list_remove(internal->sub_menus, sub);
+            efreet_menu_internal_free(sub);
+        }
+    }
+    else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
+    {
+        Efreet_Menu_Desktop *md;
+        md = eina_list_search_unsorted(internal->applications,
+                                       EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name);
+        if (md)
+        {
+            sub_entry = efreet_menu_layout_desktop(md);
+            entry->entries = eina_list_append(entry->entries, sub_entry);
+            internal->applications = eina_list_remove(internal->applications, md);
+        }
+    }
+    else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
+    {
+        if (internal->applications && !strcmp(layout->name, "files"))
+        {
+            Efreet_Menu_Desktop *md;
+
+            while ((md = eina_list_data_get(internal->applications)))
+            {
+                internal->applications = eina_list_remove_list(internal->applications,
+                                                               internal->applications);
+                sub_entry = eina_list_search_unsorted(entry->entries,
+                                                      EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
+                                                      md->desktop);
+                if (!sub_entry)
+                {
+                    sub_entry = efreet_menu_layout_desktop(md);
+                    entry->entries = eina_list_append(entry->entries, sub_entry);
+                }
+            }
+            internal->applications = eina_list_free(internal->applications);
+        }
+        else if (internal->sub_menus && !strcmp(layout->name, "menus"))
+        {
+            Efreet_Menu_Internal *sub;
+
+            while ((sub = eina_list_data_get(internal->sub_menus)))
+            {
+                internal->sub_menus = eina_list_remove_list(internal->sub_menus, internal->sub_menus);
+                if ((sub->directory && sub->directory->no_display) || sub->deleted)
+                {
+                    efreet_menu_internal_free(sub);
+                    continue;
+                }
+                sub_entry = eina_list_search_unsorted(entry->entries,
+                                                      EINA_COMPARE_CB(efreet_menu_cb_entry_compare_menu),
+                                                      sub);
+                if (!sub_entry)
+                {
+                    sub_entry = efreet_menu_layout_menu(sub);
+                    if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
+                        efreet_menu_free(sub_entry);
+                    else if (internal->in_line &&
+                            ((internal->inline_limit == 0) ||
+                             (!sub_entry->entries ||
+                              (internal->inline_limit > 0 && eina_list_count(sub_entry->entries) <= (unsigned int)internal->inline_limit))))
+                    {
+                        /* Inline */
+                        if (!sub_entry->entries)
+                        {
+                            /* Can't inline an empty submenu */
+                            entry->entries = eina_list_append(entry->entries, sub_entry);
+                        }
+                        else if (internal->inline_alias && (eina_list_count(sub_entry->entries) == 1))
+                        {
+                            Efreet_Menu *tmp;
+
+                            tmp = eina_list_data_get(sub_entry->entries);
+                            sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
+                            eina_stringshare_del(tmp->name);
+                            tmp->name = sub_entry->name;
+                            sub_entry->name = NULL;
+                            IF_RELEASE(tmp->icon);
+                            if (sub_entry->icon)
+                            {
+                                tmp->icon = sub_entry->icon;
+                                sub_entry->icon = NULL;
+                            }
+                            entry->entries = eina_list_append(entry->entries, tmp);
+                            efreet_menu_free(sub_entry);
+                        }
+                        else
+                        {
+                            Efreet_Menu *tmp;
+
+                            if (internal->inline_header)
+                            {
+                                tmp = efreet_menu_entry_new();
+                                tmp->type = EFREET_MENU_ENTRY_HEADER;
+                                tmp->name = sub_entry->name;
+                                sub_entry->name = NULL;
+                                if (sub_entry->icon) tmp->icon = sub_entry->icon;
+                                sub_entry->icon = NULL;
+                                entry->entries = eina_list_append(entry->entries, tmp);
+                            }
+                            while ((tmp = eina_list_data_get(sub_entry->entries)))
+                            {
+                                sub_entry->entries = eina_list_remove_list(sub_entry->entries,
+                                                                           sub_entry->entries);
+                                entry->entries = eina_list_append(entry->entries, tmp);
+                            }
+                            efreet_menu_free(sub_entry);
+                        }
+                    }
+                    else
+                        entry->entries = eina_list_append(entry->entries, sub_entry);
+                }
+                efreet_menu_internal_free(sub);
+            }
+            IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
+        }
+        else if (internal->sub_menus && !strcmp(layout->name, "all"))
+        {
+            const char *orig;
+
+            orig = layout->name;
+            layout->name = "menus";
+            efreet_menu_layout_entries_get(entry, internal, layout);
+            layout->name = "files";
+            efreet_menu_layout_entries_get(entry, internal, layout);
+            layout->name = orig;
+        }
+    }
+    else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
+    {
+        sub_entry = efreet_menu_entry_new();
+        sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
+        entry->entries = eina_list_append(entry->entries, sub_entry);
+    }
+}
+
+static int
+efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
+{
+    if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
+    if (!entry->name || !internal->name.name) return 1;
+    if (entry->name == internal->name.name) return 0;
+    return strcmp(entry->name, internal->name.name);
+}
+
+static int
+efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
+{
+    if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
+    if (!entry->name || !desktop->name) return -1;
+    if (entry->name == desktop->name) return 0;
+    return strcmp(entry->name, desktop->name);
+}
+
+static int
+efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
+{
+    if (!move->old_name || !old) return 1;
+    if (move->old_name == old) return 0;
+    return 1;
+}
+
+static int
+efreet_menu_layout_is_empty(Efreet_Menu *entry)
+{
+    Efreet_Menu *sub_entry;
+    Eina_List *l;
+
+    if (!entry->entries) return 1;
+
+    EINA_LIST_FOREACH(entry->entries, l, sub_entry)
+    {
+        if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
+        if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;
+    }
+    return 1;
+}
diff --git a/src/lib/efreet_menu.h b/src/lib/efreet_menu.h
new file mode 100644 (file)
index 0000000..8531cd8
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef EFREET_MENU_H
+#define EFREET_MENU_H
+
+/**
+ * @file efreet_menu.h
+ * @brief Contains the structures and methods to support the Desktop
+ * Menu Specification.
+ * @addtogroup Efreet_Menu Efreet_Menu: The FDO Desktop Menu Specification
+ * functions and structures
+ *
+ * @{
+ */
+
+/**
+ * The type of entry
+ */
+typedef enum Efreet_Menu_Entry_Type
+{
+    EFREET_MENU_ENTRY_MENU,
+    EFREET_MENU_ENTRY_DESKTOP,
+    EFREET_MENU_ENTRY_SEPARATOR,
+    EFREET_MENU_ENTRY_HEADER
+} Efreet_Menu_Entry_Type;
+
+/**
+ * Efreet_Menu
+ */
+typedef struct Efreet_Menu Efreet_Menu;
+
+/**
+ * Efreet_Menu
+ * Stores information on a entry in the menu
+ */
+struct Efreet_Menu
+{
+    Efreet_Menu_Entry_Type type;
+    const char *id;   /**< File-id for desktop and relative name for menu */
+
+    const char *name; /**< Name this entry should show */
+    const char *icon; /**< Icon for this entry */
+
+    Efreet_Desktop *desktop;   /**< The desktop we refer too */
+    Eina_List      *entries;   /**< The menu items */
+};
+
+
+/**
+ * @return Returns no value
+ * @brief Initialize legacy kde support. This function blocks while
+ * the kde-config script is run.
+ */
+EAPI int              efreet_menu_kde_legacy_init(void);
+
+/**
+ * @param name The internal name of the menu
+ * @return Returns the Efreet_Menu on success or
+ * NULL on failure
+ * @brief Creates a new menu
+ */
+EAPI Efreet_Menu     *efreet_menu_new(const char *name);
+
+/**
+ * @brief Override which file is used for menu creation
+ * @param file The file to use for menu creation
+ *
+ * This file is only used if it exists, else the standard files will be used
+ * for the menu.
+ */
+EAPI void             efreet_menu_file_set(const char *file);
+
+/**
+ * @return Returns the Efreet_Menu_Internal representation of the default menu or
+ * NULL if none found
+ * @brief Creates the default menu representation
+ */
+EAPI Efreet_Menu     *efreet_menu_get(void);
+
+/**
+ * @param path The path of the menu to load
+ * @return Returns the Efreet_Menu_Internal representation on success or NULL on
+ * failure
+ * @brief Parses the given .menu file and creates the menu representation
+ */
+EAPI Efreet_Menu     *efreet_menu_parse(const char *path);
+
+/**
+ * @param menu The menu to work with
+ * @param path The path where the menu should be saved
+ * @return Returns 1 on success, 0 on failure
+ * @brief Saves the menu to file
+ */
+EAPI int              efreet_menu_save(Efreet_Menu *menu, const char *path);
+
+/**
+ * @param menu The Efreet_Menu to free
+ * @return Returns no value
+ * @brief Frees the given structure
+ */
+EAPI void             efreet_menu_free(Efreet_Menu *menu);
+
+
+/**
+ * @param menu The menu to work with
+ * @param desktop The desktop to insert
+ * @param pos The position to place the new desktop
+ * @return Returns 1 on success, 0 on failure
+ * @brief Insert a desktop element in a menu structure. Only accepts desktop files
+ * in default directories.
+ */
+EAPI int              efreet_menu_desktop_insert(Efreet_Menu *menu,
+                                                    Efreet_Desktop *desktop,
+                                                    int pos);
+
+/**
+ * @param menu The menu to work with
+ * @param desktop The desktop to remove
+ * @return Returns 1 on success, 0 on failure
+ * @brief Remove a desktop element in a menu structure. Only accepts desktop files
+ * in default directories.
+ */
+EAPI int              efreet_menu_desktop_remove(Efreet_Menu *menu,
+                                                    Efreet_Desktop *desktop);
+
+
+/**
+ * @param menu The menu to work with
+ * @param menu The menu to work with
+ * @param indent The indent level to print the menu at
+ * @return Returns no value
+ * @brief Dumps the contents of the menu to the command line
+ */
+EAPI void             efreet_menu_dump(Efreet_Menu *menu, const char *indent);
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_mime.c b/src/lib/efreet_mime.c
new file mode 100644 (file)
index 0000000..9343d90
--- /dev/null
@@ -0,0 +1,1622 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <ctype.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fnmatch.h>
+
+#ifdef _WIN32
+# include <winsock2.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_mime_log_dom
+static int _efreet_mime_log_dom = -1;
+
+#include "Efreet.h"
+#include "Efreet_Mime.h"
+#include "efreet_private.h"
+
+static Eina_List *globs = NULL;     /* contains Efreet_Mime_Glob structs */
+static Eina_List *magics = NULL;    /* contains Efreet_Mime_Magic structs */
+static Eina_Hash *wild = NULL;      /* contains *.ext and mime.types globs*/
+static Eina_Hash *monitors = NULL;  /* contains file monitors */
+static Eina_Hash *mime_icons = NULL; /* contains cache with mime->icons */
+static Eina_Inlist *mime_icons_lru = NULL;
+static unsigned int _efreet_mime_init_count = 0;
+
+static const char *_mime_inode_symlink = NULL;
+static const char *_mime_inode_fifo = NULL;
+static const char *_mime_inode_chardevice = NULL;
+static const char *_mime_inode_blockdevice = NULL;
+static const char *_mime_inode_socket = NULL;
+static const char *_mime_inode_mountpoint = NULL;
+static const char *_mime_inode_directory = NULL;
+static const char *_mime_application_x_executable = NULL;
+static const char *_mime_application_octet_stream = NULL;
+static const char *_mime_text_plain = NULL;
+
+/**
+ * @internal
+ * @brief Holds whether we are big/little endian
+ * @note This is set during efreet_mime_init based on
+ * a runtime check.
+ */
+static enum
+{
+    EFREET_ENDIAN_BIG = 0,
+    EFREET_ENDIAN_LITTLE = 1
+} efreet_mime_endianess = EFREET_ENDIAN_BIG;
+
+/*
+ * Buffer sized used for magic checks.  The default is good enough for the
+ * current set of magic rules.  This setting is only here for the future.
+ */
+#define EFREET_MIME_MAGIC_BUFFER_SIZE 512
+
+/*
+ * Minimum timeout in seconds between mime-icons cache flush.
+ */
+#define EFREET_MIME_ICONS_FLUSH_TIMEOUT 60
+
+/*
+ * Timeout in seconds, when older mime-icons items are expired.
+ */
+#define EFREET_MIME_ICONS_EXPIRE_TIMEOUT 600
+
+/*
+ * mime-icons maximum population.
+ */
+#define EFREET_MIME_ICONS_MAX_POPULATION 512
+
+/*
+ * If defined, dump mime-icons statistics after flush.
+ */
+//#define EFREET_MIME_ICONS_DEBUG
+
+typedef struct Efreet_Mime_Glob Efreet_Mime_Glob;
+struct Efreet_Mime_Glob
+{
+    const char *glob;
+    const char *mime;
+};
+
+typedef struct Efreet_Mime_Magic Efreet_Mime_Magic;
+struct Efreet_Mime_Magic
+{
+    unsigned int priority;
+    const char *mime;
+    Eina_List *entries;
+};
+
+typedef struct Efreet_Mime_Magic_Entry Efreet_Mime_Magic_Entry;
+struct Efreet_Mime_Magic_Entry
+{
+    unsigned int indent;
+    unsigned int offset;
+    unsigned int word_size;
+    unsigned int range_len;
+    unsigned short value_len;
+    char *mask;
+    char *value;
+};
+
+typedef struct Efreet_Mime_Icon_Entry_Head Efreet_Mime_Icon_Entry_Head;
+struct Efreet_Mime_Icon_Entry_Head
+{
+    EINA_INLIST; /* node of mime_icons_lru */
+    Eina_Inlist *list;
+    const char *mime;
+    double timestamp;
+};
+
+typedef struct Efreet_Mime_Icon_Entry Efreet_Mime_Icon_Entry;
+struct Efreet_Mime_Icon_Entry
+{
+    EINA_INLIST;
+    const char *icon;
+    const char *theme;
+    unsigned int size;
+};
+
+static int efreet_mime_glob_remove(const char *glob);
+static void efreet_mime_mime_types_load(const char *file);
+static void efreet_mime_shared_mimeinfo_globs_load(const char *file);
+static void efreet_mime_shared_mimeinfo_magic_load(const char *file);
+static void efreet_mime_shared_mimeinfo_magic_parse(char *data, int size);
+static const char *efreet_mime_magic_check_priority(const char *file,
+                                                      unsigned int start,
+                                                      unsigned int end);
+static int efreet_mime_init_files(void);
+static const char *efreet_mime_special_check(const char *file);
+static const char *efreet_mime_fallback_check(const char *file);
+static void efreet_mime_glob_free(void *data);
+static void efreet_mime_magic_free(void *data);
+static void efreet_mime_magic_entry_free(void *data);
+static int efreet_mime_glob_match(const char *str, const char *glob);
+static int efreet_mime_glob_case_match(char *str, const char *glob);
+static int efreet_mime_endian_check(void);
+
+static void efreet_mime_monitor_add(const char *file);
+static void efreet_mime_cb_update_file(void *data,
+                                        Ecore_File_Monitor *monitor,
+                                        Ecore_File_Event event,
+                                        const char *path);
+
+static void efreet_mime_icons_flush(double now);
+static void efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry);
+static void efreet_mime_icon_entry_add(const char *mime,
+                                       const char *icon,
+                                       const char *theme,
+                                       unsigned int size);
+static const char *efreet_mime_icon_entry_find(const char *mime,
+                                               const char *theme,
+                                               unsigned int size);
+static void efreet_mime_icons_debug(void);
+
+EAPI int
+efreet_mime_init(void)
+{
+    if (++_efreet_mime_init_count != 1)
+        return _efreet_mime_init_count;
+
+    if (!ecore_init())
+        return --_efreet_mime_init_count;
+
+    if (!ecore_file_init())
+        goto shutdown_ecore;
+
+    if (!efreet_init())
+        goto shutdown_ecore_file;
+
+    _efreet_mime_log_dom = eina_log_domain_register
+      ("efreet_mime", EFREET_DEFAULT_LOG_COLOR);
+
+    if (_efreet_mime_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_mime.");
+        goto shutdown_efreet;
+    }
+
+    efreet_mime_endianess = efreet_mime_endian_check();
+
+    monitors = eina_hash_string_superfast_new(EINA_FREE_CB(ecore_file_monitor_del));
+
+    efreet_mime_type_cache_clear();
+
+    if (!efreet_mime_init_files())
+        goto unregister_log_domain;
+
+    return _efreet_mime_init_count;
+
+unregister_log_domain:
+    eina_log_domain_unregister(_efreet_mime_log_dom);
+    _efreet_mime_log_dom = -1;
+shutdown_efreet:
+    efreet_shutdown();
+shutdown_ecore_file:
+    ecore_file_shutdown();
+shutdown_ecore:
+    ecore_shutdown();
+
+    return --_efreet_mime_init_count;
+}
+
+EAPI int
+efreet_mime_shutdown(void)
+{
+    if (--_efreet_mime_init_count != 0)
+        return _efreet_mime_init_count;
+
+    efreet_mime_icons_debug();
+
+    IF_RELEASE(_mime_inode_symlink);
+    IF_RELEASE(_mime_inode_fifo);
+    IF_RELEASE(_mime_inode_chardevice);
+    IF_RELEASE(_mime_inode_blockdevice);
+    IF_RELEASE(_mime_inode_socket);
+    IF_RELEASE(_mime_inode_mountpoint);
+    IF_RELEASE(_mime_inode_directory);
+    IF_RELEASE(_mime_application_x_executable);
+    IF_RELEASE(_mime_application_octet_stream);
+    IF_RELEASE(_mime_text_plain);
+
+    IF_FREE_LIST(globs, efreet_mime_glob_free);
+    IF_FREE_LIST(magics, efreet_mime_magic_free);
+    IF_FREE_HASH(monitors);
+    IF_FREE_HASH(wild);
+    IF_FREE_HASH(mime_icons);
+    eina_log_domain_unregister(_efreet_mime_log_dom);
+    _efreet_mime_log_dom = -1;
+    efreet_shutdown();
+    ecore_file_shutdown();
+    ecore_shutdown();
+
+    return _efreet_mime_init_count;
+}
+
+EAPI const char *
+efreet_mime_type_get(const char *file)
+{
+    const char *type = NULL;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+    if ((type = efreet_mime_special_check(file)))
+        return type;
+
+    /* Check magics with priority > 80 */
+    if ((type = efreet_mime_magic_check_priority(file, 0, 80)))
+        return type;
+
+    /* Check globs */
+    if ((type = efreet_mime_globs_type_get(file)))
+        return type;
+
+    /* Check rest of magics */
+    if ((type = efreet_mime_magic_check_priority(file, 80, 0)))
+        return type;
+
+    return efreet_mime_fallback_check(file);
+}
+
+EAPI const char *
+efreet_mime_type_icon_get(const char *mime, const char *theme, unsigned int size)
+{
+    const char *icon = NULL;
+    char *data;
+    Eina_List *icons  = NULL;
+    const char *env = NULL;
+    char *p = NULL, *pp = NULL, *ppp = NULL;
+    char buf[PATH_MAX];
+    const char *cache;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(mime, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
+
+    mime = eina_stringshare_add(mime);
+    theme = eina_stringshare_add(theme);
+    cache = efreet_mime_icon_entry_find(mime, theme, size);
+    if (cache)
+    {
+        eina_stringshare_del(mime);
+        eina_stringshare_del(theme);
+        return cache;
+    }
+
+    /* Standard icon name */
+    p = strdup(mime);
+    pp = p;
+    while (*pp)
+    {
+        if (*pp == '/') *pp = '-';
+        pp++;
+    }
+    icons = eina_list_append(icons, p);
+
+    /* Environment Based icon names */
+    if ((env = efreet_desktop_environment_get()))
+    {
+        snprintf(buf, sizeof(buf), "%s-mime-%s", env, p);
+        icons = eina_list_append(icons, strdup(buf));
+
+        snprintf(buf, sizeof(buf), "%s-%s", env, p);
+        icons = eina_list_append(icons, strdup(buf));
+    }
+
+    /* Mime prefixed icon names */
+    snprintf(buf, sizeof(buf), "mime-%s", p);
+    icons = eina_list_append(icons, strdup(buf));
+
+    /* Generic icons */
+    pp = strdup(p);
+    while ((ppp = strrchr(pp, '-')))
+    {
+        *ppp = '\0';
+
+        snprintf(buf, sizeof(buf), "%s-x-generic", pp);
+        icons = eina_list_append(icons, strdup(buf));
+
+        snprintf(buf, sizeof(buf), "%s-generic", pp);
+        icons = eina_list_append(icons, strdup(buf));
+
+        snprintf(buf, sizeof(buf), "%s", pp);
+        icons = eina_list_append(icons, strdup(buf));
+    }
+    FREE(pp);
+
+    /* Search for icons using list */
+    icon = efreet_icon_list_find(theme, icons, size);
+    while (icons)
+    {
+        data = eina_list_data_get(icons);
+        free(data);
+        icons = eina_list_remove_list(icons, icons);
+    }
+
+    efreet_mime_icon_entry_add(mime, eina_stringshare_add(icon), theme, size);
+
+    return icon;
+}
+
+EAPI void
+efreet_mime_type_cache_clear(void)
+{
+    if (mime_icons)
+    {
+        eina_hash_free(mime_icons);
+        mime_icons_lru = NULL;
+    }
+    mime_icons = eina_hash_stringshared_new(EINA_FREE_CB(efreet_mime_icon_entry_head_free));
+}
+
+EAPI void
+efreet_mime_type_cache_flush(void)
+{
+    efreet_mime_icons_flush(ecore_loop_time_get());
+}
+
+
+EAPI const char *
+efreet_mime_magic_type_get(const char *file)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+    return efreet_mime_magic_check_priority(file, 0, 0);
+}
+
+EAPI const char *
+efreet_mime_globs_type_get(const char *file)
+{
+    Eina_List *l;
+    Efreet_Mime_Glob *g;
+    char *sl, *p;
+    const char *s;
+    char *ext, *mime;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+
+    /* Check in the extension hash for the type */
+    ext = strchr(file, '.');
+    if (ext)
+    {
+        sl = alloca(strlen(ext) + 1);
+        for (s = ext, p = sl; *s; s++, p++) *p = tolower(*s);
+        *p = 0;
+        p = sl;
+        while (p)
+        {
+            p++;
+            if (p && (mime = eina_hash_find(wild, p))) return mime;
+            p = strchr(p, '.');
+        }
+    }
+
+    /* Fallback to the other globs if not found */
+    EINA_LIST_FOREACH(globs, l, g)
+    {
+        if (efreet_mime_glob_match(file, g->glob))
+            return g->mime;
+    }
+
+    ext = alloca(strlen(file) + 1);
+    for (s = file, p = ext; *s; s++, p++) *p = tolower(*s);
+    *p = 0;
+    EINA_LIST_FOREACH(globs, l, g)
+    {
+        if (efreet_mime_glob_case_match(ext, g->glob))
+            return g->mime;
+    }
+    return NULL;
+}
+
+EAPI const char *
+efreet_mime_special_type_get(const char *file)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+    return efreet_mime_special_check(file);
+}
+
+EAPI const char *
+efreet_mime_fallback_type_get(const char *file)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+    return efreet_mime_fallback_check(file);
+}
+
+/**
+ * @internal
+ * @return Returns the endianess
+ * @brief Retreive the endianess of the machine
+ */
+static int
+efreet_mime_endian_check(void)
+{
+    int test = 1;
+    return (*((char*)(&test)));
+}
+
+/**
+ * @internal
+ * @param file File to monitor
+ * @return Returns no value.
+ * @brief Creates a new file monitor if we aren't already monitoring the
+ * given file
+ */
+static void
+efreet_mime_monitor_add(const char *file)
+{
+    Ecore_File_Monitor *fm = NULL;
+
+    /* if this is already in our hash then we're already monitoring so no
+     * reason to re-monitor */
+    if (eina_hash_find(monitors, file))
+        return;
+
+    if ((fm = ecore_file_monitor_add(file, efreet_mime_cb_update_file, NULL)))
+    {
+        eina_hash_del(monitors, file, NULL);
+        eina_hash_add(monitors, file, fm);
+    }
+}
+
+/**
+ * @internal
+ * @param datadirs List of XDG data dirs
+ * @param datahome Path to XDG data home directory
+ * @return Returns no value
+ * @brief Read all glob files in XDG data/home dirs.
+ * Also reads the /etc/mime.types file.
+ */
+static void
+efreet_mime_load_globs(Eina_List *datadirs, const char *datahome)
+{
+    Eina_List *l;
+    char buf[4096];
+    const char *datadir = NULL;
+
+    IF_FREE_HASH(wild);
+    wild = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
+    while (globs)
+    {
+        efreet_mime_glob_free(eina_list_data_get(globs));
+        globs = eina_list_remove_list(globs, globs);
+    }
+
+    /*
+     * This is here for legacy reasons.  It is mentioned briefly
+     * in the spec and seems to still be quite valid.  It is
+     * loaded first so the globs files will override anything
+     * in here.
+    */
+    efreet_mime_mime_types_load("/etc/mime.types");
+
+    datadir = datahome;
+    snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
+    efreet_mime_shared_mimeinfo_globs_load(buf);
+
+    EINA_LIST_FOREACH(datadirs, l, datadir)
+    {
+        snprintf(buf, sizeof(buf), "%s/mime/globs", datadir);
+        efreet_mime_shared_mimeinfo_globs_load(buf);
+    }
+}
+
+/**
+ * @internal
+ * @param datadirs List of XDG data dirs
+ * @param datahome Path to XDG data home directory
+ * @return Returns no value
+ * @brief Read all magic files in XDG data/home dirs.
+ */
+static void
+efreet_mime_load_magics(Eina_List *datadirs, const char *datahome)
+{
+    Eina_List *l;
+    char buf[4096];
+    const char *datadir = NULL;
+
+    while (magics)
+    {
+        efreet_mime_magic_free(eina_list_data_get(magics));
+        magics = eina_list_remove_list(magics, magics);
+    }
+
+    datadir = datahome;
+    snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
+    efreet_mime_shared_mimeinfo_magic_load(buf);
+
+    EINA_LIST_FOREACH(datadirs, l, datadir)
+    {
+        snprintf(buf, sizeof(buf), "%s/mime/magic", datadir);
+        efreet_mime_shared_mimeinfo_magic_load(buf);
+    }
+}
+
+/**
+ * @internal
+ * @param data Data pointer passed to monitor_add
+ * @param monitor Ecore_File_Monitor associated with this event
+ * @param event The type of event
+ * @param path Path to the file that was updated
+ * @return Returns no value
+ * @brief Callback for all file monitors.  Just reloads the appropriate
+ * list depending on which file changed.  If it was a magic file
+ * only the magic list is updated.  If it was a glob file or /etc/mime.types,
+ * the globs are updated.
+ */
+static void
+efreet_mime_cb_update_file(void *data __UNUSED__,
+                    Ecore_File_Monitor *monitor __UNUSED__,
+                    Ecore_File_Event event __UNUSED__,
+                    const char *path)
+{
+    Eina_List *datadirs = NULL;
+    const char *datahome = NULL;
+
+    if (!(datahome = efreet_data_home_get()))
+        return;
+
+    if (!(datadirs = efreet_data_dirs_get()))
+        return;
+
+    if (strstr(path, "magic"))
+        efreet_mime_load_magics(datadirs, datahome);
+    else
+        efreet_mime_load_globs(datadirs, datahome);
+}
+
+/**
+ * @internal
+ * @param datadirs List of XDG data dirs
+ * @param datahome Path to XDG data home directory
+ * @return Returns 1 on success, 0 on failure
+ * @brief Initializes globs, magics, and monitors lists.
+ */
+static int
+efreet_mime_init_files(void)
+{
+    Eina_List *l;
+    Eina_List *datadirs = NULL;
+    char buf[PATH_MAX];
+    const char *datahome, *datadir = NULL;
+
+    if (!(datahome = efreet_data_home_get()))
+        return 0;
+
+    if (!(datadirs = efreet_data_dirs_get()))
+        return 0;
+
+    /*
+     * Add our file monitors
+     * We watch the directories so we can watch for new files
+     */
+    datadir = datahome;
+    snprintf(buf, sizeof(buf), "%s/mime", datadir);
+    efreet_mime_monitor_add(buf);
+
+    EINA_LIST_FOREACH(datadirs, l, datadir)
+    {
+        snprintf(buf, sizeof(buf), "%s/mime", datadir);
+        efreet_mime_monitor_add(buf);
+    }
+    efreet_mime_monitor_add("/etc/mime.types");
+
+    /* Load our mime information */
+    efreet_mime_load_globs(datadirs, datahome);
+    efreet_mime_load_magics(datadirs, datahome);
+
+    _mime_inode_symlink                   = eina_stringshare_add("inode/symlink");
+    _mime_inode_fifo              = eina_stringshare_add("inode/fifo");
+    _mime_inode_chardevice        = eina_stringshare_add("inode/chardevice");
+    _mime_inode_blockdevice       = eina_stringshare_add("inode/blockdevice");
+    _mime_inode_socket            = eina_stringshare_add("inode/socket");
+    _mime_inode_mountpoint        = eina_stringshare_add("inode/mountpoint");
+    _mime_inode_directory         = eina_stringshare_add("inode/directory");
+    _mime_application_x_executable = eina_stringshare_add("application/x-executable");
+    _mime_application_octet_stream = eina_stringshare_add("application/octet-stream");
+    _mime_text_plain               = eina_stringshare_add("text/plain");
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param file File to examine
+ * @return Returns mime type if special file, else NULL
+ * @brief Returns a mime type based on the stat of a file.
+ * This is used first to catch directories and other special
+ * files.  A NULL return doesn't necessarily mean failure, but
+ * can also mean the file is regular.
+ * @note Mapping of file types to mime types:
+ * Stat Macro   File Type           Mime Type
+ * S_IFREG      regular             NULL
+ * S_IFIFO      named pipe (fifo)   inode/fifo
+ * S_IFCHR      character special   inode/chardevice
+ * S_IFDIR      directory           inode/directory
+ * S_IFBLK      block special       inode/blockdevice
+ * S_IFLNK      symbolic link       inode/symlink
+ * S_IFSOCK     socket              inode/socket
+ *
+ * This function can also return inode/mount-point.
+ * This is calculated by comparing the st_dev of the directory
+ * against that of it's parent directory.  If they differ it
+ * is considered a mount point.
+ */
+static const char *
+efreet_mime_special_check(const char *file)
+{
+    struct stat s;
+    int path_len = 0;
+
+    /* no link on Windows < Vista */
+#ifdef _WIN32
+    if (!stat(file, &s))
+#else
+    if (!lstat(file, &s))
+#endif
+    {
+        if (S_ISREG(s.st_mode))
+            return NULL;
+
+#ifndef _WIN32
+        if (S_ISLNK(s.st_mode))
+        return _mime_inode_symlink;
+#endif
+
+        if (S_ISFIFO(s.st_mode))
+            return _mime_inode_fifo;
+
+        if (S_ISCHR(s.st_mode))
+            return _mime_inode_chardevice;
+
+        if (S_ISBLK(s.st_mode))
+            return _mime_inode_blockdevice;
+
+#ifndef _WIN32
+        if (S_ISSOCK(s.st_mode))
+            return _mime_inode_socket;
+#endif
+
+        if (S_ISDIR(s.st_mode))
+        {
+            struct stat s2;
+            char parent[PATH_MAX];
+            char path[PATH_MAX];
+
+            strncpy(path, file, PATH_MAX);
+
+            path_len = strlen(file);
+            strncpy(parent, path, PATH_MAX);
+
+            /* Kill any trailing slash */
+            parent[--path_len] = '\0';
+
+            /* Truncate to last slash */
+            while (parent[--path_len] != '/') parent[path_len] = '\0';
+
+#ifdef _WIN32
+            if (!stat(file, &s2))
+#else
+            if (!lstat(parent, &s2))
+#endif
+            {
+                if (s.st_dev != s2.st_dev)
+                    return _mime_inode_mountpoint;
+            }
+
+            return _mime_inode_directory;
+        }
+
+        return NULL;
+    }
+
+    return NULL;
+}
+
+/**
+ * @internal
+ * @param file File to examine
+ * @return Returns mime type or NULL if the file doesn't exist
+ * @brief Returns text/plain if the file appears to contain text and
+ * returns application/octet-stream if it appears to be binary.
+ */
+static const char *
+efreet_mime_fallback_check(const char *file)
+{
+    FILE *f = NULL;
+    char buf[32];
+    int i;
+
+    if (ecore_file_can_exec(file))
+        return _mime_application_x_executable;
+
+    if (!(f = fopen(file, "r"))) return NULL;
+
+    i = fread(buf, 1, sizeof(buf), f);
+    fclose(f);
+
+    if (i == 0) return _mime_application_octet_stream;
+
+    /*
+     * Check for ASCII control characters in the first 32 bytes.
+     * Line Feeds, carriage returns, and tabs are ignored as they are
+     * quite common in text files in the first 32 chars.
+     */
+    for (i -= 1; i >= 0; --i)
+    {
+        if ((buf[i] < 0x20) &&
+            (buf[i] != '\n') &&     /* Line Feed */
+            (buf[i] != '\r') &&     /* Carriage Return */
+            (buf[i] != '\t'))       /* Tab */
+            return _mime_application_octet_stream;
+    }
+
+    return _mime_text_plain;
+}
+
+/**
+ * @internal
+ * @param glob Glob to search for
+ * @return Returns 1 on success, 0 on failure
+ * @brief Removes a glob from the list
+ */
+static int
+efreet_mime_glob_remove(const char *glob)
+{
+    Efreet_Mime_Glob *mime = NULL;
+
+    if ((mime = eina_list_search_unsorted(globs, EINA_COMPARE_CB(strcmp), glob)))
+    {
+        globs = eina_list_remove(globs, mime);
+        IF_RELEASE(mime->glob);
+        IF_RELEASE(mime->mime);
+        FREE(mime);
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline const char *
+efreet_eat_space(const char *head, const Eina_File_Line *ln, Eina_Bool not)
+{
+   if (not)
+     {
+        while (!isspace(*head) && (head < ln->end))
+          head++;
+     }
+   else
+     {
+        while (isspace(*head) && (head < ln->end))
+          head++;
+     }
+
+   return head;
+}
+
+/**
+ * @internal
+ * @param file mime.types file to load
+ * @return Returns no value
+ * @brief Loads values from a mime.types style file
+ * into the globs list.
+ * @note Format:
+ * application/msaccess     mdb
+ * application/msword       doc dot
+ */
+static void
+efreet_mime_mime_types_load(const char *file)
+{
+   const Eina_File_Line *ln;
+   Eina_Iterator *it;
+   Eina_File *f;
+   const char *head_line;
+   const char *word_start;
+   const char *mimetype;
+
+   f = eina_file_open(file, 0);
+   if (!f) return ;
+
+   it = eina_file_map_lines(f);
+   if (it)
+     {
+        Eina_Strbuf *ext;
+
+        ext = eina_strbuf_new();
+
+        EINA_ITERATOR_FOREACH(it, ln)
+          {
+             head_line = efreet_eat_space(ln->start, ln, EINA_FALSE);
+             if (head_line == ln->end) continue ;
+
+             if (*head_line == '#') continue ;
+
+             word_start = head_line;
+             head_line = efreet_eat_space(head_line, ln, EINA_TRUE);
+
+             if (head_line == ln->end) continue ;
+             mimetype = eina_stringshare_add_length(word_start, head_line - word_start);
+             do
+               {
+                  head_line = efreet_eat_space(head_line, ln, EINA_FALSE);
+                  if (head_line == ln->end) break ;
+
+                  word_start = head_line;
+                  head_line = efreet_eat_space(head_line, ln, EINA_TRUE);
+
+                  eina_strbuf_append_length(ext, word_start, head_line - word_start);
+
+                  eina_hash_del(wild,
+                                eina_strbuf_string_get(ext),
+                                NULL);
+                  eina_hash_add(wild,
+                                eina_strbuf_string_get(ext),
+                                eina_stringshare_ref(mimetype));
+
+                  eina_strbuf_reset(ext);
+               }
+             while (head_line < ln->end);
+
+             eina_stringshare_del(mimetype);
+          }
+
+        eina_strbuf_free(ext);
+        eina_iterator_free(it);
+     }
+   eina_file_close(f);
+}
+
+/**
+ * @internal
+ * @param file globs file to load
+ * @return Returns no value
+ * @brief Loads values from a mime.types style file
+ * into the globs list.
+ * @note Format:
+ * text/vnd.wap.wml:*.wml
+ * application/x-7z-compressed:*.7z
+ * application/vnd.corel-draw:*.cdr
+ * text/spreadsheet:*.sylk
+ */
+static void
+efreet_mime_shared_mimeinfo_globs_load(const char *file)
+{
+    FILE *f = NULL;
+    char buf[4096], mimetype[4096], ext[4096], *p, *pp;
+    Efreet_Mime_Glob *mime = NULL;
+
+    f = fopen(file, "rb");
+    if (!f) return;
+
+    while (fgets(buf, sizeof(buf), f))
+    {
+        p = buf;
+        while (isspace(*p) && (*p != 0) && (*p != '\n')) p++;
+
+        if (*p == '#') continue;
+        if ((*p == '\n') || (*p == 0)) continue;
+
+        pp = p;
+        while ((*p != ':') && (*p != 0) && (*p != '\n')) p++;
+
+        if ((*p == '\n') || (*p == 0)) continue;
+        strncpy(mimetype, pp, (p - pp));
+        mimetype[p - pp] = 0;
+        p++;
+        pp = ext;
+
+        while ((*p != 0) && (*p != '\n'))
+        {
+            *pp = *p;
+            pp++;
+            p++;
+        }
+
+        *pp = 0;
+
+        if (ext[0] == '*' && ext[1] == '.')
+        {
+            eina_hash_del(wild, &(ext[2]), NULL);
+            eina_hash_add(wild, &(ext[2]),
+                            (void*)eina_stringshare_add(mimetype));
+        }
+        else
+        {
+            mime = NEW(Efreet_Mime_Glob, 1);
+            if (mime)
+            {
+                mime->mime = eina_stringshare_add(mimetype);
+                mime->glob = eina_stringshare_add(ext);
+                if ((!mime->mime) || (!mime->glob))
+                {
+                    IF_RELEASE(mime->mime);
+                    IF_RELEASE(mime->glob);
+                    FREE(mime);
+                }
+                else
+                {
+                    efreet_mime_glob_remove(ext);
+                    globs = eina_list_append(globs, mime);
+                }
+            }
+        }
+    }
+
+    fclose(f);
+}
+
+/**
+ * @internal
+ * @param in Number to count the digits
+ * @return Returns number of digits
+ * @brief Calculates and returns the number of digits
+ * in a number.
+ */
+static int
+efreet_mime_count_digits(int in)
+{
+    int i = 1, j = in;
+
+    if (j < 10) return 1;
+    while ((j /= 10) > 0) ++i;
+
+    return i;
+}
+
+/**
+ * @internal
+ * @param file File to parse
+ * @return Returns no value
+ * @brief Loads a magic file and adds information to magics list
+ */
+static void
+efreet_mime_shared_mimeinfo_magic_load(const char *file)
+{
+    int fd = -1, size;
+    char *data = (void *)-1;
+
+    if (!file) return;
+
+    size = ecore_file_size(file);
+    if (size <= 0) return;
+
+    fd = open(file, O_RDONLY);
+    if (fd == -1) return;
+
+    /* let's make mmap safe and just get 0 pages for IO erro */
+    eina_mmap_safety_enabled_set(EINA_TRUE);
+
+    data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+    if (data == MAP_FAILED)
+    {
+        close(fd);
+        return;
+    }
+
+    efreet_mime_shared_mimeinfo_magic_parse(data, size);
+
+    munmap(data, size);
+    close(fd);
+}
+
+/**
+ * @param data The data from the file
+ * @return Returns no value
+ * @brief Parses a magic file
+ * @note Format:
+ *
+ * ----------------------------------------------------------------------
+ * |                     HEX                         |    ASCII         |
+ * ----------------------------------------------------------------------
+ * |4D 49 4D 45 2D 4D 61 67 69 63 00 0A 5B 39 30 3A  | MIME-Magic..[90: |
+ * |61 70 70 6C 69 63 61 74 69 6F 6E 2F 64 6F 63 62  | application/docb |
+ * |6F 6F 6B 2B 78 6D 6C 5D 0A 3E 30 3D 00 05 3C 3F  | ook+xml].>0=..<? |
+ * |78 6D 6C 0A 31 3E 30 3D 00 19 2D 2F 2F 4F 41 53  | xml.1>0=..-//OAS |
+ * |49 53 2F 2F 44 54 44 20 44 6F 63 42 6F 6F 6B 20  | IS//DTD DocBook  |
+ * |58 4D 4C 2B 31 30 31 0A 31 3E 30 3D 00 17 2D 2F  | XML+101.1>0=..-/ |
+ * ----------------------------------------------------------------------
+ *
+ * indent
+ *   The nesting depth of the rule, corresponding to the number of '>'
+ *   characters in the traditional file format.
+ * ">" start-offset
+ *     The offset into the file to look for a match.
+ * "=" value
+ *     Two bytes giving the (big-endian) length of the value, followed by the
+ *     value itself.
+ * "&" mask
+ *     The mask, which (if present) is exactly the same length as the value.
+ * "~" word-size
+ *     On little-endian machines, the size of each group to byte-swap.
+ * "+" range-length
+ *     The length of the region in the file to check.
+ *
+ * The indent, range-length, word-size and mask components are optional.
+ * If missing, indent defaults to 0, range-length to 1, the word-size to 1,
+ * and the mask to all 'one' bits.  In our case, mask is null as it is
+ * quicker, uses less memory and will achieve the same exact effect.
+ */
+static void
+efreet_mime_shared_mimeinfo_magic_parse(char *data, int size)
+{
+    Efreet_Mime_Magic *mime = NULL;
+    Efreet_Mime_Magic_Entry *entry = NULL;
+    char *ptr;
+
+    ptr = data;
+
+    /* make sure we're a magic file */
+    if (!ptr || (size < 12) || strncmp(ptr, "MIME-Magic\0\n", 12))
+        return;
+
+    ptr += 12;
+
+    for (; (ptr - data) < size; )
+    {
+        if (*ptr == '[')
+        {
+            char *val, buf[512];
+
+            mime = NEW(Efreet_Mime_Magic, 1);
+            magics = eina_list_append(magics, mime);
+
+            val = ++ptr;
+            while ((*val != ':')) val++;
+            memcpy(&buf, ptr, val - ptr);
+            buf[val - ptr] = '\0';
+
+            mime->priority = atoi(buf);
+            ptr = ++val;
+
+            while ((*val != ']')) val++;
+            memcpy(&buf, ptr, val - ptr);
+            buf[val - ptr] = '\0';
+
+            mime->mime = eina_stringshare_add(buf);
+            ptr = ++val;
+
+            while (*ptr != '\n') ptr++;
+            ptr++;
+        }
+        else
+        {
+            short tshort;
+
+            if (!mime) continue;
+            if (!entry)
+            {
+                if (!(entry = NEW(Efreet_Mime_Magic_Entry, 1)))
+                {
+                    IF_FREE_LIST(magics, efreet_mime_magic_free);
+                    return;
+                }
+
+                entry->indent = 0;
+                entry->offset = 0;
+                entry->value_len = 0;
+                entry->word_size = 1;
+                entry->range_len = 1;
+                entry->mask = NULL;
+                entry->value = NULL;
+
+                mime->entries = eina_list_append(mime->entries, entry);
+            }
+
+            switch(*ptr)
+            {
+                case '>':
+                    ptr ++;
+                    entry->offset = atoi(ptr);
+                    ptr += efreet_mime_count_digits(entry->offset);
+                    break;
+
+                case '=':
+                    ptr++;
+
+                    tshort = 0;
+                    memcpy(&tshort, ptr, sizeof(short));
+                    entry->value_len = ntohs(tshort);
+                    ptr += 2;
+
+                    entry->value = NEW(1, entry->value_len);
+                    memcpy(entry->value, ptr, entry->value_len);
+                    ptr += entry->value_len;
+                    break;
+
+                case '&':
+                    ptr++;
+                    entry->mask = NEW(1, entry->value_len);
+                    memcpy(entry->mask, ptr, entry->value_len);
+                    ptr += entry->value_len;
+                    break;
+
+                case '~':
+                    ptr++;
+                    entry->word_size = atoi(ptr);
+                    if ((entry->word_size != 0) && (((entry->word_size != 1)
+                                    && (entry->word_size != 2)
+                                    && (entry->word_size != 4))
+                                || (entry->value_len % entry->word_size)))
+                    {
+                        /* Invalid, Destroy */
+                        FREE(entry->value);
+                        FREE(entry->mask);
+                        FREE(entry);
+
+                        while (*ptr != '\n') ptr++;
+                        break;
+                    }
+
+                    if (efreet_mime_endianess == EFREET_ENDIAN_LITTLE)
+                    {
+                        int j;
+
+                        for (j = 0; j < entry->value_len; j += entry->word_size)
+                        {
+                            if (entry->word_size == 2)
+                            {
+                                ((short*)entry->value)[j] =
+                                              ntohs(((short*)entry->value)[j]);
+
+                                if (entry->mask)
+                                    ((short*)entry->mask)[j] =
+                                              ntohs(((short*)entry->mask)[j]);
+                            }
+                            else if (entry->word_size == 4)
+                            {
+                                ((int*)entry->value)[j] =
+                                              ntohl(((int*)entry->value)[j]);
+
+                                if (entry->mask)
+                                    ((int*)entry->mask)[j] =
+                                              ntohl(((int*)entry->mask)[j]);
+                            }
+                        }
+                    }
+
+                    ptr += efreet_mime_count_digits(entry->word_size);
+                    break;
+
+                case '+':
+                    ptr++;
+                    entry->range_len = atoi(ptr);
+                    ptr += efreet_mime_count_digits(entry->range_len);
+                    break;
+
+                case '\n':
+                    ptr++;
+                    entry = NULL;
+                    break;
+
+                default:
+                    if (isdigit(*ptr))
+                    {
+                        entry->indent = atoi(ptr);
+                        ptr += efreet_mime_count_digits(entry->indent);
+                    }
+                    break;
+            }
+        }
+    }
+/*
+    if (entry)
+    {
+        IF_FREE(entry->value);
+        IF_FREE(entry->mask);
+        FREE(entry);
+    }
+ */
+}
+
+/**
+ * @internal
+ * @param file File to check
+ * @param start Start priority, if 0 start at beginning
+ * @param end End priority, should be less then start
+ * unless start
+ * @return Returns mime type for file if found, NULL if not
+ * @brief Applies magic rules to a file given a start and end priority
+ */
+static const char *
+efreet_mime_magic_check_priority(const char *file,
+                                  unsigned int start,
+                                  unsigned int end)
+{
+    Efreet_Mime_Magic *m = NULL;
+    Efreet_Mime_Magic_Entry *e = NULL;
+    Eina_List *l, *ll;
+    FILE *f = NULL;
+    unsigned int i = 0, offset = 0,level = 0, match = 0, bytes_read = 0;
+    const char *last_mime = NULL;
+    char c, v, buf[EFREET_MIME_MAGIC_BUFFER_SIZE];
+
+    f = fopen(file, "rb");
+    if (!f) return NULL;
+
+    if (!magics)
+    {
+        fclose(f);
+        return NULL;
+    }
+
+    if ((bytes_read = fread(buf, 1, sizeof(buf), f)) == 0)
+    {
+        fclose(f);
+        return NULL;
+    }
+
+    EINA_LIST_FOREACH(magics, l, m)
+    {
+        if ((start != 0) && (m->priority > start))
+            continue;
+
+        if (m->priority < end)
+            break;
+
+        EINA_LIST_FOREACH(m->entries, ll, e)
+        {
+            if ((level < e->indent) && !match)
+                continue;
+
+            if ((level >= e->indent) && !match)
+                level = e->indent;
+
+            else if ((level > e->indent) && match)
+            {
+                fclose(f);
+                return last_mime;
+            }
+
+            for (offset = e->offset; offset < e->offset + e->range_len; offset++)
+            {
+                if (((offset + e->value_len) > bytes_read) &&
+                        (fseek(f, offset, SEEK_SET) == -1))
+                    break;
+
+                match = 1;
+                for (i = 0; i < e->value_len; ++i)
+                {
+                    if (offset + e->value_len > bytes_read)
+                        c = fgetc(f);
+                    else
+                        c = buf[offset + i];
+
+                    v = e->value[i];
+                    if (e->mask) v &= e->mask[i];
+
+                    if (!(c == v))
+                    {
+                        match = 0;
+                        break;
+                    }
+                }
+
+                if (match)
+                {
+                    level += 1;
+                    last_mime = m->mime;
+                    break;
+                }
+            }
+        }
+
+        if (match)
+        {
+            fclose(f);
+            return last_mime;
+        }
+    }
+    fclose(f);
+
+    return NULL;
+}
+
+/**
+ * @internal
+ * @param data Data pointer that is being destroyed
+ * @return Returns no value
+ * @brief Callback for globs destroy
+ */
+static void
+efreet_mime_glob_free(void *data)
+{
+    Efreet_Mime_Glob *m = data;
+
+    IF_RELEASE(m->mime);
+    IF_RELEASE(m->glob);
+    IF_FREE(m);
+}
+
+/**
+ * @internal
+ * @param data Data pointer that is being destroyed
+ * @return Returns no value
+ * @brief Callback for magics destroy
+ */
+static void
+efreet_mime_magic_free(void *data)
+{
+    Efreet_Mime_Magic *m = data;
+
+    IF_RELEASE(m->mime);
+    IF_FREE_LIST(m->entries, efreet_mime_magic_entry_free);
+    IF_FREE(m);
+}
+
+/**
+ * @internal
+ * @param data Data pointer that is being destroyed
+ * @return Returns no value
+ * @brief Callback for magic entry destroy
+ */
+static void
+efreet_mime_magic_entry_free(void *data)
+{
+    Efreet_Mime_Magic_Entry *e = data;
+
+    IF_FREE(e->mask);
+    IF_FREE(e->value);
+    IF_FREE(e);
+}
+
+
+/**
+ * @internal
+ * @param str String (filename) to match
+ * @param glob Glob to match str to
+ * @return Returns 1 on success, 0 on failure
+ * @brief Compares str to glob, case sensitive
+ */
+static int
+efreet_mime_glob_match(const char *str, const char *glob)
+{
+    if (!str || !glob) return 0;
+    if (glob[0] == 0)
+    {
+        if (str[0] == 0) return 1;
+        return 0;
+    }
+    if (!fnmatch(glob, str, 0)) return 1;
+    return 0;
+}
+
+/**
+ * @internal
+ * @param str String (filename) to match
+ * @param glob Glob to match str to
+ * @return Returns 1 on success, 0 on failure
+ * @brief Compares str to glob, case insensitive (expects str already in lower case)
+ */
+static int
+efreet_mime_glob_case_match(char *str, const char *glob)
+{
+    const char *p;
+    char *tglob, *tp;
+
+    if (!str || !glob) return 0;
+    if (glob[0] == 0)
+    {
+        if (str[0] == 0) return 1;
+        return 0;
+    }
+    tglob = alloca(strlen(glob) + 1);
+    for (tp = tglob, p = glob; *p; p++, tp++) *tp = tolower(*p);
+    *tp = 0;
+    if (!fnmatch(str, tglob, 0)) return 1;
+    return 0;
+}
+
+static void
+efreet_mime_icons_flush(double now)
+{
+    Eina_Inlist *l;
+    static double old = 0;
+    int todo;
+
+    if (now - old < EFREET_MIME_ICONS_FLUSH_TIMEOUT)
+        return;
+    old = now;
+
+    todo = eina_hash_population(mime_icons) - EFREET_MIME_ICONS_MAX_POPULATION;
+    if (todo <= 0)
+        return;
+
+    l = mime_icons_lru->last; /* mime_icons_lru is not NULL, since todo > 0 */
+    for (; todo > 0; todo--)
+    {
+        Efreet_Mime_Icon_Entry_Head *entry = (Efreet_Mime_Icon_Entry_Head *)l;
+        Eina_Inlist *prev = l->prev;
+
+        mime_icons_lru = eina_inlist_remove(mime_icons_lru, l);
+        eina_hash_del_by_key(mime_icons, entry->mime);
+        l = prev;
+    }
+
+    efreet_mime_icons_debug();
+}
+
+static void
+efreet_mime_icon_entry_free(Efreet_Mime_Icon_Entry *node)
+{
+    eina_stringshare_del(node->icon);
+    eina_stringshare_del(node->theme);
+    free(node);
+}
+
+static void
+efreet_mime_icon_entry_head_free(Efreet_Mime_Icon_Entry_Head *entry)
+{
+    while (entry->list)
+    {
+        Efreet_Mime_Icon_Entry *n = (Efreet_Mime_Icon_Entry *)entry->list;
+        entry->list = eina_inlist_remove(entry->list, entry->list);
+        efreet_mime_icon_entry_free(n);
+    }
+
+    eina_stringshare_del(entry->mime);
+    free(entry);
+}
+
+static Efreet_Mime_Icon_Entry *
+efreet_mime_icon_entry_new(const char *icon,
+                           const char *theme,
+                           unsigned int size)
+{
+    Efreet_Mime_Icon_Entry *entry;
+
+    entry = malloc(sizeof(*entry));
+    if (!entry)
+        return NULL;
+
+    entry->icon = icon;
+    entry->theme = theme;
+    entry->size = size;
+
+    return entry;
+}
+
+static void
+efreet_mime_icon_entry_add(const char *mime,
+                           const char *icon,
+                           const char *theme,
+                           unsigned int size)
+{
+    Efreet_Mime_Icon_Entry_Head *entry;
+    Efreet_Mime_Icon_Entry *n;
+
+    n = efreet_mime_icon_entry_new(icon, theme, size);
+    if (!n)
+        return;
+    entry = eina_hash_find(mime_icons, mime);
+
+    if (entry)
+    {
+        Eina_Inlist *l;
+
+        l = EINA_INLIST_GET(n);
+        entry->list = eina_inlist_prepend(entry->list, l);
+
+        l = EINA_INLIST_GET(entry);
+        mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
+    }
+    else
+    {
+        Eina_Inlist *l;
+
+        entry = malloc(sizeof(*entry));
+        if (!entry)
+        {
+            efreet_mime_icon_entry_free(n);
+            return;
+        }
+
+        l = EINA_INLIST_GET(n);
+        entry->list = eina_inlist_prepend(NULL, l);
+        entry->mime = mime;
+        eina_hash_direct_add(mime_icons, mime, entry);
+
+        l = EINA_INLIST_GET(entry);
+        mime_icons_lru = eina_inlist_prepend(mime_icons_lru, l);
+    }
+
+    entry->timestamp = ecore_loop_time_get();
+    efreet_mime_icons_flush(entry->timestamp);
+}
+
+static const char *
+efreet_mime_icon_entry_find(const char *mime,
+                            const char *theme,
+                            unsigned int size)
+{
+    Efreet_Mime_Icon_Entry_Head *entry;
+    Efreet_Mime_Icon_Entry *n;
+
+    entry = eina_hash_find(mime_icons, mime);
+    if (!entry)
+        return NULL;
+
+    EINA_INLIST_FOREACH(entry->list, n)
+    {
+        if ((n->theme == theme) && (n->size == size))
+        {
+            Eina_Inlist *l;
+
+            l = EINA_INLIST_GET(n);
+            if (entry->list != l)
+                entry->list = eina_inlist_promote(entry->list, l);
+
+            l = EINA_INLIST_GET(entry);
+            if (mime_icons_lru != l)
+                mime_icons_lru = eina_inlist_promote(mime_icons_lru, l);
+
+            entry->timestamp = ecore_loop_time_get();
+            return n->icon;
+        }
+    }
+
+    return NULL;
+}
+
+#ifdef EFREET_MIME_ICONS_DEBUG
+static void
+efreet_mime_icons_debug(void)
+{
+    double now = ecore_loop_time_get();
+    Efreet_Mime_Icon_Entry_Head *entry;
+    EINA_INLIST_FOREACH(mime_icons_lru, entry)
+    {
+        Efreet_Mime_Icon_Entry *n;
+
+        if ((now > 0) &&
+            (now - entry->timestamp >= EFREET_MIME_ICONS_EXPIRE_TIMEOUT))
+        {
+            puts("*** FOLLOWING ENTRIES ARE AGED AND CAN BE EXPIRED ***");
+            now = 0;
+        }
+
+        DBG("mime-icon entry: '%s' last used: %s",
+            entry->mime, ctime(&entry->timestamp));
+
+        EINA_INLIST_FOREACH(entry->list, n)
+            DBG("\tsize: %3u theme: '%s' icon: '%s'",
+                   n->theme, n->size, n->icon);
+    }
+}
+#else
+static void
+efreet_mime_icons_debug(void)
+{
+}
+#endif
diff --git a/src/lib/efreet_private.h b/src/lib/efreet_private.h
new file mode 100644 (file)
index 0000000..ac43c4a
--- /dev/null
@@ -0,0 +1,227 @@
+#ifndef EFREET_PRIVATE_H
+#define EFREET_PRIVATE_H
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(str) dgettext(PACKAGE, str)
+#else
+# define _(str) (str)
+#endif
+
+/**
+ * @file efreet_private.h
+ * @brief Contains methods and defines that are private to the Efreet
+ * implementaion
+ * @addtogroup Efreet_Private Efreet_Private: Private methods and defines
+ *
+ * @{
+ */
+
+/**
+ * @def NEW(x, c)
+ * Allocate and zero out c structures of type x
+ */
+#define NEW(x, c) calloc(c, sizeof(x))
+
+/**
+ * @def FREE(x)
+ * Free x and set to NULL
+ */
+#define FREE(x) do { free(x); x = NULL; } while (0)
+
+/**
+ * @def IF_FREE(x)
+ * If x is set, free x and set to NULL
+ */
+#define IF_FREE(x) do { if (x) FREE(x); } while (0)
+
+/**
+ * @def IF_RELEASE(x)
+ * If x is set, eina_stringshare_del x and set to NULL
+ */
+#define IF_RELEASE(x) do { \
+    if (x) { \
+        const char *__tmp; __tmp = (x); (x) = NULL; eina_stringshare_del(__tmp); \
+    } \
+    (x) = NULL; \
+} while (0)
+
+/**
+ * @def IF_FREE_LIST(x)
+ * If x is a valid pointer destroy x and set to NULL
+ */
+#define IF_FREE_LIST(list, free_cb) do { \
+    void *_data; \
+    EINA_LIST_FREE(list, _data) \
+    free_cb(_data); \
+    list = NULL; \
+} while (0)
+
+/**
+ * @def IF_FREE_HASH(x)
+ * If x is a valid pointer destroy x and set to NULL
+ */
+#define IF_FREE_HASH(x) do { \
+    if (x) { \
+        Eina_Hash *__tmp; __tmp = (x); (x) = NULL; eina_hash_free(__tmp); \
+    } \
+    (x) = NULL; \
+} while (0)
+
+/**
+ * @def IF_FREE_HASH_CB(x, cb)
+ * If x is a valid pointer destroy x with cb and set to NULL
+ */
+#define IF_FREE_HASH_CB(x, cb) do { \
+    if (x) { \
+        Eina_Hash *__tmp; __tmp = (x); (x) = NULL; efreet_hash_free(__tmp, cb); \
+    } \
+    (x) = NULL; \
+} while (0)
+
+#ifdef EFREET_DEFAULT_LOG_COLOR
+#undef EFREET_DEFAULT_LOG_COLOR
+#endif
+#define EFREET_DEFAULT_LOG_COLOR "\033[36m"
+
+#ifndef EFREET_MODULE_LOG_DOM
+#error "Need to define a log domain"
+#endif
+
+/**
+ * macros that are used all around the code for message processing
+ * four macros are defined ERR, WRN, DGB, INF. 
+ * EFREET_MODULE_LOG_DOM should be defined individually for each module
+ */
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(EFREET_MODULE_LOG_DOM, __VA_ARGS__)
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(EFREET_MODULE_LOG_DOM, __VA_ARGS__)
+#ifdef INF
+#undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(EFREET_MODULE_LOG_DOM, __VA_ARGS__)
+#ifdef WRN
+#undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(EFREET_MODULE_LOG_DOM, __VA_ARGS__)
+
+typedef struct _Efreet_Cache_Icon Efreet_Cache_Icon;
+typedef struct _Efreet_Cache_Icon_Element Efreet_Cache_Icon_Element;
+typedef struct _Efreet_Cache_Fallback_Icon Efreet_Cache_Fallback_Icon;
+
+struct _Efreet_Cache_Icon
+{
+    const char *theme;
+
+    Efreet_Cache_Icon_Element **icons;
+    unsigned int icons_count;
+};
+
+struct _Efreet_Cache_Icon_Element
+{
+    const char **paths;          /* possible paths for icon */
+    unsigned int paths_count;
+
+    unsigned short type;         /* size type of icon */
+
+    unsigned short normal;       /* The size for this icon */
+    unsigned short min;          /* The minimum size for this icon */
+    unsigned short max;          /* The maximum size for this icon */
+};
+
+struct _Efreet_Cache_Fallback_Icon
+{
+    const char *theme;
+    const char **icons;
+    unsigned int icons_count;
+};
+
+typedef struct _Efreet_Cache_Version Efreet_Cache_Version;
+struct _Efreet_Cache_Version
+{
+    unsigned char major;
+    unsigned char minor;
+};
+
+typedef struct _Efreet_Cache_Hash Efreet_Cache_Hash;
+struct _Efreet_Cache_Hash
+{
+    Eina_Hash *hash;
+};
+
+typedef struct _Efreet_Cache_Array_String Efreet_Cache_Array_String;
+struct _Efreet_Cache_Array_String
+{
+    const char **array;
+    unsigned int array_count;
+};
+
+int efreet_base_init(void);
+void efreet_base_shutdown(void);
+
+int efreet_cache_init(void);
+void efreet_cache_shutdown(void);
+
+int efreet_icon_init(void);
+void efreet_icon_shutdown(void);
+
+int efreet_menu_init(void);
+void efreet_menu_shutdown(void);
+EAPI Eina_List *efreet_default_dirs_get(const char *user_dir,
+                                        Eina_List *system_dirs,
+                                        const char *suffix);
+
+int efreet_ini_init(void);
+void efreet_ini_shutdown(void);
+
+int efreet_desktop_init(void);
+void efreet_desktop_shutdown(void);
+
+int efreet_util_init(void);
+int efreet_util_shutdown(void);
+
+const char *efreet_home_dir_get(void);
+void        efreet_dirs_reset(void);
+
+const char *efreet_lang_get(void);
+const char *efreet_lang_country_get(void);
+const char *efreet_lang_modifier_get(void);
+
+size_t efreet_array_cat(char *buffer, size_t size, const char *strs[]);
+
+void efreet_cache_desktop_update(void);
+void efreet_cache_desktop_close(void);
+void efreet_cache_icon_update(void);
+
+Efreet_Desktop *efreet_cache_desktop_find(const char *file);
+void efreet_cache_desktop_free(Efreet_Desktop *desktop);
+void efreet_cache_desktop_add(Efreet_Desktop *desktop);
+Efreet_Cache_Array_String *efreet_cache_desktop_dirs(void);
+
+Efreet_Cache_Icon *efreet_cache_icon_find(Efreet_Icon_Theme *theme, const char *icon);
+Efreet_Cache_Fallback_Icon *efreet_cache_icon_fallback_find(const char *icon);
+Efreet_Icon_Theme *efreet_cache_icon_theme_find(const char *theme);
+Eina_List *efreet_cache_icon_theme_list(void);
+
+Efreet_Cache_Hash *efreet_cache_util_hash_string(const char *key);
+Efreet_Cache_Hash *efreet_cache_util_hash_array_string(const char *key);
+Efreet_Cache_Array_String *efreet_cache_util_names(const char *key);
+
+EAPI void efreet_cache_array_string_free(Efreet_Cache_Array_String *array);
+
+EAPI void efreet_hash_free(Eina_Hash *hash, Eina_Free_Cb free_cb);
+EAPI void efreet_setowner(const char *path);
+EAPI void efreet_fsetowner(int fd);
+
+EAPI extern int efreet_cache_update;
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_trash.c b/src/lib/efreet_trash.c
new file mode 100644 (file)
index 0000000..27cc2b1
--- /dev/null
@@ -0,0 +1,288 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_trash_log_dom
+static int _efreet_trash_log_dom = -1;
+
+#include "Efreet.h"
+#include "Efreet_Trash.h"
+#include "efreet_private.h"
+
+static unsigned int _efreet_trash_init_count = 0;
+static const char *efreet_trash_dir = NULL;
+
+#ifdef _WIN32
+# define getuid() GetCurrentProcessId()
+#endif
+
+EAPI int
+efreet_trash_init(void)
+{
+    if (++_efreet_trash_init_count != 1)
+        return _efreet_trash_init_count;
+
+    if (!eina_init())
+        return --_efreet_trash_init_count;
+
+    _efreet_trash_log_dom = eina_log_domain_register
+      ("efreet_trash", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_trash_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_trash");
+        eina_shutdown();
+        return --_efreet_trash_init_count;
+    }
+    return _efreet_trash_init_count;
+}
+
+EAPI int
+efreet_trash_shutdown(void)
+{
+    if (--_efreet_trash_init_count != 0)
+        return _efreet_trash_init_count;
+
+    IF_RELEASE(efreet_trash_dir);
+    eina_log_domain_unregister(_efreet_trash_log_dom);
+    _efreet_trash_log_dom = -1;
+    eina_shutdown();
+
+    return _efreet_trash_init_count;
+}
+
+EAPI const char*
+efreet_trash_dir_get(const char *file)
+{
+    char buf[PATH_MAX];
+    struct stat s_dest;
+    struct stat s_src;
+    const char *trash_dir = NULL;
+
+    if (file)
+    {
+        if (stat(efreet_data_home_get(), &s_dest) != 0)
+            return NULL;
+
+        if (stat(file, &s_src) != 0)
+            return NULL;
+    }
+
+    if (!file || s_src.st_dev == s_dest.st_dev)
+    {
+        if (efreet_trash_dir && ecore_file_exists(efreet_trash_dir))
+        {
+            eina_stringshare_ref(efreet_trash_dir);
+            return efreet_trash_dir;
+        }
+
+        snprintf(buf, sizeof(buf), "%s/Trash", efreet_data_home_get());
+        if (!ecore_file_exists(buf) && !ecore_file_mkpath(buf))
+            return NULL;
+
+        IF_RELEASE(efreet_trash_dir);
+        efreet_trash_dir = eina_stringshare_add(buf);
+        trash_dir = eina_stringshare_ref(efreet_trash_dir);
+    }
+    else
+    {
+        char *dir;
+        char path[PATH_MAX];
+
+        strncpy(buf, file, PATH_MAX);
+        buf[PATH_MAX - 1] = 0;
+        path[0] = 0;
+
+        while (strlen(buf) > 1)
+        {
+            strncpy(path, buf, PATH_MAX);
+            dir = dirname(buf);
+
+            if (stat(dir, &s_dest) == 0)
+            {
+                if (s_src.st_dev == s_dest.st_dev){
+
+                    strncpy(buf, dir, PATH_MAX);
+                    continue;
+                }
+                else
+                {
+                    /* other device */
+                    break;
+                }
+            }
+            path[0] = 0;
+            break;
+        }
+
+        if (path[0])
+        {
+            snprintf(buf, sizeof(buf), "%s/.Trash-%d", path, getuid());
+            if (!ecore_file_exists(buf) && !ecore_file_mkpath(buf))
+                return NULL;
+
+            trash_dir = eina_stringshare_add(buf);
+        }
+    }
+    if (trash_dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/files", trash_dir);
+        if (!ecore_file_exists(buf) && !ecore_file_mkpath(buf))
+        {
+            eina_stringshare_del(trash_dir);
+            return NULL;
+        }
+
+        snprintf(buf, sizeof(buf), "%s/info", trash_dir);
+        if (!ecore_file_exists(buf) && !ecore_file_mkpath(buf))
+        {
+            eina_stringshare_del(trash_dir);
+            return NULL;
+        }
+    }
+
+    return trash_dir;
+}
+
+EAPI int
+efreet_trash_delete_uri(Efreet_Uri *uri, int force_delete)
+{
+    char dest[PATH_MAX];
+    char times[64];
+    const char *fname;
+    const char *escaped;
+    const char *trash_dir;
+    int i = 1;
+    time_t now;
+    FILE *f;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(uri, 0);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(uri->path, 0);
+    EINA_SAFETY_ON_FALSE_RETURN_VAL(ecore_file_can_write(uri->path), 0);
+
+    fname = ecore_file_file_get(uri->path);
+
+    trash_dir = efreet_trash_dir_get(uri->path);
+    if (!trash_dir)
+    {
+        ERR("EFREET TRASH ERROR: No trash directory.");
+        return 0;
+    }
+    snprintf(dest, sizeof(dest), "%s/files/%s", trash_dir, fname);
+
+    /* search for a free filename */
+    while (ecore_file_exists(dest) && (i < 100))
+        snprintf(dest, sizeof(dest), "%s/files/%s$%d",
+                    trash_dir, fname, i++);
+
+    fname = ecore_file_file_get(dest);
+
+    /* move file to trash dir */
+    if (rename(uri->path, dest))
+    {
+        if (errno == EXDEV)
+        {
+            if (!force_delete)
+            {
+                eina_stringshare_del(trash_dir);
+                return -1;
+            }
+
+            if (!ecore_file_recursive_rm(uri->path))
+            {
+                ERR("EFREET TRASH ERROR: Can't delete file.");
+                eina_stringshare_del(trash_dir);
+                return 0;
+            }
+        }
+        else
+        {
+            ERR("EFREET TRASH ERROR: Can't move file to trash.");
+            eina_stringshare_del(trash_dir);
+            return 0;
+        }
+    }
+
+    /* create info file */
+    snprintf(dest, sizeof(dest), "%s/info/%s.trashinfo", trash_dir, fname);
+
+    if ((f = fopen(dest, "w")))
+    {
+        fputs("[Trash Info]\n", f);
+
+        fputs("Path=", f);
+        escaped = efreet_uri_encode(uri);
+        fputs(escaped + 7, f); // +7 == don't write 'file://'
+        IF_RELEASE(escaped);
+
+        time(&now);
+        strftime(times, sizeof(times), "%Y-%m-%dT%H:%M:%S", localtime(&now));
+        fputs("\nDeletionDate=", f);
+        fputs(times, f);
+        fputs("\n", f);
+        fclose(f);
+    }
+    else
+    {
+        ERR("EFREET TRASH ERROR: Can't create trash info file.");
+        return 0;
+    }
+
+    return 1;
+}
+
+EAPI int
+efreet_trash_is_empty(void)
+{
+    char buf[PATH_MAX];
+
+    snprintf(buf, sizeof(buf), "%s/files", efreet_trash_dir_get(NULL));
+
+    /* TODO Check also trash in other filesystems */
+    return ecore_file_dir_is_empty(buf);
+}
+
+EAPI int
+efreet_trash_empty_trash(void)
+{
+    char buf[PATH_MAX];
+
+    snprintf(buf, sizeof(buf), "%s/info", efreet_trash_dir_get(NULL));
+    if (!ecore_file_recursive_rm(buf)) return 0;
+    ecore_file_mkdir(buf);
+
+    snprintf(buf, sizeof(buf), "%s/files", efreet_trash_dir_get(NULL));
+    if (!ecore_file_recursive_rm(buf)) return 0;
+    ecore_file_mkdir(buf);
+
+    /* TODO Empty also trash in other filesystems */
+    return 1;
+}
+
+EAPI Eina_List*
+efreet_trash_ls(void)
+{
+    char *infofile;
+    char buf[PATH_MAX];
+    Eina_List *files, *l;
+
+    // NOTE THIS FUNCTION NOW IS NOT COMPLETE AS I DON'T NEED IT
+    // TODO read the name from the infofile instead of the filename
+
+    snprintf(buf, sizeof(buf), "%s/files", efreet_trash_dir_get(NULL));
+    files = ecore_file_ls(buf);
+
+    if (eina_log_domain_level_check(_efreet_trash_log_dom, EINA_LOG_LEVEL_INFO))
+        EINA_LIST_FOREACH(files, l, infofile)
+            INF("FILE: %s\n", infofile);
+
+    return files;
+}
+
diff --git a/src/lib/efreet_uri.c b/src/lib/efreet_uri.c
new file mode 100644 (file)
index 0000000..20ebe39
--- /dev/null
@@ -0,0 +1,119 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+
+#ifndef _POSIX_HOST_NAME_MAX
+#define _POSIX_HOST_NAME_MAX 255
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM /* no logging in this file */
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+
+EAPI Efreet_Uri *
+efreet_uri_decode(const char *full_uri)
+{
+    Efreet_Uri *uri;
+    const char *p;
+    char protocol[64], hostname[_POSIX_HOST_NAME_MAX], path[PATH_MAX];
+    int i = 0;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(full_uri, NULL);
+
+    /* An uri should be in the form <protocol>://<hostname>/<path> */
+    if (!strstr(full_uri, "://")) return NULL;
+
+    memset(protocol, 0, 64);
+    memset(hostname, 0, _POSIX_HOST_NAME_MAX);
+    memset(path, 0, PATH_MAX);
+
+    /* parse protocol */
+    p = full_uri;
+    for (i = 0; *p != ':' && *p != '\0' && i < 64; p++, i++)
+         protocol[i] = *p;
+    protocol[i] = '\0';
+
+    /* parse hostname */
+    p += 3;
+    if (*p != '/')
+    {
+        for (i = 0; *p != '/' && *p != '\0' && i < _POSIX_HOST_NAME_MAX; p++, i++)
+            hostname[i] = *p;
+        hostname[i] = '\0';
+    }
+    else
+        hostname[0] = '\0';
+
+    /* parse path */
+    /* See http://www.faqs.org/rfcs/rfc1738.html for the escaped chars */
+    for (i = 0; *p != '\0' && i < PATH_MAX; i++, p++)
+    {
+        if (*p == '%')
+        {
+            path[i] = *(++p);
+            path[i + 1] = *(++p);
+            path[i] = (char)strtol(&(path[i]), NULL, 16);
+            path[i + 1] = '\0';
+        }
+        else
+            path[i] = *p;
+    }
+
+    uri = NEW(Efreet_Uri, 1);
+    if (!uri) return NULL;
+
+    uri->protocol = eina_stringshare_add(protocol);
+    uri->hostname = eina_stringshare_add(hostname);
+    uri->path = eina_stringshare_add(path);
+
+    return uri;
+}
+
+EAPI const char *
+efreet_uri_encode(Efreet_Uri *uri)
+{
+    char dest[PATH_MAX * 3 + 4];
+    const char *p;
+    int i;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(uri, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(uri->path, NULL);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(uri->protocol, NULL);
+
+    memset(dest, 0, PATH_MAX * 3 + 4);
+    snprintf(dest, strlen(uri->protocol) + 4, "%s://", uri->protocol);
+
+    /* Most app doesn't handle the hostname in the uri so it's put to NULL */
+    for (i = strlen(uri->protocol) + 3, p = uri->path; *p != '\0'; p++, i++)
+    {
+        if (isalnum(*p) || strchr("/$-_.+!*'()", *p))
+            dest[i] = *p;
+        else
+        {
+            snprintf(&(dest[i]), 4, "%%%02X", (unsigned char) *p);
+            i += 2;
+        }
+    }
+
+    return eina_stringshare_add(dest);
+}
+
+EAPI void
+efreet_uri_free(Efreet_Uri *uri)
+{
+    if (!uri) return;
+
+    IF_RELEASE(uri->protocol);
+    IF_RELEASE(uri->path);
+    IF_RELEASE(uri->hostname);
+    FREE(uri);
+}
diff --git a/src/lib/efreet_uri.h b/src/lib/efreet_uri.h
new file mode 100644 (file)
index 0000000..32aaeee
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef EFREET_URI_H
+#define EFREET_URI_H
+
+/**
+ * @file efreet_uri.h
+ * @brief Contains the methods used to support the FDO URI specification.
+ * @addtogroup Efreet_Uri Efreet_Uri: The FDO URI Specification functions
+ * @{
+ */
+
+
+/**
+ * Efreet_Uri
+ */
+typedef struct Efreet_Uri Efreet_Uri;
+
+/**
+ * Efreet_Uri
+ * @brief Contains a simple rappresentation of an uri. The string don't have 
+ * special chars escaped.
+ */
+struct Efreet_Uri
+{
+    const char *protocol;   /**< The protocol used (usually 'file')*/
+    const char *hostname;   /**< The name of the host if any, or NULL */
+    const char *path;       /**< The full file path whitout protocol nor host*/
+};
+
+
+
+/**
+ * @param uri Create an URI string from an Efreet_Uri struct
+ * @return The string rapresentation of uri (ex: 'file:///home/my%20name')
+ * @brief Get the string rapresentation of the given uri struct escaping
+ * illegal caracters. Remember to free the string with eina_stringshare_del()
+ * when you don't need it anymore.
+ * @note The resulting string will contain the protocol and the path but not
+ * the hostname, as many apps doesn't handle it.
+ */
+EAPI const char *efreet_uri_encode(Efreet_Uri *uri);
+
+/**
+ * @param val a valid uri string to parse
+ * @return Return The corresponding Efreet_Uri structure. Or NULL on errors.
+ * @brief Read a single uri and return an Efreet_Uri struct. If there's no
+ * hostname in the uri then the hostname parameter will be NULL. All the uri
+ * escaped chars will be converted to normal.
+ */
+EAPI Efreet_Uri *efreet_uri_decode(const char *val);
+
+/**
+ * @param uri The uri to free
+ * @brief Free the given uri structure.
+ */
+EAPI void        efreet_uri_free(Efreet_Uri *uri);
+
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/lib/efreet_utils.c b/src/lib/efreet_utils.c
new file mode 100644 (file)
index 0000000..dab85ef
--- /dev/null
@@ -0,0 +1,488 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* TODO: add no_display check, as we might want only displayable items */
+
+#undef alloca
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <fnmatch.h>
+
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_utils_log_dom
+static int _efreet_utils_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static char *efreet_util_path_in_default(const char *section, const char *path);
+
+static int  efreet_util_glob_match(const char *str, const char *glob);
+
+static Eina_List *efreet_util_menus_find_helper(Eina_List *menus, const char *config_dir);
+
+static Efreet_Desktop *efreet_util_cache_find(const char *search, const char *what1, const char *what2);
+static Eina_List *efreet_util_cache_list(const char *search, const char *what);
+static Eina_List *efreet_util_cache_glob_list(const char *search, const char *what);
+
+static Eina_Hash *file_id_by_desktop_path = NULL;
+
+static int init = 0;
+
+int
+efreet_util_init(void)
+{
+    if (init++) return init;
+    _efreet_utils_log_dom = eina_log_domain_register
+      ("efreet_util", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_utils_log_dom < 0)
+    {
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_util");
+        return 0;
+    }
+
+    file_id_by_desktop_path = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
+
+    return init;
+}
+
+int
+efreet_util_shutdown(void)
+{
+    if (--init) return init;
+
+    eina_log_domain_unregister(_efreet_utils_log_dom);
+    _efreet_utils_log_dom = -1;
+    IF_FREE_HASH(file_id_by_desktop_path);
+
+    return init;
+}
+
+static char *
+efreet_util_path_in_default(const char *section, const char *path)
+{
+    Eina_List *dirs;
+    char *ret = NULL;
+    char *dir;
+
+    dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
+                                   section);
+
+    EINA_LIST_FREE(dirs, dir)
+    {
+        if (!strncmp(path, dir, strlen(dir)))
+            ret = dir;
+        else
+            eina_stringshare_del(dir);
+    }
+
+    return ret;
+}
+
+EAPI const char *
+efreet_util_path_to_file_id(const char *path)
+{
+    size_t len, len2;
+    char *tmp, *p;
+    char *base;
+    const char *file_id;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
+
+    file_id = eina_hash_find(file_id_by_desktop_path, path);
+    if (file_id) return file_id;
+
+    base = efreet_util_path_in_default("applications", path);
+    if (!base) return NULL;
+
+    len = strlen(base);
+    if (strlen(path) <= len)
+    {
+        eina_stringshare_del(base);
+        return NULL;
+    }
+    if (strncmp(path, base, len))
+    {
+        eina_stringshare_del(base);
+        return NULL;
+    }
+
+    len2 = strlen(path + len + 1) + 1;
+    tmp = alloca(len2);
+    memcpy(tmp, path + len + 1, len2);
+    p = tmp;
+    while (*p)
+    {
+        if (*p == '/') *p = '-';
+        p++;
+    }
+    eina_stringshare_del(base);
+    file_id = eina_stringshare_add(tmp);
+    eina_hash_add(file_id_by_desktop_path, path, (void *)file_id);
+    return file_id;
+}
+
+EAPI Eina_List *
+efreet_util_desktop_mime_list(const char *mime)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(mime, NULL);
+    return efreet_util_cache_list("mime_types", mime);
+}
+
+EAPI Efreet_Desktop *
+efreet_util_desktop_wm_class_find(const char *wmname, const char *wmclass)
+{
+    EINA_SAFETY_ON_TRUE_RETURN_VAL((!wmname) && (!wmclass), NULL);
+    return efreet_util_cache_find("startup_wm_class", wmname, wmclass);
+}
+
+EAPI Efreet_Desktop *
+efreet_util_desktop_file_id_find(const char *file_id)
+{
+    Efreet_Cache_Hash *hash;
+    Efreet_Desktop *ret = NULL;
+    const char *str;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(file_id, NULL);
+
+    hash = efreet_cache_util_hash_string("file_id");
+    if (!hash) return NULL;
+    str = eina_hash_find(hash->hash, file_id);
+    if (str)
+        ret = efreet_desktop_get(str);
+    return ret;
+}
+
+EAPI Efreet_Desktop *
+efreet_util_desktop_exec_find(const char *exec)
+{
+    Efreet_Cache_Hash *hash = NULL;
+    Efreet_Desktop *ret = NULL;
+    Efreet_Cache_Array_String *names = NULL;
+    unsigned int i;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(exec, NULL);
+
+    names = efreet_cache_util_names("exec_list");
+    if (!names) return NULL;
+    for (i = 0; i < names->array_count; i++)
+    {
+        const char *file;
+        char *exe;
+        unsigned int j;
+        Efreet_Cache_Array_String *array;
+
+        exe = ecore_file_app_exe_get(names->array[i]);
+        if (!exe) continue;
+        file = ecore_file_file_get(exe);
+        if (!file) continue;
+        if (strcmp(exec, exe) && strcmp(exec, file))
+        {
+            free(exe);
+            continue;
+        }
+        free(exe);
+
+        if (!hash)
+            hash = efreet_cache_util_hash_array_string("exec_hash");
+        if (!hash) return NULL;
+        array = eina_hash_find(hash->hash, names->array[i]);
+        if (!array) continue;
+        for (j = 0; j < array->array_count; j++)
+        {
+            ret = efreet_desktop_get(array->array[j]);
+            if (ret) break;
+        }
+        if (ret) break;
+    }
+    return ret;
+}
+
+EAPI Efreet_Desktop *
+efreet_util_desktop_name_find(const char *name)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
+    return efreet_util_cache_find("name", name, NULL);
+}
+
+EAPI Efreet_Desktop *
+efreet_util_desktop_generic_name_find(const char *generic_name)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(generic_name, NULL);
+    return efreet_util_cache_find("generic_name", generic_name, NULL);
+}
+
+EAPI Eina_List *
+efreet_util_desktop_name_glob_list(const char *glob)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(glob, NULL);
+    return efreet_util_cache_glob_list("name", glob);
+}
+
+EAPI Eina_List *
+efreet_util_desktop_exec_glob_list(const char *glob)
+{
+    Efreet_Cache_Hash *hash = NULL;
+    Eina_List *ret = NULL;
+    Efreet_Cache_Array_String *names = NULL;
+    unsigned int i;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(glob, NULL);
+
+    if (!strcmp(glob, "*"))
+        glob = NULL;
+
+    names = efreet_cache_util_names("exec_list");
+    if (!names) return NULL;
+    for (i = 0; i < names->array_count; i++)
+    {
+        Efreet_Cache_Array_String *array;
+        unsigned int j;
+        char *exe;
+        Efreet_Desktop *desk;
+
+        exe = ecore_file_app_exe_get(names->array[i]);
+        if (!exe) continue;
+        if (glob && !efreet_util_glob_match(exe, glob))
+        {
+            free(exe);
+            continue;
+        }
+        free(exe);
+
+        if (!hash)
+            hash = efreet_cache_util_hash_array_string("exec_hash");
+        if (!hash) return NULL;
+
+        array = eina_hash_find(hash->hash, names->array[i]);
+        if (!array) continue;
+        for (j = 0; j < array->array_count; j++)
+        {
+            desk = efreet_desktop_get(array->array[j]);
+            if (desk)
+                ret = eina_list_append(ret, desk);
+        }
+    }
+    return ret;
+}
+
+EAPI Eina_List *
+efreet_util_desktop_generic_name_glob_list(const char *glob)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(glob, NULL);
+    return efreet_util_cache_glob_list("generic_name", glob);
+}
+
+EAPI Eina_List *
+efreet_util_desktop_comment_glob_list(const char *glob)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(glob, NULL);
+    return efreet_util_cache_glob_list("comment", glob);
+}
+
+EAPI Eina_List *
+efreet_util_desktop_categories_list(void)
+{
+    Efreet_Cache_Array_String *array;
+    Eina_List *ret = NULL;
+    unsigned int i;
+
+    array = efreet_cache_util_names("categories_list");
+    if (!array) return NULL;
+    for (i = 0; i < array->array_count; i++)
+        ret = eina_list_append(ret, array->array[i]);
+    return ret;
+}
+
+EAPI Eina_List *
+efreet_util_desktop_category_list(const char *category)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(category, NULL);
+    return efreet_util_cache_list("categories", category);
+}
+
+static int
+efreet_util_glob_match(const char *str, const char *glob)
+{
+    if (!str || !glob)
+        return 0;
+    if (glob[0] == '\0')
+    {
+        if (str[0] == '\0') return 1;
+        return 0;
+    }
+    if (!strcmp(glob, "*")) return 1;
+    if (!fnmatch(glob, str, 0)) return 1;
+    return 0;
+}
+
+EAPI Eina_List *
+efreet_util_menus_find(void)
+{
+    Eina_List *menus = NULL;
+    Eina_List *dirs, *l;
+    const char *dir;
+
+    menus = efreet_util_menus_find_helper(menus, efreet_config_home_get());
+
+    dirs = efreet_config_dirs_get();
+    EINA_LIST_FOREACH(dirs, l, dir)
+        menus = efreet_util_menus_find_helper(menus, dir);
+
+    return menus;
+}
+
+static Eina_List *
+efreet_util_menus_find_helper(Eina_List *menus, const char *config_dir)
+{
+    Eina_Iterator *it;
+    Eina_File_Direct_Info *info;
+    char dbuf[PATH_MAX];
+
+    snprintf(dbuf, sizeof(dbuf), "%s/menus", config_dir);
+    it = eina_file_direct_ls(dbuf);
+    if (!it) return menus;
+    EINA_ITERATOR_FOREACH(it, info)
+    {
+        const char *exten;
+        exten = strrchr(info->path + info->name_start, '.');
+        if (!exten) continue;
+        if (strcmp(".menu", exten)) continue;
+
+        if (ecore_file_is_dir(info->path)) continue;
+
+        menus = eina_list_append(menus, strdup(info->path));
+    }
+    eina_iterator_free(it);
+    return menus;
+}
+
+static Efreet_Desktop *
+efreet_util_cache_find(const char *search, const char *what1, const char *what2)
+{
+    Efreet_Cache_Hash *hash;
+    Efreet_Desktop *ret = NULL;
+    Efreet_Cache_Array_String *array = NULL;
+    char key[256];
+
+    if ((!what1) && (!what2)) return NULL;
+
+    snprintf(key, sizeof(key), "%s_hash", search);
+    hash = efreet_cache_util_hash_array_string(key);
+    if (!hash) return NULL;
+    if (what1)
+        array = eina_hash_find(hash->hash, what1);
+    if (!array && what2) array = eina_hash_find(hash->hash, what2);
+    if (array)
+    {
+        unsigned int i;
+
+        for (i = 0; i < array->array_count; i++)
+        {
+            ret = efreet_desktop_get(array->array[i]);
+            if (ret) break;
+        }
+    }
+    return ret;
+}
+
+static Eina_List *
+efreet_util_cache_list(const char *search, const char *what)
+{
+    Efreet_Cache_Hash *hash;
+    Efreet_Cache_Array_String *array;
+    Eina_List *ret = NULL;
+    char key[256];
+
+    if (!what) return NULL;
+
+    snprintf(key, sizeof(key), "%s_hash", search);
+    hash = efreet_cache_util_hash_array_string(key);
+    if (!hash) return NULL;
+    array = eina_hash_find(hash->hash, what);
+    if (array)
+    {
+        unsigned int i;
+        Efreet_Desktop *desk;
+
+        for (i = 0; i < array->array_count; i++)
+        {
+            desk = efreet_desktop_get(array->array[i]);
+            if (desk)
+                ret = eina_list_append(ret, desk);
+        }
+    }
+    return ret;
+}
+
+static Eina_List *
+efreet_util_cache_glob_list(const char *search, const char *what)
+{
+    Efreet_Cache_Hash *hash = NULL;
+    Eina_List *ret = NULL;
+    Efreet_Cache_Array_String *names = NULL;
+    char key[256];
+    unsigned int i;
+
+    if (!what) return NULL;
+    if (!strcmp(what, "*"))
+        what = NULL;
+
+    snprintf(key, sizeof(key), "%s_list", search);
+    names = efreet_cache_util_names(key);
+    if (!names) return NULL;
+    for (i = 0; i < names->array_count; i++)
+    {
+        Efreet_Cache_Array_String *array;
+        unsigned int j;
+        Efreet_Desktop *desk;
+
+        if (what && !efreet_util_glob_match(names->array[i], what)) continue;
+
+        if (!hash)
+        {
+            snprintf(key, sizeof(key), "%s_hash", search);
+            hash = efreet_cache_util_hash_array_string(key);
+        }
+        if (!hash) return NULL;
+
+        array = eina_hash_find(hash->hash, names->array[i]);
+        if (!array) continue;
+        for (j = 0; j < array->array_count; j++)
+        {
+            desk = efreet_desktop_get(array->array[j]);
+            if (desk)
+                ret = eina_list_append(ret, desk);
+        }
+    }
+    return ret;
+}
+
+/*
+ * Needs EAPI because of helper binaries
+ */
+EAPI void
+efreet_hash_free(Eina_Hash *hash, Eina_Free_Cb free_cb)
+{
+    eina_hash_free_cb_set(hash, free_cb);
+    eina_hash_free(hash);
+}
+
diff --git a/src/lib/efreet_utils.h b/src/lib/efreet_utils.h
new file mode 100644 (file)
index 0000000..6a5e69e
--- /dev/null
@@ -0,0 +1,157 @@
+#ifndef EFREET_UTILS_H
+#define EFREET_UTILS_H
+
+/**
+ * @file efreet_utils.h
+ * @brief Contains utility functions to ease usage of Efreet.
+ *        FDO desktop entry specificiation.
+ * @addtogroup Efreet_Utils Efreet utilities for FDO
+ *
+ * @{
+ */
+
+
+/**
+ * Returns the fdo file id for a given path. If the file isn't inside
+ * a default fdo path it will return NULL.
+ *
+ * @param path The path to find the file id for
+ *
+ * @return File id for path or NULL
+ */
+EAPI const char *efreet_util_path_to_file_id(const char *path);
+
+
+/**
+ * Find all desktops for a given mime type
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param mime the mime type
+ * @return a list of desktops
+ */
+EAPI Eina_List *efreet_util_desktop_mime_list(const char *mime);
+
+
+/**
+ * Find all desktops for a given wm class
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param wmname the wm name
+ * @param wmclass the wm class
+ * @return a list of desktops
+ */
+EAPI Efreet_Desktop *efreet_util_desktop_wm_class_find(const char *wmname, const char *wmclass);
+
+/**
+ * Find a desktop by file id
+ *
+ * return value must be freed by efreet_desktop_free
+ *
+ * @param file_id the file id
+ * @return a desktop
+ */
+EAPI Efreet_Desktop *efreet_util_desktop_file_id_find(const char *file_id);
+
+/**
+ * Find a desktop by exec
+ *
+ * return value must be freed by efreet_desktop_free
+ *
+ * @param exec the exec name
+ * @return a desktop
+ */
+EAPI Efreet_Desktop *efreet_util_desktop_exec_find(const char *exec);
+
+/**
+ * Find a desktop by name
+ *
+ * return value must be freed by efreet_desktop_free
+ *
+ * @param name the name
+ * @return a desktop
+ */
+EAPI Efreet_Desktop *efreet_util_desktop_name_find(const char *name);
+
+/**
+ * Find a desktop by generic name
+ *
+ * return value must be freed by efreet_desktop_free
+ *
+ * @param generic_name the generic name
+ * @return a desktop
+ */
+EAPI Efreet_Desktop *efreet_util_desktop_generic_name_find(const char *generic_name);
+
+
+/**
+ * Find all desktops where name matches a glob pattern
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param glob the pattern to match
+ * @return a list of desktops
+ */
+EAPI Eina_List *efreet_util_desktop_name_glob_list(const char *glob);
+
+/**
+ * Find all desktops where exec matches a glob pattern
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param glob the pattern to match
+ * @return a list of desktops
+ */
+EAPI Eina_List *efreet_util_desktop_exec_glob_list(const char *glob);
+
+/**
+ * Find all desktops where generic name matches a glob pattern
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param glob the pattern to match
+ * @return a list of desktops
+ */
+EAPI Eina_List *efreet_util_desktop_generic_name_glob_list(const char *glob);
+
+/**
+ * Find all desktops where comment matches a glob pattern
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param glob the pattern to match
+ * @return a list of desktops
+ */
+EAPI Eina_List *efreet_util_desktop_comment_glob_list(const char *glob);
+
+
+/**
+ * Find all desktop categories
+ * This list must be freed using EINA_LIST_FREE
+ *
+ * @return an Eina_List of category names (const char *)
+ */
+EAPI Eina_List *efreet_util_desktop_categories_list(void);
+
+/**
+ * Find all desktops in a given category
+ *
+ * This list must be freed using EINA_LIST_FREE / efreet_desktop_free
+ *
+ * @param category the category name
+ * @return a list of desktops
+ */
+EAPI Eina_List *efreet_util_desktop_category_list(const char *category);
+
+
+/**
+ * Returns a list of .menu files found in the various config dirs.
+ * @return An eina list of menu file paths (const char *). This must be freed with EINA_LIST_FREE.
+ */
+EAPI Eina_List *efreet_util_menus_find(void);
+
+/**
+ * @}
+ */
+#endif
diff --git a/src/lib/efreet_xml.c b/src/lib/efreet_xml.c
new file mode 100644 (file)
index 0000000..9767d75
--- /dev/null
@@ -0,0 +1,609 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <Ecore_File.h>
+
+/* define macros and variable for using the eina logging system  */
+#define EFREET_MODULE_LOG_DOM _efreet_xml_log_dom
+static int _efreet_xml_log_dom = -1;
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_xml.h"
+
+#if 0
+static void efreet_xml_dump(Efreet_Xml *xml, int level);
+#endif
+
+static Efreet_Xml *efreet_xml_parse(char **data, int *size);
+static int efreet_xml_tag_parse(char **data, int *size, const char **tag);
+static void efreet_xml_attributes_parse(char **data, int *size,
+                                        Efreet_Xml_Attribute ***attributes);
+static void efreet_xml_text_parse(char **data, int *size, const char **text);
+
+static int efreet_xml_tag_empty(char **data, int *size);
+static int efreet_xml_tag_close(char **data, int *size, const char *tag);
+
+static void efreet_xml_cb_attribute_free(void *data);
+static void efreet_xml_comment_skip(char **data, int *size);
+
+static int error = 0;
+
+static int _efreet_xml_init_count = 0;
+
+/**
+ * @internal
+ * @return Returns > 0 on success or 0 on failure
+ * @brief Initialize the XML parser subsystem
+ */
+int
+efreet_xml_init(void)
+{
+    _efreet_xml_init_count++;
+    if (_efreet_xml_init_count > 1) return _efreet_xml_init_count;
+    _efreet_xml_log_dom = eina_log_domain_register
+      ("efreet_xml", EFREET_DEFAULT_LOG_COLOR);
+    if (_efreet_xml_log_dom < 0)
+    {
+        _efreet_xml_init_count--;
+        EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_xml.");
+        return _efreet_xml_init_count;
+    }
+    return _efreet_xml_init_count;
+}
+
+/**
+ * @internal
+ * @returns the number of initializations left for this system
+ * @brief Attempts to shut down the subsystem if nothing else is using it
+ */
+void
+efreet_xml_shutdown(void)
+{
+    _efreet_xml_init_count--;
+    if (_efreet_xml_init_count > 0) return;
+    eina_log_domain_unregister(_efreet_xml_log_dom);
+    _efreet_xml_log_dom = -1;
+}
+
+/**
+ * @internal
+ * @param file The file to parse
+ * @return Returns an Efreet_Xml structure for the given file @a file or
+ * NULL on failure
+ * @brief Parses the given file into an Efreet_Xml structure.
+ */
+Efreet_Xml *
+efreet_xml_new(const char *file)
+{
+    Efreet_Xml *xml = NULL;
+    int size, fd = -1;
+    char *data = MAP_FAILED;
+
+    if (!file) return NULL;
+    if (!ecore_file_exists(file)) return NULL;
+
+    size = ecore_file_size(file);
+    if (size <= 0) goto efreet_error;
+
+    fd = open(file, O_RDONLY);
+    if (fd == -1) goto efreet_error;
+
+    /* let's make mmap safe and just get 0 pages for IO erro */
+    eina_mmap_safety_enabled_set(EINA_TRUE);
+   
+    data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+    if (data == MAP_FAILED) goto efreet_error;
+
+    error = 0;
+    xml = efreet_xml_parse(&data, &size);
+    if (!xml || error) goto efreet_error;
+
+    munmap(data, size);
+    close(fd);
+    return xml;
+
+efreet_error:
+    ERR("could not parse xml file");
+    if (data != MAP_FAILED) munmap(data, size);
+    if (fd != -1) close(fd);
+    if (xml) efreet_xml_del(xml);
+    return NULL;
+}
+
+/**
+ * @internal
+ * @param xml The Efree_Xml to free
+ * @return Returns no value
+ * @brief Frees up the given Efreet_Xml structure
+ */
+void
+efreet_xml_del(Efreet_Xml *xml)
+{
+    IF_FREE_LIST(xml->children, efreet_xml_cb_attribute_free);
+
+    if (xml->tag) eina_stringshare_del(xml->tag);
+    if (xml->attributes)
+    {
+        Efreet_Xml_Attribute **curr;
+
+        curr = xml->attributes;
+        while (*curr)
+        {
+            eina_stringshare_del((*curr)->key);
+            eina_stringshare_del((*curr)->value);
+
+            FREE(*curr);
+            curr++;
+        }
+        FREE(xml->attributes);
+    }
+    IF_RELEASE(xml->text);
+    FREE(xml);
+}
+
+/**
+ * @param xml The xml struct to work with
+ * @param key The attribute key to look for
+ * @return Returns the value for the given key, or NULL if none found
+ * @brief Retrieves the value for the given attribute key
+ */
+const char *
+efreet_xml_attribute_get(Efreet_Xml *xml, const char *key)
+{
+    Efreet_Xml_Attribute **curr;
+
+    if (!xml || !key || !xml->attributes) return NULL;
+
+    for (curr = xml->attributes; *curr; curr++)
+    {
+        if (!strcmp((*curr)->key, key))
+            return (*curr)->value;
+    }
+    return NULL;
+}
+
+static void
+efreet_xml_cb_attribute_free(void *data)
+{
+    efreet_xml_del(data);
+}
+
+#if 0
+static void
+efreet_xml_dump(Efreet_Xml *xml, int level)
+{
+    int i;
+
+    for (i = 0; i < level; i++)
+        printf("\t");
+    printf("<%s", xml->tag);
+    if (xml->attributes)
+    {
+        Efreet_Xml_Attribute **curr;
+        for (curr = xml->attributes; *curr; curr++)
+            printf(" %s=\"%s\"", (*curr)->key, (*curr)->value);
+    }
+
+    if (xml->children)
+    {
+        Efreet_Xml *child;
+        Eina_List *l;
+
+        printf(">");
+
+        EINA_LIST_FOREACH(xml->children, l, child)
+            efreet_xml_dump(child, level + 1);
+
+        for (i = 0; i < level; i++)
+            printf("\t");
+        printf("</%s>", xml->tag);
+    }
+    else if (xml->text)
+        printf(">%s</%s>\n", xml->text, xml->tag);
+    else
+        printf("/>\n");
+}
+#endif
+
+static Efreet_Xml *
+efreet_xml_parse(char **data, int *size)
+{
+    Efreet_Xml *xml, *sub_xml;
+    const char *tag = NULL;
+
+    /* parse this tag */
+    if (!efreet_xml_tag_parse(data, size, &(tag))) return NULL;
+    xml = NEW(Efreet_Xml, 1);
+    if (!xml)
+    {
+        eina_stringshare_del(tag);
+        return NULL;
+    }
+
+    xml->children = NULL;
+
+    xml->tag = tag;
+    efreet_xml_attributes_parse(data, size, &(xml->attributes));
+
+    /* Check wether element is empty */
+    if (efreet_xml_tag_empty(data, size)) return xml;
+    efreet_xml_text_parse(data, size, &(xml->text));
+
+    /* Check wether element is closed */
+    if (efreet_xml_tag_close(data, size, xml->tag)) return xml;
+
+    while ((sub_xml = efreet_xml_parse(data, size)))
+        xml->children = eina_list_append(xml->children, sub_xml);
+
+    efreet_xml_tag_close(data, size, xml->tag);
+
+    return xml;
+}
+
+static int
+efreet_xml_tag_parse(char **data, int *size, const char **tag)
+{
+    const char *start = NULL, *end = NULL;
+    char buf[256];
+    int buf_size;
+
+    /* Search for tag */
+    while (*size > 1)
+    {
+        /* Check for tag start */
+        if (**data == '<')
+        {
+            /* Check for end tag */
+            if (*(*data + 1) == '/') return 0;
+
+            /* skip comments */
+            if (*size > 3 && *(*data + 1) == '!' && *(*data + 2) == '-' && *(*data + 3) == '-')
+            {
+                (*data) += 3;
+                (*size) -= 3;
+                efreet_xml_comment_skip(data, size);
+                continue;
+            }
+
+            /* Check for xml directives (and ignore them) */
+            else if ((*(*data + 1) != '!') && (*(*data + 1) != '?'))
+            {
+                (*size)--;
+                (*data)++;
+                start = *data;
+                break;
+            }
+        }
+        (*size)--;
+        (*data)++;
+    }
+
+    if (!start)
+    {
+        ERR("missing start tag");
+        error = 1;
+        return 0;
+    }
+
+    while (*size > 0)
+    {
+        if (!isalpha(**data))
+        {
+            end = *data;
+            break;
+        }
+        (*size)--;
+        (*data)++;
+    }
+
+    if (!end)
+    {
+        ERR("no end of tag");
+        error = 1;
+        return 0;
+    }
+
+    buf_size = end - start + 1;
+    if (buf_size <= 1)
+    {
+        ERR("no tag name");
+        error = 1;
+        return 0;
+    }
+
+    if (buf_size > 256) buf_size = 256;
+    memcpy(buf, start, buf_size - 1);
+    buf[buf_size - 1] = '\0';
+    *tag = eina_stringshare_add(buf);
+
+    return 1;
+}
+
+static void
+efreet_xml_attributes_parse(char **data, int *size,
+        Efreet_Xml_Attribute ***attributes)
+{
+    Efreet_Xml_Attribute attr[10];
+    int i, count = 0;
+
+    while (*size > 0)
+    {
+        if (**data == '>')
+        {
+            (*size)++;
+            (*data)--;
+            break;
+        }
+        else if ((count < 10) && (isalpha(**data)))
+        {
+            /* beginning of key */
+            const char *start = NULL, *end = NULL;
+            char buf[256];
+            int buf_size;
+
+            attr[count].key = NULL;
+            attr[count].value = NULL;
+
+            start = *data;
+            while ((*size > 0) && ((isalpha(**data)) || (**data == '_')))
+            {
+                (*size)--;
+                (*data)++;
+            }
+
+            end = *data;
+            buf_size = end - start + 1;
+            if (buf_size <= 1)
+            {
+                ERR("zero length key");
+                goto efreet_error;
+            }
+
+            if (buf_size > 256) buf_size = 256;
+            memcpy(buf, start, buf_size - 1);
+            buf[buf_size - 1] = '\0';
+            attr[count].key = eina_stringshare_add(buf);
+
+            /* search for '=', key/value seperator */
+            start = NULL;
+            while (*size > 0)
+            {
+                if (**data == '=')
+                {
+                    start = *data;
+                    break;
+                }
+                (*size)--;
+                (*data)++;
+            }
+
+            if (!start)
+            {
+                ERR("missing value for attribute!");
+                goto efreet_error;
+            }
+
+            /* search for '"', beginning of value */
+            start = NULL;
+            while (*size > 0)
+            {
+                if (**data == '"')
+                {
+                    start = *data;
+                    break;
+                }
+                (*size)--;
+                (*data)++;
+            }
+
+            if (!start)
+            {
+                ERR("erroneous value for attribute!");
+                goto efreet_error;
+            }
+
+            /* skip '"' */
+            start++;
+            (*size)--;
+            (*data)++;
+
+            /* search for '"', end of value */
+            end = NULL;
+            while (*size > 0)
+            {
+                if (**data == '"')
+                {
+                    end = *data;
+                    break;
+                }
+                (*size)--;
+                (*data)++;
+            }
+
+            if (!end)
+            {
+                ERR("erroneous value for attribute!");
+                goto efreet_error;
+            }
+
+            buf_size = end - start + 1;
+            if (buf_size <= 1)
+            {
+                ERR("zero length value");
+                goto efreet_error;
+            }
+
+            if (buf_size > 256) buf_size = 256;
+            memcpy(buf, start, buf_size - 1);
+            buf[buf_size - 1] = '\0';
+            attr[count].value = eina_stringshare_add(buf);
+
+            count++;
+        }
+
+        (*size)--;
+        (*data)++;
+    }
+
+    *attributes = NEW(Efreet_Xml_Attribute *, count + 1);
+    if (!*attributes) goto efreet_error;
+    for (i = 0; i < count; i++)
+    {
+        (*attributes)[i] = malloc(sizeof(Efreet_Xml_Attribute));
+        (*attributes)[i]->key = attr[i].key;
+        (*attributes)[i]->value = attr[i].value;
+    }
+    return;
+
+efreet_error:
+    while (count >= 0)
+    {
+        if (attr[count].key) eina_stringshare_del(attr[count].key);
+        if (attr[count].value) eina_stringshare_del(attr[count].value);
+        count--;
+    }
+    error = 1;
+    return;
+}
+
+static void
+efreet_xml_text_parse(char **data, int *size, const char **text)
+{
+    const char *start = NULL, *end = NULL;
+    int buf_size;
+
+    /* skip leading whitespace */
+    while (*size > 0)
+    {
+        if (!isspace(**data))
+        {
+            start = *data;
+            break;
+        }
+        (*size)--;
+        (*data)++;
+    }
+
+    if (!start) return;
+
+    /* find next tag */
+    while (*size > 0)
+    {
+        if (**data == '<')
+        {
+            end = *data;
+            break;
+        }
+        (*size)--;
+        (*data)++;
+    }
+    if (!end) return;
+
+    /* skip trailing whitespace */
+    while (isspace(*(end - 1))) end--;
+
+    /* copy text */
+    buf_size = end - start + 1;
+    if (buf_size <= 1) return;
+
+    *text = eina_stringshare_add_length(start, buf_size - 1);
+}
+
+static int
+efreet_xml_tag_empty(char **data, int *size)
+{
+    while (*size > 1)
+    {
+        if (**data == '/')
+        {
+            (*size)--;
+            (*data)++;
+            if (**data == '>')
+            {
+                (*size)--;
+                (*data)++;
+                return 1;
+            }
+        }
+        else if (**data == '>')
+        {
+            (*size)--;
+            (*data)++;
+            return 0;
+        }
+        (*size)--;
+        (*data)++;
+    }
+    ERR("missing end of tag");
+    error = 1;
+
+    return 1;
+}
+
+static int
+efreet_xml_tag_close(char **data, int *size, const char *tag)
+{
+    while (*size > 1)
+    {
+        if (**data == '<')
+        {
+            if (*(*data + 1) == '/')
+            {
+                (*size) -= 2;
+                (*data) += 2;
+                if ((int)strlen(tag) > *size)
+                {
+                    ERR("wrong end tag");
+                    error = 1;
+                    return 1;
+                }
+                else
+                {
+                    char *tmp;
+                    tmp = *data;
+                    while ((*tag) && (*tmp == *tag))
+                    {
+                        tmp++;
+                        tag++;
+                    }
+
+                    if (*tag)
+                    {
+                        ERR("wrong end tag");
+                        error = 1;
+                        return 1;
+                    }
+                }
+                return 1;
+            }
+            else return 0;
+        }
+        (*size)--;
+        (*data)++;
+    }
+    return 0;
+}
+
+static void
+efreet_xml_comment_skip(char **data, int *size)
+{
+    while (*size > 2)
+    {
+        if (**data == '-' && *(*data + 1) == '-' && *(*data + 2) == '>')
+        {
+            (*data) += 3;
+            (*size) -= 3;
+            return;
+        }
+        (*data)++;
+        (*size)--;
+    }
+}
diff --git a/src/lib/efreet_xml.h b/src/lib/efreet_xml.h
new file mode 100644 (file)
index 0000000..77473cc
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef EFREET_XML_H
+#define EFREET_XML_H
+
+/**
+ * @internal
+ * @file efreet_xml.h
+ * @brief A simple and fast XML parser
+ * @addtogroup Efreet_Xml Efreet_Xml: An XML parser
+ *
+ * @{
+ */
+
+/**
+ * Efreet_Xml_Attributes
+ */
+typedef struct Efreet_Xml_Attribute Efreet_Xml_Attribute;
+
+/**
+ * Efreet_Xml_Attributes
+ * @brief Contains information about a given XML attribute
+ */
+struct Efreet_Xml_Attribute
+{
+    const char *key;        /**< The attribute key */
+    const char *value;      /**< The attribute value */
+};
+
+/**
+ * Efreet_Xml
+ */
+typedef struct Efreet_Xml Efreet_Xml;
+
+/**
+ * Efreet_Xml
+ * @brief Contains the XML tree for a given XML document
+ */
+struct Efreet_Xml
+{
+    const char *text;                   /**< The XML text for this node */
+    const char *tag;                    /**< The tag for this node */
+
+    Efreet_Xml_Attribute **attributes;  /**< The attributes for this node */
+
+    Eina_List *children;               /**< Child nodes */
+};
+
+int efreet_xml_init(void);
+void efreet_xml_shutdown(void);
+
+Efreet_Xml *efreet_xml_new(const char *file);
+void efreet_xml_del(Efreet_Xml *xml);
+
+const char *efreet_xml_attribute_get(Efreet_Xml *xml, const char *key);
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
new file mode 100644 (file)
index 0000000..13c1274
--- /dev/null
@@ -0,0 +1,66 @@
+
+SUBDIRS = data compare
+
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_srcdir)/src/lib \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)\" \
+-DPKG_DATA_DIR=\"$(pkgdatadir)\" \
+@EFREET_CFLAGS@
+
+bin_PROGRAMS = \
+efreet_test \
+efreet_spec_test \
+efreet_cache_test \
+efreet_icon_cache_dump
+
+efreet_test_LDADD = $(top_builddir)/src/lib/libefreet.la \
+                    $(top_builddir)/src/lib/libefreet_mime.la \
+                    @EFREET_LIBS@
+efreet_test_SOURCES = \
+ef_test.h \
+ef_data_dirs.c \
+ef_icon_theme.c \
+ef_ini.c \
+ef_utils.c \
+ef_desktop.c \
+ef_menu.c \
+ef_mime.c \
+main.c
+
+if DEFAULT_VISIBILITY
+efreet_test_SOURCES += \
+ef_locale.c
+endif
+
+efreet_spec_test_LDADD = $(top_builddir)/src/lib/libefreet.la @EFREET_LIBS@
+efreet_spec_test_SOURCES = \
+efreet_spec_test.c
+
+efreet_cache_test_LDADD = $(top_builddir)/src/lib/libefreet.la @EFREET_LIBS@
+efreet_cache_test_SOURCES = \
+ef_cache.c
+
+if EFL_ENABLE_TESTS
+
+check_PROGRAMS = efreet_suite
+
+efreet_suite_SOURCES = \
+efreet_suite.c \
+efreet_test_efreet.c \
+efreet_test_efreet_cache.c
+
+efreet_suite_LDADD = @CHECK_LIBS@ $(top_builddir)/src/lib/libefreet.la @EFREET_LIBS@
+
+endif
+
+efreet_icon_cache_dump_LDADD = \
+$(top_builddir)/src/lib/libefreet.la \
+@EFREET_LIBS@
+
+efreet_icon_cache_dump_SOURCES = \
+efreet_icon_cache_dump.c
diff --git a/src/tests/compare/Makefile.am b/src/tests/compare/Makefile.am
new file mode 100644 (file)
index 0000000..bb43591
--- /dev/null
@@ -0,0 +1,15 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_srcdir)/src/lib \
+@EFREET_CFLAGS@
+
+bin_PROGRAMS = efreet_alloc efreet_menu_alloc
+
+efreet_menu_alloc_SOURCES = efreet_menu_alloc.c comp.h
+efreet_menu_alloc_LDADD = $(top_builddir)/src/lib/libefreet.la @EFREET_LIBS@
+
+efreet_alloc_SOURCES = efreet_alloc.c comp.h
+efreet_alloc_LDADD = $(top_builddir)/src/lib/libefreet.la @EFREET_LIBS@
+
diff --git a/src/tests/compare/comp.h b/src/tests/compare/comp.h
new file mode 100644 (file)
index 0000000..c02eebe
--- /dev/null
@@ -0,0 +1,527 @@
+#ifndef COMP_H
+#define COMP_H
+
+#define LOOPS 1000
+#define THEME "Tango"
+#define SIZE 16
+
+#define ADDRESS_BOOK_NEW "address-book-new"
+#define APPLICATION_EXIT "application-exit"
+#define APPOINTMENT_NEW "appointment-new"
+#define CONTACT_NEW "contact-new"
+#define DIALOG_APPLY "dialog-apply"
+#define DIALOG_CANCEL "dialog-cancel"
+#define DIALOG_CLOSE "dialog-close"
+#define DIALOG_OK "dialog-ok"
+#define DOCUMENT_NEW "document-new"
+#define DOCUMENT_OPEN "document-open"
+#define DOCUMENT_OPEN_RECENT "document-open-recent"
+#define DOCUMENT_PAGE_SETUP "document-page-setup"
+#define DOCUMENT_PRINT "document-print"
+#define DOCUMENT_PRINT_PREVIEW "document-print-preview"
+#define DOCUMENT_PROPERTIES "document-properties"
+#define DOCUMENT_REVERT "document-revert"
+#define DOCUMENT_SAVE "document-save"
+#define DOCUMENT_SAVE_AS "document-save-as"
+#define EDIT_COPY "edit-copy"
+#define EDIT_CUT "edit-cut"
+#define EDIT_DELETE "edit-delete"
+#define EDIT_FIND "edit-find"
+#define EDIT_FIND_REPLACE "edit-find-replace"
+#define EDIT_PASTE "edit-paste"
+#define EDIT_REDO "edit-redo"
+#define EDIT_SELECT_ALL "edit-select-all"
+#define EDIT_UNDO "edit-undo"
+#define FORMAT_INDENT_LESS "format-indent-less"
+#define FORMAT_INDENT_MORE "format-indent-more"
+#define FORMAT_JUSTIFY_CENTER "format-justify-center"
+#define FORMAT_JUSTIFY_FILL "format-justify-fill"
+#define FORMAT_JUSTIFY_LEFT "format-justify-left"
+#define FORMAT_JUSTIFY_RIGHT "format-justify-right"
+#define FORMAT_TEXT_DIRECTION_LTR "format-text-direction-ltr"
+#define FORMAT_TEXT_DIRECTION_RTL "format-text-direction-rtl"
+#define FORMAT_TEXT_BOLD "format-text-bold"
+#define FORMAT_TEXT_ITALIC "format-text-italic"
+#define FORMAT_TEXT_UNDERLINE "format-text-underline"
+#define FORMAT_TEXT_STRIKETHROUGH "format-text-strikethrough"
+#define GO_BOTTOM "go-bottom"
+#define GO_DOWN "go-down"
+#define GO_FIRST "go-first"
+#define GO_HOME "go-home"
+#define GO_JUMP "go-jump"
+#define GO_LAST "go-last"
+#define GO_NEXT "go-next"
+#define GO_PREVIOUS "go-previous"
+#define GO_TOP "go-top"
+#define GO_UP "go-up"
+#define HELP_ABOUT "help-about"
+#define HELP_CONTENTS "help-contents"
+#define HELP_FAQ "help-faq"
+#define INSERT_IMAGE "insert-image"
+#define INSERT_LINK "insert-link"
+#define INSERT_OBJECT "insert-object"
+#define INSERT_TEXT "insert-text"
+#define LIST_ADD "list-add"
+#define LIST_REMOVE "list-remove"
+#define MAIL_FORWARD "mail-forward"
+#define MAIL_MARK_IMPORTANT "mail-mark-important"
+#define MAIL_MARK_JUNK "mail-mark-junk"
+#define MAIL_MARK_NOTJUNK "mail-mark-notjunk"
+#define MAIL_MARK_READ "mail-mark-read"
+#define MAIL_MARK_UNREAD "mail-mark-unread"
+#define MAIL_MESSAGE_NEW "mail-message-new"
+#define MAIL_REPLY_ALL "mail-reply-all"
+#define MAIL_REPLY_SENDER "mail-reply-sender"
+#define MAIL_SEND_RECEIVE "mail-send-receive"
+#define MEDIA_EJECT "media-eject"
+#define MEDIA_PLAYBACK_PAUSE "media-playback-pause"
+#define MEDIA_PLAYBACK_START "media-playback-start"
+#define MEDIA_PLAYBACK_STOP "media-playback-stop"
+#define MEDIA_RECORD "media-record"
+#define MEDIA_SEEK_BACKWARD "media-seek-backward"
+#define MEDIA_SEEK_FORWARD "media-seek-forward"
+#define MEDIA_SKIP_BACKWARD "media-skip-backward"
+#define MEDIA_SKIP_FORWARD "media-skip-forward"
+#define SYSTEM_LOCK_SCREEN "system-lock-screen"
+#define SYSTEM_LOG_OUT "system-log-out"
+#define SYSTEM_RUN "system-run"
+#define SYSTEM_SEARCH "system-search"
+#define TOOLS_CHECK_SPELLING "tools-check-spelling"
+#define VIEW_FULLSCREEN "view-fullscreen"
+#define VIEW_REFRESH "view-refresh"
+#define VIEW_SORT_ASCENDING "view-sort-ascending"
+#define VIEW_SORT_DESCENDING "view-sort-descending"
+#define WINDOW_CLOSE "window-close"
+#define WINDOW_NEW "window-new"
+#define ZOOM_BEST_FIT "zoom-best-fit"
+#define ZOOM_IN "zoom-in"
+#define ZOOM_ORIGINAL "zoom-original"
+#define ZOOM_OUT "zoom-out"
+
+#define PROCESS_WORKING "process-working"
+
+#define ACCESSORIES_CALCULATOR "accessories-calculator"
+#define ACCESSORIES_CHARACTER_MAP "accessories-character-map"
+#define ACCESSORIES_DICTIONARY "accessories-dictionary"
+#define ACCESSORIES_TEXT_EDITOR "accessories-text-editor"
+#define HELP_BROWSER "help-browser"
+#define MULTIMEDIA_VOLUME_CONTROL "multimedia-volume-control"
+#define PREFERENCES_DESKTOP_ACCESSIBILITY "preferences-desktop-accessibility"
+#define PREFERENCES_DESKTOP_FONT "preferences-desktop-font"
+#define PREFERENCES_DESKTOP_KEYBOARD "preferences-desktop-keyboard"
+#define PREFERENCES_DESKTOP_LOCALE "preferences-desktop-locale"
+#define PREFERENCES_DESKTOP_MULTIMEDIA "preferences-desktop-multimedia"
+#define PREFERENCES_DESKTOP_SCREENSAVER "preferences-desktop-screensaver"
+#define PREFERENCES_DESKTOP_THEME "preferences-desktop-theme"
+#define PREFERENCES_DESKTOP_WALLPAPER "preferences-desktop-wallpaper"
+#define SYSTEM_FILE_MANAGER "system-file-manager"
+#define SYSTEM_SOFTWARE_UPDATE "system-software-update"
+#define UTILITIES_TERMINAL "utilities-terminal"
+
+#define APPLICATIONS_ACCESSORIES "applications-accessories"
+#define APPLICATIONS_DEVELOPMENT "applications-development"
+#define APPLICATIONS_GAMES "applications-games"
+#define APPLICATIONS_GRAPHICS "applications-graphics"
+#define APPLICATIONS_INTERNET "applications-internet"
+#define APPLICATIONS_MULTIMEDIA "applications-multimedia"
+#define APPLICATIONS_OFFICE "applications-office"
+#define APPLICATIONS_OTHER "applications-other"
+#define APPLICATIONS_SYSTEM "applications-system"
+#define APPLICATIONS_UTILITIES "applications-utilities"
+#define PREFERENCES_DESKTOP "preferences-desktop"
+#define PREFERENCES_DESKTOP_ACCESSIBILITY "preferences-desktop-accessibility"
+#define PREFERENCES_DESKTOP_PERIPHERALS "preferences-desktop-peripherals"
+#define PREFERENCES_DESKTOP_PERSONAL "preferences-desktop-personal"
+#define PREFERENCES_OTHER "preferences-other"
+#define PREFERENCES_SYSTEM "preferences-system"
+#define PREFERENCES_SYSTEM_NETWORK "preferences-system-network"
+#define SYSTEM_HELP "system-help"
+
+#define AUDIO_CARD "audio-card"
+#define AUDIO_INPUT_MICROPHONE "audio-input-microphone"
+#define BATTERY "battery"
+#define CAMERA_PHOTO "camera-photo"
+#define CAMERA_VIDEO "camera-video"
+#define COMPUTER "computer"
+#define DRIVE_CDROM "drive-cdrom"
+#define DRIVE_HARDDISK "drive-harddisk"
+#define DRIVE_REMOVABLE_MEDIA "drive-removable-media"
+#define INPUT_GAMING "input-gaming"
+#define INPUT_KEYBOARD "input-keyboard"
+#define INPUT_MOUSE "input-mouse"
+#define MEDIA_CDROM "media-cdrom"
+#define MEDIA_FLOPPY "media-floppy"
+#define MULTIMEDIA_PLAYER "multimedia-player"
+#define NETWORK_WIRED "network-wired"
+#define NETWORK_WIRELESS "network-wireless"
+#define PRINTER "printer"
+
+#define EMBLEM_DEFAULT "emblem-default"
+#define EMBLEM_DOCUMENTS "emblem-documents"
+#define EMBLEM_DOWNLOADS "emblem-downloads"
+#define EMBLEM_FAVORITE "emblem-favorite"
+#define EMBLEM_IMPORTANT "emblem-important"
+#define EMBLEM_MAIL "emblem-mail"
+#define EMBLEM_PHOTOS "emblem-photos"
+#define EMBLEM_READONLY "emblem-readonly"
+#define EMBLEM_SHARED "emblem-shared"
+#define EMBLEM_SYMBOLIC_LINK "emblem-symbolic-link"
+#define EMBLEM_SYNCHRONIZED "emblem-synchronized"
+#define EMBLEM_SYSTEM "emblem-system"
+#define EMBLEM_UNREADABLE "emblem-unreadable"
+
+#define FACE_ANGEL "face-angel"
+#define FACE_CRYING "face-crying"
+#define FACE_DEVIL_GRIN "face-devil-grin"
+#define FACE_DEVIL_SAD "face-devil-sad"
+#define FACE_GLASSES "face-glasses"
+#define FACE_KISS "face-kiss"
+#define FACE_MONKEY "face-monkey"
+#define FACE_PLAIN "face-plain"
+#define FACE_SAD "face-sad"
+#define FACE_SMILE "face-smile"
+#define FACE_SMILE_BIG "face-smile-big"
+#define FACE_SMIRK "face-smirk"
+#define FACE_SURPRISE "face-surprise"
+#define FACE_WINK "face-wink"
+
+#define APPLICATION_X_EXECUTABLE "application-x-executable"
+#define AUDIO_X_GENERIC "audio-x-generic"
+#define FONT_X_GENERIC "font-x-generic"
+#define IMAGE_X_GENERIC "image-x-generic"
+#define PACKAGE_X_GENERIC "package-x-generic"
+#define TEXT_HTML "text-html"
+#define TEXT_X_GENERIC "text-x-generic"
+#define TEXT_X_GENERIC_TEMPLATE "text-x-generic-template"
+#define TEXT_X_SCRIPT "text-x-script"
+#define VIDEO_X_GENERIC "video-x-generic"
+#define X_OFFICE_ADDRESS_BOOK "x-office-address-book"
+#define X_OFFICE_CALENDAR "x-office-calendar"
+#define X_OFFICE_DOCUMENT "x-office-document"
+#define X_OFFICE_PRESENTATION "x-office-presentation"
+#define X_OFFICE_SPREADSHEET "x-office-spreadsheet"
+
+#define FOLDER "folder"
+#define FOLDER_REMOTE "folder-remote"
+#define NETWORK_SERVER "network-server"
+#define NETWORK_WORKGROUP "network-workgroup"
+#define START_HERE "start-here"
+#define USER_DESKTOP "user-desktop"
+#define USER_HOME "user-home"
+#define USER_TRASH "user-trash"
+
+#define APPOINTMENT_MISSED "appointment-missed"
+#define APPOINTMENT_SOON "appointment-soon"
+#define AUDIO_VOLUME_HIGH "audio-volume-high"
+#define AUDIO_VOLUME_LOW "audio-volume-low"
+#define AUDIO_VOLUME_MEDIUM "audio-volume-medium"
+#define AUDIO_VOLUME_MUTED "audio-volume-muted"
+#define BATTERY_CAUTION "battery-caution"
+#define BATTERY_LOW "battery-low"
+#define DIALOG_ERROR "dialog-error"
+#define DIALOG_INFORMATION "dialog-information"
+#define DIALOG_PASSWORD "dialog-password"
+#define DIALOG_QUESTION "dialog-question"
+#define DIALOG_WARNING "dialog-warning"
+#define FOLDER_DRAG_ACCEPT "folder-drag-accept"
+#define FOLDER_OPEN "folder-open"
+#define FOLDER_VISITING "folder-visiting"
+#define IMAGE_LOADING "image-loading"
+#define IMAGE_MISSING "image-missing"
+#define MAIL_ATTACHMENT "mail-attachment"
+#define MAIL_UNREAD "mail-unread"
+#define MAIL_READ "mail-read"
+#define MAIL_REPLIED "mail-replied"
+#define MAIL_SIGNED "mail-signed"
+#define MAIL_SIGNED_VERIFIED "mail-signed-verified"
+#define MEDIA_PLAYLIST_REPEAT "media-playlist-repeat"
+#define MEDIA_PLAYLIST_SHUFFLE "media-playlist-shuffle"
+#define NETWORK_ERROR "network-error"
+#define NETWORK_IDLE "network-idle"
+
+#define NETWORK_OFFLINE "network-offline"
+#define NETWORK_RECEIVE "network-receive"
+#define NETWORK_TRANSMIT "network-transmit"
+#define NETWORK_TRANSMIT_RECEIVE "network-transmit-receive"
+#define PRINTER_ERROR "printer-error"
+#define PRINTER_PRINTING "printer-printing"
+#define SOFTWARE_UPDATE_AVAILABLE "software-update-available"
+#define SOFTWARE_UPDATE_URGENT "software-update-urgent"
+#define SYNC_ERROR "sync-error"
+#define SYNC_SYNCHRONIZING "sync-synchronizing"
+#define TASK_DUE "task-due"
+#define TASK_PASSED_DUE "task-passed-due"
+#define USER_AWAY "user-away"
+#define USER_IDLE "user-idle"
+#define USER_OFFLINE "user-offline"
+#define USER_ONLINE "user-online"
+#define USER_TRASH_FULL "user-trash-full"
+#define WEATHER_CLEAR "weather-clear"
+#define WEATHER_CLEAR_NIGHT "weather-clear-night"
+#define WEATHER_FEW_CLOUDS "weather-few-clouds"
+#define WEATHER_FEW_CLOUDS_NIGHT "weather-few-clouds-night"
+#define WEATHER_FOG "weather-fog"
+#define WEATHER_OVERCAST "weather-overcast"
+#define WEATHER_SEVERE_ALERT "weather-severe-alert"
+#define WEATHER_SHOWERS "weather-showers"
+#define WEATHER_SHOWERS_SCATTERED "weather-showers-scattered"
+#define WEATHER_SNOW "weather-snow"
+#define WEATHER_STORM "weather-storm"
+
+const char *icons[] = {
+                       ADDRESS_BOOK_NEW,
+                       APPLICATION_EXIT,
+                       APPOINTMENT_NEW,
+                       CONTACT_NEW,
+                       DIALOG_APPLY,
+                       DIALOG_CANCEL,
+                       DIALOG_CLOSE,
+                       DIALOG_OK,
+                       DOCUMENT_NEW,
+                       DOCUMENT_OPEN,
+                       DOCUMENT_OPEN_RECENT,
+                       DOCUMENT_PAGE_SETUP,
+                       DOCUMENT_PRINT,
+                       DOCUMENT_PRINT_PREVIEW,
+                       DOCUMENT_PROPERTIES,
+                       DOCUMENT_REVERT,
+                       DOCUMENT_SAVE,
+                       DOCUMENT_SAVE_AS,
+                       EDIT_COPY,
+                       EDIT_CUT,
+                       EDIT_DELETE,
+                       EDIT_FIND,
+                       EDIT_FIND_REPLACE,
+                       EDIT_PASTE,
+                       EDIT_REDO,
+                       EDIT_SELECT_ALL,
+                       EDIT_UNDO,
+                       FORMAT_INDENT_LESS,
+                       FORMAT_INDENT_MORE,
+                       FORMAT_JUSTIFY_CENTER,
+                       FORMAT_JUSTIFY_FILL,
+                       FORMAT_JUSTIFY_LEFT,
+                       FORMAT_JUSTIFY_RIGHT,
+                       FORMAT_TEXT_DIRECTION_LTR,
+                       FORMAT_TEXT_DIRECTION_RTL,
+                       FORMAT_TEXT_BOLD,
+                       FORMAT_TEXT_ITALIC,
+                       FORMAT_TEXT_UNDERLINE,
+                       FORMAT_TEXT_STRIKETHROUGH,
+                       GO_BOTTOM,
+                       GO_DOWN,
+                       GO_FIRST,
+                       GO_HOME,
+                       GO_JUMP,
+                       GO_LAST,
+                       GO_NEXT,
+                       GO_PREVIOUS,
+                       GO_TOP,
+                       GO_UP,
+                       HELP_ABOUT,
+                       HELP_CONTENTS,
+                       HELP_FAQ,
+                       INSERT_IMAGE,
+                       INSERT_LINK,
+                       INSERT_OBJECT,
+                       INSERT_TEXT,
+                       LIST_ADD,
+                       LIST_REMOVE,
+                       MAIL_FORWARD,
+                       MAIL_MARK_IMPORTANT,
+                       MAIL_MARK_JUNK,
+                       MAIL_MARK_NOTJUNK,
+                       MAIL_MARK_READ,
+                       MAIL_MARK_UNREAD,
+                       MAIL_MESSAGE_NEW,
+                       MAIL_REPLY_ALL,
+                       MAIL_REPLY_SENDER,
+                       MAIL_SEND_RECEIVE,
+                       MEDIA_EJECT,
+                       MEDIA_PLAYBACK_PAUSE,
+                       MEDIA_PLAYBACK_START,
+                       MEDIA_PLAYBACK_STOP,
+                       MEDIA_RECORD,
+                       MEDIA_SEEK_BACKWARD,
+                       MEDIA_SEEK_FORWARD,
+                       MEDIA_SKIP_BACKWARD,
+                       MEDIA_SKIP_FORWARD,
+                       SYSTEM_LOCK_SCREEN,
+                       SYSTEM_LOG_OUT,
+                       SYSTEM_RUN,
+                       SYSTEM_SEARCH,
+                       TOOLS_CHECK_SPELLING,
+                       VIEW_FULLSCREEN,
+                       VIEW_REFRESH,
+                       VIEW_SORT_ASCENDING,
+                       VIEW_SORT_DESCENDING,
+                       WINDOW_CLOSE,
+                       WINDOW_NEW,
+                       ZOOM_BEST_FIT,
+                       ZOOM_IN,
+                       ZOOM_ORIGINAL,
+                       ZOOM_OUT,
+                       PROCESS_WORKING,
+                       ACCESSORIES_CALCULATOR,
+                       ACCESSORIES_CHARACTER_MAP,
+                       ACCESSORIES_DICTIONARY,
+                       ACCESSORIES_TEXT_EDITOR,
+                       HELP_BROWSER,
+                       MULTIMEDIA_VOLUME_CONTROL,
+                       PREFERENCES_DESKTOP_ACCESSIBILITY,
+                       PREFERENCES_DESKTOP_FONT,
+                       PREFERENCES_DESKTOP_KEYBOARD,
+                       PREFERENCES_DESKTOP_LOCALE,
+                       PREFERENCES_DESKTOP_MULTIMEDIA,
+                       PREFERENCES_DESKTOP_SCREENSAVER,
+                       PREFERENCES_DESKTOP_THEME,
+                       PREFERENCES_DESKTOP_WALLPAPER,
+                       SYSTEM_FILE_MANAGER,
+                       SYSTEM_SOFTWARE_UPDATE,
+                       UTILITIES_TERMINAL,
+                       APPLICATIONS_ACCESSORIES,
+                       APPLICATIONS_DEVELOPMENT,
+                       APPLICATIONS_GAMES,
+                       APPLICATIONS_GRAPHICS,
+                       APPLICATIONS_INTERNET,
+                       APPLICATIONS_MULTIMEDIA,
+                       APPLICATIONS_OFFICE,
+                       APPLICATIONS_OTHER,
+                       APPLICATIONS_SYSTEM,
+                       APPLICATIONS_UTILITIES,
+                       PREFERENCES_DESKTOP,
+                       PREFERENCES_DESKTOP_ACCESSIBILITY,
+                       PREFERENCES_DESKTOP_PERIPHERALS,
+                       PREFERENCES_DESKTOP_PERSONAL,
+                       PREFERENCES_OTHER,
+                       PREFERENCES_SYSTEM,
+                       PREFERENCES_SYSTEM_NETWORK,
+                       SYSTEM_HELP,
+                       AUDIO_CARD,
+                       AUDIO_INPUT_MICROPHONE,
+                       BATTERY,
+                       CAMERA_PHOTO,
+                       CAMERA_VIDEO,
+                       COMPUTER,
+                       DRIVE_CDROM,
+                       DRIVE_HARDDISK,
+                       DRIVE_REMOVABLE_MEDIA,
+                       INPUT_GAMING,
+                       INPUT_KEYBOARD,
+                       INPUT_MOUSE,
+                       MEDIA_CDROM,
+                       MEDIA_FLOPPY,
+                       MULTIMEDIA_PLAYER,
+                       NETWORK_WIRED,
+                       NETWORK_WIRELESS,
+                       PRINTER,
+                       EMBLEM_DEFAULT,
+                       EMBLEM_DOCUMENTS,
+                       EMBLEM_DOWNLOADS,
+                       EMBLEM_FAVORITE,
+                       EMBLEM_IMPORTANT,
+                       EMBLEM_MAIL,
+                       EMBLEM_PHOTOS,
+                       EMBLEM_READONLY,
+                       EMBLEM_SHARED,
+                       EMBLEM_SYMBOLIC_LINK,
+                       EMBLEM_SYNCHRONIZED,
+                       EMBLEM_SYSTEM,
+                       EMBLEM_UNREADABLE,
+                       FACE_ANGEL,
+                       FACE_CRYING,
+                       FACE_DEVIL_GRIN,
+                       FACE_DEVIL_SAD,
+                       FACE_GLASSES,
+                       FACE_KISS,
+                       FACE_MONKEY,
+                       FACE_PLAIN,
+                       FACE_SAD,
+                       FACE_SMILE,
+                       FACE_SMILE_BIG,
+                       FACE_SMIRK,
+                       FACE_SURPRISE,
+                       FACE_WINK,
+                       APPLICATION_X_EXECUTABLE,
+                       AUDIO_X_GENERIC,
+                       FONT_X_GENERIC,
+                       IMAGE_X_GENERIC,
+                       PACKAGE_X_GENERIC,
+                       TEXT_HTML,
+                       TEXT_X_GENERIC,
+                       TEXT_X_GENERIC_TEMPLATE,
+                       TEXT_X_SCRIPT,
+                       VIDEO_X_GENERIC,
+                       X_OFFICE_ADDRESS_BOOK,
+                       X_OFFICE_CALENDAR,
+                       X_OFFICE_DOCUMENT,
+                       X_OFFICE_PRESENTATION,
+                       X_OFFICE_SPREADSHEET,
+                       FOLDER,
+                       FOLDER_REMOTE,
+                       NETWORK_SERVER,
+                       NETWORK_WORKGROUP,
+                       START_HERE,
+                       USER_DESKTOP,
+                       USER_HOME,
+                       USER_TRASH,
+                       APPOINTMENT_MISSED,
+                       APPOINTMENT_SOON,
+                       AUDIO_VOLUME_HIGH,
+                       AUDIO_VOLUME_LOW,
+                       AUDIO_VOLUME_MEDIUM,
+                       AUDIO_VOLUME_MUTED,
+                       BATTERY_CAUTION,
+                       BATTERY_LOW,
+                       DIALOG_ERROR,
+                       DIALOG_INFORMATION,
+                       DIALOG_PASSWORD,
+                       DIALOG_QUESTION,
+                       DIALOG_WARNING,
+                       FOLDER_DRAG_ACCEPT,
+                       FOLDER_OPEN,
+                       FOLDER_VISITING,
+                       IMAGE_LOADING,
+                       IMAGE_MISSING,
+                       MAIL_ATTACHMENT,
+                       MAIL_UNREAD,
+                       MAIL_READ,
+                       MAIL_REPLIED,
+                       MAIL_SIGNED,
+                       MAIL_SIGNED_VERIFIED,
+                       MEDIA_PLAYLIST_REPEAT,
+                       MEDIA_PLAYLIST_SHUFFLE,
+                       NETWORK_ERROR,
+                       NETWORK_IDLE,
+                       NETWORK_OFFLINE,
+                       NETWORK_RECEIVE,
+                       NETWORK_TRANSMIT,
+                       NETWORK_TRANSMIT_RECEIVE,
+                       PRINTER_ERROR,
+                       PRINTER_PRINTING,
+                       SOFTWARE_UPDATE_AVAILABLE,
+                       SOFTWARE_UPDATE_URGENT,
+                       SYNC_ERROR,
+                       SYNC_SYNCHRONIZING,
+                       TASK_DUE,
+                       TASK_PASSED_DUE,
+                       USER_AWAY,
+                       USER_IDLE,
+                       USER_OFFLINE,
+                       USER_ONLINE,
+                       USER_TRASH_FULL,
+                       WEATHER_CLEAR,
+                       WEATHER_CLEAR_NIGHT,
+                       WEATHER_FEW_CLOUDS,
+                       WEATHER_FEW_CLOUDS_NIGHT,
+                       WEATHER_FOG,
+                       WEATHER_OVERCAST,
+                       WEATHER_SEVERE_ALERT,
+                       WEATHER_SHOWERS,
+                       WEATHER_SHOWERS_SCATTERED,
+                       WEATHER_SNOW,
+                       WEATHER_STORM,
+                       NULL
+               };
+
+#endif
diff --git a/src/tests/compare/efreet_alloc.c b/src/tests/compare/efreet_alloc.c
new file mode 100644 (file)
index 0000000..1527f77
--- /dev/null
@@ -0,0 +1,30 @@
+#include <Efreet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "comp.h"
+
+int
+main(void)
+{
+   int i = 0, k, errs = 0;
+    const char *path;
+
+    efreet_init();
+
+    for (k = 0; k < LOOPS; k++)
+    {
+        for (i = 0; icons[i]; i++)
+        {
+            path = efreet_icon_path_find(THEME, icons[i], SIZE);
+            if (!path)
+              {
+                 printf("%s: NOT FOUND\n", icons[i]);
+                 errs++;
+              }
+        }
+    }
+
+    efreet_shutdown();
+
+    return errs > 0;
+}
diff --git a/src/tests/compare/efreet_menu_alloc.c b/src/tests/compare/efreet_menu_alloc.c
new file mode 100644 (file)
index 0000000..fcfb75b
--- /dev/null
@@ -0,0 +1,23 @@
+#include <Efreet.h>
+#include <stdio.h>
+#include "comp.h"
+
+int
+main(void)
+{
+    int k;
+
+    efreet_init();
+
+    for (k = 0; k < LOOPS; k++)
+    {
+        Efreet_Menu *menu;
+        menu = efreet_menu_get();
+        efreet_menu_free(menu);
+    }
+
+    efreet_shutdown();
+
+    return 0;
+}
+
diff --git a/src/tests/data/Makefile.am b/src/tests/data/Makefile.am
new file mode 100644 (file)
index 0000000..50eef85
--- /dev/null
@@ -0,0 +1,18 @@
+SUBDIRS = sub
+
+MAINTAINERCLEANFILES = Makefile.in
+
+testdir = $(pkgdatadir)/test
+test_DATA = \
+test.ini \
+long.ini \
+test.desktop \
+test_type.desktop \
+test.menu \
+test_menu_slash_bad.menu \
+entry.png \
+entry \
+preferences.menu \
+test_garbage
+
+EXTRA_DIST = $(test_DATA)
diff --git a/src/tests/data/entry b/src/tests/data/entry
new file mode 100644 (file)
index 0000000..503d8e5
Binary files /dev/null and b/src/tests/data/entry differ
diff --git a/src/tests/data/entry.png b/src/tests/data/entry.png
new file mode 100644 (file)
index 0000000..82e5cbe
Binary files /dev/null and b/src/tests/data/entry.png differ
diff --git a/src/tests/data/long.ini b/src/tests/data/long.ini
new file mode 100644 (file)
index 0000000..32154dd
--- /dev/null
@@ -0,0 +1,3 @@
+[section]
+key=averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,the last value
+key2=averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,averylongvalue,the last value
diff --git a/src/tests/data/preferences.menu b/src/tests/data/preferences.menu
new file mode 100644 (file)
index 0000000..904dbd2
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE Menu PUBLIC 
+        "-//freedesktop//DTD Menu 1.0//EN"
+        "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
+
+<Menu>
+  <Name>Preferences</Name>
+  <Directory>Preferences.directory</Directory>
+
+  <AppDir>blah</AppDir>
+  <AppDir>/var/tmp</AppDir>
+
+  <Move> 
+    <Old>Blah</Old 
+    <New>Borp</New> 
+  </Move>  
+
+  <Menu> 
+    <Name>House</Name> 
+    <Directory>House.directory</Directory> 
+    <Include> 
+      <Category>House</Category> 
+      <Category>Garden</Category>
+    </Include> 
+  </Menu> 
+  <Menu> 
+    <Name>Mouse</Name> 
+    <Directory>House.directory</Directory> 
+    <Include> 
+      <Category>House</Category> 
+      <Category>Garden</Category>
+    </Include> 
+  </Menu> 
+  <Menu> 
+    <Name>House</Name> 
+    <Directory>House.directory</Directory> 
+    <Include> 
+      <Category>Cat</Category>
+    </Include> 
+  </Menu> 
+</Menu>
+
diff --git a/src/tests/data/sub/Makefile.am b/src/tests/data/sub/Makefile.am
new file mode 100644 (file)
index 0000000..7aaf7fc
--- /dev/null
@@ -0,0 +1,8 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+testdir = $(pkgdatadir)/test/sub
+test_DATA = \
+test.desktop
+
+EXTRA_DIST = $(test_DATA)
diff --git a/src/tests/data/sub/test.desktop b/src/tests/data/sub/test.desktop
new file mode 100644 (file)
index 0000000..7abf2ae
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=Sub
+Exec=subtest
+Categories=Test
diff --git a/src/tests/data/test.desktop b/src/tests/data/test.desktop
new file mode 100644 (file)
index 0000000..412601f
--- /dev/null
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Application
+Name=Efreet Test Application
+GenericName=Test Application
+Exec=efreet_test %F %i
+Categories=Test;Enlightenment;
+Icon=TestIcon
+X-Test=Own key
diff --git a/src/tests/data/test.ini b/src/tests/data/test.ini
new file mode 100644 (file)
index 0000000..7f73d81
--- /dev/null
@@ -0,0 +1,21 @@
+# Comments should be ignored (and empty lines)
+
+[contact]
+Name=Foo Bar
+Name[en_US]=English Foo Bar
+Email=  foo@bar.com
+Email[de_DE] = foo@bar.de
+Age =  30
+TrueBoolean=true
+FalseBoolean=false
+InvalidBoolean=invalid
+Escaped=line1\nline2\r\nline3\ttabbed \\ with a backslash\sand\sspaces
+
+[AIM]
+Username=foobar
+
+# the next line has a single space. it should be skipped as well
+[Jabber]
+Username=foobar@bar.de
+
diff --git a/src/tests/data/test.menu b/src/tests/data/test.menu
new file mode 100644 (file)
index 0000000..7ae21ff
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE Menu PUBLIC 
+        "-//freedesktop//DTD Menu 1.0//EN"
+        "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
+
+<Menu>
+  <Name>Applications</Name>
+  <Directory>Applications.directory</Directory>
+
+  <DefaultAppDirs/>
+  <DefaultDirectoryDirs/>
+
+  <MergeDir>applications-merged</MergeDir>
+
+  <LegacyDir>/usr/share/applnk</LegacyDir>
+
+  <DefaultLayout>
+    <Merge type="menus"/>
+    <Merge type="files"/>
+    <Separator/>
+    <Menuname>More</Menuname>
+  </DefaultLayout>
+
+  <Move> 
+    <Old>Foo</Old> 
+    <New>Bar</New> 
+    <Old>Foo2</Old> 
+    <New>Bar2</New> 
+  </Move>  
+
+  <Menu> 
+    <Name>Preferences</Name> 
+    <Directory>Preferences.directory</Directory> 
+    <MergeFile>preferences.menu</MergeFile> 
+  </Menu> 
+
+  <Menu> 
+    <Name>Office</Name> 
+    <Directory>Office.directory</Directory> 
+    <Include> 
+      <Category>Office</Category> 
+    </Include> 
+    <Exclude> 
+      <Filename>foo.desktop</Filename> 
+      <And>
+        <Not>
+          <Filename>bar.desktop</Filename>
+        </Not>
+      </And>
+    </Exclude> 
+  </Menu> 
+</Menu>
+
diff --git a/src/tests/data/test_garbage b/src/tests/data/test_garbage
new file mode 100644 (file)
index 0000000..7d178fd
--- /dev/null
@@ -0,0 +1,2341 @@
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcdef
+abcd
\ No newline at end of file
diff --git a/src/tests/data/test_menu_bad_comment.menu b/src/tests/data/test_menu_bad_comment.menu
new file mode 100644 (file)
index 0000000..5815b89
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE Menu PUBLIC 
+        "-//freedesktop//DTD Menu 1.0//EN"
+        "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
+
+<Menu>
+  <Name>Applications</Name>
+  <Directory>Applications.directory</Directory>
+
+  <!-- a comment
+   <!-- with a comment -->
+  <Or> This should be commented </Or>
+  -->
+</Menu>
+
diff --git a/src/tests/data/test_menu_slash_bad.menu b/src/tests/data/test_menu_slash_bad.menu
new file mode 100644 (file)
index 0000000..8f7b6f0
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE Menu PUBLIC 
+        "-//freedesktop//DTD Menu 1.0//EN"
+        "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
+
+<Menu>
+  <Name>Applications/Bar</Name>
+
+  <Menu> 
+    <Name>Preferences/Baz</Name> 
+  </Menu> 
+</Menu>
diff --git a/src/tests/data/test_type.desktop b/src/tests/data/test_type.desktop
new file mode 100644 (file)
index 0000000..1b67ff5
--- /dev/null
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=My_Type
+Name=Efreet Test Application
+GenericName=Test Application
+Exec=efreet_test %F %i
+Categories=Test;Enlightenment;
+Icon=TestIcon
+X-Test=Own key
diff --git a/src/tests/ef_cache.c b/src/tests/ef_cache.c
new file mode 100644 (file)
index 0000000..3cbee9e
--- /dev/null
@@ -0,0 +1,199 @@
+#include "Efreet.h"
+#include <stdio.h>
+#include <Ecore.h>
+#include "ef_test.h"
+
+#if 0
+EAPI Efreet_Desktop *efreet_util_desktop_file_id_find(const char *file_id);
+
+EAPI Eina_List *efreet_util_desktop_generic_name_glob_list(const char *glob);
+EAPI Eina_List *efreet_util_desktop_comment_glob_list(const char *glob);
+#endif
+
+static Eina_Bool icon_cb = EINA_FALSE;
+static Eina_Bool desktop_cb = EINA_FALSE;
+
+static void
+check(void)
+{
+    Eina_List *list;
+    Efreet_Desktop *desktop, *desktop2;
+    double start;
+    const char *id;
+
+    // EAPI char *efreet_util_path_to_file_id(const char *path);
+    start = ecore_time_get();
+    id = efreet_util_path_to_file_id("/usr/share/applications/gnome-panel.desktop");
+    if (id)
+    {
+        printf("efreet_util_path_to_file_id(/usr/share/applications/gnome-panel.desktop): %s %.6f\n", id, (ecore_time_get() - start));
+    }
+    else
+        printf("efreet_util_path_to_file_id(/usr/share/applications/gnome-panel.desktop): NULL %.6f\n", (ecore_time_get() - start));
+
+    //EAPI Efreet_Desktop *efreet_util_desktop_name_find(const char *name);
+    start = ecore_time_get();
+    desktop = efreet_util_desktop_name_find("Evolution");
+    if (desktop)
+        printf("efreet_util_desktop_name_find(Evolution): %s %.6f\n", desktop->orig_path, (ecore_time_get() - start));
+    else
+        printf("efreet_util_desktop_name_find(Evolution): NULL %.6f\n", (ecore_time_get() - start));
+    efreet_desktop_free(desktop);
+
+    //EAPI Efreet_Desktop *efreet_util_desktop_generic_name_find(const char *generic_name);
+    start = ecore_time_get();
+    desktop = efreet_util_desktop_generic_name_find("Spreadsheet");
+    if (desktop)
+        printf("efreet_util_desktop_generic_name_find(Spreadsheet): %s %.6f\n", desktop->orig_path, (ecore_time_get() - start));
+    else
+        printf("efreet_util_desktop_generic_name_find(Spreadsheet): NULL %.6f\n", (ecore_time_get() - start));
+    efreet_desktop_free(desktop);
+
+    //EAPI Efreet_Desktop *efreet_util_desktop_wm_class_find(const char *wmname, const char *wmclass);
+    start = ecore_time_get();
+    desktop = efreet_util_desktop_wm_class_find("Firefox", NULL);
+    if (desktop)
+        printf("efreet_util_desktop_wm_class_find(Firefox): %s %.6f\n", desktop->orig_path, (ecore_time_get() - start));
+    else
+        printf("efreet_util_desktop_wm_class_find(Firefox): NULL %.6f\n", (ecore_time_get() - start));
+    efreet_desktop_free(desktop);
+
+    //EAPI Efreet_Desktop *efreet_util_desktop_exec_find(const char *exec);
+    start = ecore_time_get();
+    desktop = efreet_util_desktop_exec_find("/usr/bin/update-manager");
+    if (desktop)
+        printf("efreet_util_desktop_exec_find(update-manager): %s %.6f\n", desktop->orig_path, (ecore_time_get() - start));
+    else
+        printf("efreet_util_desktop_exec_find(update-manager): NULL %.6f\n", (ecore_time_get() - start));
+    efreet_desktop_free(desktop);
+
+    //EAPI Eina_List *efreet_util_desktop_name_glob_list(const char *glob);
+    start = ecore_time_get();
+    list = efreet_util_desktop_name_glob_list("Ubuntu*");
+    if (list)
+    {
+        EINA_LIST_FREE(list, desktop)
+        {
+            printf("efreet_util_desktop_name_glob_list(Ubuntu*): %s\n", desktop->name);
+            efreet_desktop_free(desktop);
+        }
+    }
+    printf("time: %.6f\n", (ecore_time_get() - start));
+
+    //EAPI Eina_List *efreet_util_desktop_mime_list(const char *mime);
+    start = ecore_time_get();
+    list = efreet_util_desktop_mime_list("application/ogg");
+    if (list)
+    {
+        EINA_LIST_FREE(list, desktop)
+        {
+            printf("efreet_util_desktop_mime_list(application/ogg): %s\n", desktop->name);
+            efreet_desktop_free(desktop);
+        }
+    }
+    printf("time: %.6f\n", (ecore_time_get() - start));
+
+    //EAPI Eina_List *efreet_util_desktop_exec_glob_list(const char *glob);
+    start = ecore_time_get();
+    list = efreet_util_desktop_exec_glob_list("*gnome*");
+    if (list)
+    {
+        EINA_LIST_FREE(list, desktop)
+        {
+            printf("efreet_util_desktop_exec_glob_list(*gnome*): %s\n", desktop->exec);
+            efreet_desktop_free(desktop);
+        }
+    }
+    printf("time: %.6f\n", (ecore_time_get() - start));
+
+    //EAPI Eina_List *efreet_util_desktop_categories_list(void);
+    start = ecore_time_get();
+    list = efreet_util_desktop_categories_list();
+    if (list)
+    {
+        EINA_LIST_FREE(list, id)
+        {
+            printf("efreet_util_desktop_categories_list(): %s\n", id);
+        }
+    }
+    printf("time: %.6f\n", (ecore_time_get() - start));
+
+    //EAPI Eina_List *efreet_util_desktop_category_list(const char *category);
+    start = ecore_time_get();
+    list = efreet_util_desktop_category_list("Graphics");
+    if (list)
+    {
+        EINA_LIST_FREE(list, desktop)
+        {
+            printf("efreet_util_desktop_category_list(Graphics): %s\n", desktop->name);
+            efreet_desktop_free(desktop);
+        }
+    }
+    printf("time: %.6f\n", (ecore_time_get() - start));
+
+    desktop = efreet_desktop_get("/opt/google/chrome/google-chrome.desktop");
+    if (desktop)
+        printf("%s: %d %d\n", desktop->orig_path, desktop->ref, desktop->eet);
+    desktop2 = efreet_desktop_new("/opt/google/chrome/google-chrome.desktop");
+    if (desktop2)
+    {
+        printf("%s: %d %d\n", desktop2->orig_path, desktop2->ref, desktop2->eet);
+        efreet_desktop_free(desktop2);
+    }
+    if (desktop)
+        efreet_desktop_free(desktop);
+
+    desktop = efreet_desktop_get("/usr/share/applications/firefox.desktop");
+    if (desktop)
+        printf("%s: %d %d\n", desktop->orig_path, desktop->ref, desktop->eet);
+    desktop2 = efreet_desktop_new("/usr/share/applications/firefox.desktop");
+    if (desktop2)
+    {
+        printf("%s: %d %d\n", desktop2->orig_path, desktop2->ref, desktop2->eet);
+        efreet_desktop_free(desktop2);
+    }
+    if (desktop)
+        efreet_desktop_free(desktop);
+    fflush(stdout);
+}
+
+static Eina_Bool
+icon_handler_cb(void *data __UNUSED__, int event_type __UNUSED__, void *event __UNUSED__)
+{
+    icon_cb = EINA_TRUE;
+    if (icon_cb && desktop_cb)
+    {
+        check();
+        ecore_main_loop_quit();
+    }
+    return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+desktop_handler_cb(void *data __UNUSED__, int event_type __UNUSED__, void *event __UNUSED__)
+{
+    desktop_cb = EINA_TRUE;
+    if (icon_cb && desktop_cb)
+    {
+        check();
+        ecore_main_loop_quit();
+    }
+    return ECORE_CALLBACK_PASS_ON;
+}
+
+int
+main(int argc __UNUSED__, char **argv __UNUSED__)
+{
+    Ecore_Event_Handler *icon_handler;
+    Ecore_Event_Handler *desktop_handler;
+
+    if (!efreet_init()) return 1;
+    icon_handler = ecore_event_handler_add(EFREET_EVENT_ICON_CACHE_UPDATE, icon_handler_cb, NULL);
+    desktop_handler = ecore_event_handler_add(EFREET_EVENT_DESKTOP_CACHE_UPDATE, desktop_handler_cb, NULL);
+    check();
+    ecore_main_loop_begin();
+    ecore_event_handler_del(icon_handler);
+    ecore_event_handler_del(desktop_handler);
+    efreet_shutdown();
+    return 0;
+}
diff --git a/src/tests/ef_data_dirs.c b/src/tests/ef_data_dirs.c
new file mode 100644 (file)
index 0000000..a99b2ae
--- /dev/null
@@ -0,0 +1,330 @@
+#include "Efreet.h"
+#include <Ecore_File.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int
+ef_cb_efreet_data_home(void)
+{
+    const char *tmp;
+    int ret = 1;
+
+    efreet_shutdown();
+    setenv("XDG_DATA_HOME", "/var/tmp", 1);
+    efreet_init();
+
+    tmp = efreet_data_home_get();
+    if (strcmp(tmp, "/var/tmp"))
+    {
+        printf("efreet_data_home_get() returned incorrect "
+                "value (%s) on XDG_DATA_HOME=/var/tmp\n", tmp);
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    unsetenv("XDG_DATA_HOME");
+    setenv("HOME", "/home/tmp", 1);
+    efreet_init();
+
+    tmp = efreet_data_home_get();
+    if (strcmp(tmp, "/home/tmp/.local/share"))
+    {
+        printf("efreet_data_home_get() returned incorrect "
+                "value (%s) on blank XDG_DATA_HOME\n", tmp);
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    unsetenv("XDG_DATA_HOME");
+    unsetenv("HOME");
+#ifdef _WIN32
+    unsetenv("USERPROFILE");
+#endif
+    efreet_init();
+
+    tmp = efreet_data_home_get();
+    if (strcmp(tmp, "/tmp/.local/share"))
+    {
+        printf("efreet_data_home_get() returned incorrect "
+                "value (%s) on blank XDG_DATA_HOME and blank HOME\n", tmp);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+int
+ef_cb_efreet_config_home(void)
+{
+    const char *tmp;
+    int ret = 1;
+
+    efreet_shutdown();
+    setenv("XDG_CONFIG_HOME", "/var/tmp", 1);
+    efreet_init();
+
+    tmp = efreet_config_home_get();
+    if (strcmp(tmp, "/var/tmp"))
+    {
+        printf("efreet_config_home_get() returned incorrect "
+                "value (%s) on XDG_CONFIG_HOME=/var/tmp\n", tmp);
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    unsetenv("XDG_CONFIG_HOME");
+    setenv("HOME", "/home/tmp", 1);
+    efreet_init();
+
+    tmp = efreet_config_home_get();
+    if (strcmp(tmp, "/home/tmp/.config"))
+    {
+        printf("efreet_config_home_get() returned incorrect "
+                "value (%s) on blank XDG_CONFIG_HOME\n", tmp);
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    unsetenv("XDG_CONFIG_HOME");
+    unsetenv("HOME");
+#ifdef _WIN32
+    unsetenv("USERPROFILE");
+#endif
+    efreet_init();
+
+    tmp = efreet_config_home_get();
+    if (strcmp(tmp, "/tmp/.config"))
+    {
+        printf("efreet_config_home_get() returned incorrect "
+                "value (%s) on blank XDG_CONFIG_HOME and blank HOME\n", tmp);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+int
+ef_cb_efreet_cache_home(void)
+{
+    const char *tmp;
+    int ret = 1;
+
+    efreet_shutdown();
+    setenv("XDG_CACHE_HOME", "/var/tmp", 1);
+    efreet_init();
+
+    tmp = efreet_cache_home_get();
+    if (strcmp(tmp, "/var/tmp"))
+    {
+        printf("efreet_cache_home_get() returned incorrect "
+                "value (%s) on XDG_CACHE_HOME=/var/tmp\n", tmp);
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    unsetenv("XDG_CACHE_HOME");
+    setenv("HOME", "/home/tmp", 1);
+    efreet_init();
+
+    tmp = efreet_cache_home_get();
+    if (strcmp(tmp, "/home/tmp/.cache"))
+    {
+        printf("efreet_cache_home_get() returned incorrect "
+                "value (%s) on blank XDG_CACHE_HOME\n", tmp);
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    unsetenv("XDG_CACHE_HOME");
+    unsetenv("HOME");
+#ifdef _WIN32
+    unsetenv("USERPROFILE");
+#endif
+    efreet_init();
+
+    tmp = efreet_cache_home_get();
+    if (strcmp(tmp, "/tmp/.cache"))
+    {
+        printf("efreet_cache_home_get() returned incorrect "
+                "value (%s) on blank XDG_CACHE_HOME and blank HOME\n", tmp);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+int
+ef_cb_efreet_data_dirs(void)
+{
+    Eina_List *tmp, *l;
+    int ret = 1;
+    unsigned int i;
+    unsigned int ok;
+    char dirs[128], *val;
+    char *vals[] = {"/var/tmp/a", "/tmp/b", "/usr/local/share", "/etc", NULL};
+    char *def_vals[] = {PACKAGE_DATA_DIR, "/usr/share", "/usr/local/share", NULL};
+
+    dirs[0] = '\0';
+    for (i = 0; vals[i]; i++)
+    {
+        if (i > 0) strcat(dirs, ":");
+        strcat(dirs, vals[i]);
+    }
+
+    efreet_shutdown();
+    setenv("XDG_DATA_DIRS", dirs, 1);
+    efreet_init();
+
+    ok = 0;
+    tmp = efreet_data_dirs_get();
+    for (i = 0; vals[i]; i++)
+    {
+        char *found;
+
+        found = eina_list_search_unsorted(tmp, EINA_COMPARE_CB(strcmp), vals[i]);
+        if (!ecore_file_exists(vals[i]) && found)
+        {
+            printf("efreet_data_dirs_get() includes non-existing dir (%s) when "
+                   "%s set\n", vals[i], dirs);
+            ret = 0;
+            continue;
+        }
+        if (ecore_file_exists(vals[i]) && !found)
+        {
+            printf("efreet_data_dirs_get() is missing dir (%s) when "
+                   "%s set\n", vals[i], dirs);
+            ret = 0;
+            continue;
+        }
+        if (ecore_file_exists(vals[i]) && found)
+            ok++;
+    }
+    if (eina_list_count(tmp) != ok)
+    {
+        printf("efreet_data_dirs_get() returned more values then it "
+               "should have given %s as input\n", dirs);
+        ret = 0;
+    }
+
+    efreet_shutdown();
+    unsetenv("XDG_DATA_DIRS");
+    efreet_init();
+
+    i = 0;
+    tmp = efreet_data_dirs_get();
+    if (eina_list_count(tmp) != 3)
+    {
+        printf("efreet_data_dirs_get() nodes is differnet from expected default\n");
+        ret = 0;
+    }
+
+    EINA_LIST_FOREACH(tmp, l, val)
+    {
+        if (!def_vals[i])
+        {
+            printf("efreet_data_dirs_get() returned more values then it "
+                    "should have given %s as input\n", dirs);
+            ret = 0;
+            break;
+        }
+
+        if (strcmp(val, def_vals[i]))
+        {
+            printf("efreet_data_dirs_get() returned incorrect value (%s) when "
+                    "XDG_DATA_DIRS= is set %s\n", val, def_vals[i]);
+            ret = 0;
+        }
+
+        i++;
+    }
+    return ret;
+}
+
+int
+ef_cb_efreet_config_dirs(void)
+{
+    Eina_List *tmp, *l;
+    int ret = 1;
+    unsigned int i;
+    unsigned int ok;
+    char dirs[128], *val;
+    char *vals[] = {"/var/tmp/a", "/tmp/b", "/usr/local/share", "/etc", NULL};
+    char *def_vals[] = {"/etc/xdg", NULL};
+
+    dirs[0] = '\0';
+
+    for (i = 0; vals[i]; i++)
+    {
+        if (i > 0) strcat(dirs, ":");
+        strcat(dirs, vals[i]);
+    }
+
+    efreet_shutdown();
+    setenv("XDG_CONFIG_DIRS", dirs, 1);
+    efreet_init();
+
+    ok = 0;
+    tmp = efreet_config_dirs_get();
+    for (i = 0; vals[i]; i++)
+    {
+        char *found;
+
+        found = eina_list_search_unsorted(tmp, EINA_COMPARE_CB(strcmp), vals[i]);
+        if (!ecore_file_exists(vals[i]) && found)
+        {
+            printf("efreet_data_dirs_get() includes non-existing dir (%s) when "
+                   "%s set\n", vals[i], dirs);
+            ret = 0;
+            continue;
+        }
+        if (ecore_file_exists(vals[i]) && !found)
+        {
+            printf("efreet_data_dirs_get() is missing dir (%s) when "
+                   "%s set\n", vals[i], dirs);
+            ret = 0;
+            continue;
+        }
+        if (ecore_file_exists(vals[i]) && found)
+            ok++;
+    }
+    if (eina_list_count(tmp) != ok)
+    {
+        printf("efreet_data_dirs_get() returned more values then it "
+               "should have given %s as input\n", dirs);
+        ret = 0;
+    }
+
+    efreet_shutdown();
+    unsetenv("XDG_CONFIG_DIRS");
+    efreet_init();
+
+    i = 0;
+    tmp = efreet_config_dirs_get();
+    EINA_LIST_FOREACH(tmp, l, val)
+    {
+        if (!def_vals[i])
+        {
+            printf("efreet_config_dirs_get() returned more values then it "
+                    "should have given %s as input\n", dirs);
+            ret = 0;
+            break;
+        }
+
+        if (strcmp(val, def_vals[i]))
+        {
+            printf("efreet_config_dirs_get() returned incorrect value (%s) when "
+                    "XDG_CONFIG_DIRS= is set\n", val);
+            ret = 0;
+        }
+
+        i++;
+    }
+    return ret;
+}
diff --git a/src/tests/ef_desktop.c b/src/tests/ef_desktop.c
new file mode 100644 (file)
index 0000000..39f942a
--- /dev/null
@@ -0,0 +1,401 @@
+#include "Efreet.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include "ef_test.h"
+
+static void *_cb_command(void *data, Efreet_Desktop *desktop, char *exec, int remaining);
+
+
+int
+ef_cb_desktop_parse(void)
+{
+    Efreet_Desktop *desktop;
+    Eina_List *l;
+    int ret = 1;
+
+    desktop = efreet_desktop_get(PKG_DATA_DIR"/test/test.desktop");
+    if (!desktop)
+    {
+        printf("No desktop found.\n");
+        return 0;
+    }
+
+    if (!desktop->name || strcmp(desktop->name, "Efreet Test Application"))
+    {
+        printf("Invalid Name\n");
+        ret = 0;
+    }
+
+    if (!desktop->generic_name ||
+        strcmp(desktop->generic_name, "Test Application"))
+    {
+        printf("Incorrent GenericName\n");
+        ret = 0;
+    }
+
+    if (!desktop->exec || strcmp(desktop->exec, "efreet_test %F %i"))
+    {
+        printf("Incorrect Exec (%s)\n", (desktop->exec ? desktop->exec : "(null)"));
+        ret = 0;
+    }
+
+    if (desktop->categories)
+    {
+        const char *categories[] = {"Test", "Enlightenment"};
+        const char *cat;
+        int num_categories = 2, i = 0;
+
+       EINA_LIST_FOREACH(desktop->categories, l, cat)
+        {
+            if (i >= num_categories)
+            {
+                printf("Too many categories found.\n");
+                ret = 0;
+                break;
+            }
+
+            if (!cat || !categories[i] || strcmp(cat, categories[i]))
+            {
+                printf("Expected category %s, found %s\n", categories[i], cat);
+                ret = 0;
+            }
+            i++;
+        }
+    }
+    else ret = 0;
+
+    efreet_desktop_free(desktop);
+
+    return ret;
+}
+
+#if 0
+int
+ef_cb_desktop_file_id(void)
+{
+    Efreet_Desktop *desktop;
+    int ret = 1;
+
+    desktop = efreet_desktop_get(PKG_DATA_DIR"/test/test.desktop");
+    if (desktop)
+    {
+        const char *id;
+        int i = 0;
+
+        struct {
+            char *dir;
+            int legacy;
+            char *prefix;
+            char *expected;
+        } tests[] = {
+            {PKG_DATA_DIR"/test/", 0, NULL, "test.desktop"},
+            {PKG_DATA_DIR"/", 0, NULL, "test-test.desktop"},
+            {PKG_DATA_DIR"/", 1, NULL, "test.desktop"},
+            {PKG_DATA_DIR"/", 1, "prefix", "prefix-test.desktop"},
+            {NULL, 0, NULL, NULL}
+        };
+
+        for (i = 0; tests[i].dir != NULL; i++)
+        {
+            id = efreet_desktop_id_get(desktop,
+                                       tests[i].dir,
+                                       tests[i].legacy,
+                                       tests[i].prefix);
+            if (!id || strcmp(id, tests[i].expected))
+            {
+                printf("Expecting id: %s, got: %s\n", tests[i].expected, id);
+                ret = 0;
+            }
+            if (id) eina_stringshare_del(id);
+        }
+    }
+    else
+        ret = 0;
+
+    return ret;
+}
+#endif
+
+int
+ef_cb_desktop_save(void)
+{
+    Efreet_Desktop *desktop;
+
+    printf("\n");
+    desktop = efreet_desktop_get(PKG_DATA_DIR"/test/test.desktop");
+    if (!desktop)
+    {
+        printf("Failed to get Desktop file\n");
+        return 0;
+    }
+
+    printf("save data: %d\n", efreet_desktop_save(desktop));
+    efreet_desktop_free(desktop);
+
+    desktop = efreet_desktop_empty_new("/tmp/test.desktop");
+    desktop->name = strdup("Efreet Test Application");
+    desktop->type = EFREET_DESKTOP_TYPE_APPLICATION;
+    desktop->generic_name = strdup("Test Application");
+    desktop->exec = strdup("efreet_test");
+    desktop->categories = NULL;
+    desktop->categories = eina_list_append(desktop->categories, eina_stringshare_add("Test"));
+    desktop->categories = eina_list_append(desktop->categories, eina_stringshare_add("Enlightenment"));
+    printf("save test: %d\n", efreet_desktop_save(desktop));
+    unlink("/tmp/test.desktop");
+    efreet_desktop_free(desktop);
+
+    return 1;
+}
+
+typedef struct
+{
+  Eina_List *expected;
+  int error;
+  char type;
+} Test_Info;
+
+int
+ef_cb_desktop_command_get(void)
+{
+    Efreet_Desktop *desktop;
+    Eina_List *files, *expected;
+    char olddir[PATH_MAX];
+    Test_Info *info;
+    int ret;
+
+    if (getcwd(olddir, PATH_MAX) != 0) ret = 0;
+    if (chdir("/") != 0) ret = 0;
+
+    printf("\n");
+    desktop = efreet_desktop_empty_new("test.desktop");
+
+    desktop->name = strdup("App Name");
+    desktop->icon = strdup("icon.png");
+
+    files = NULL;
+    files = eina_list_append(files, "/tmp/absolute_path");
+    files = eina_list_append(files, "relative_path");
+    files = eina_list_append(files, "file:///tmp/absolute_uri");
+    files = eina_list_append(files, "file:relative_uri");
+
+    info = NEW(Test_Info, 1);
+    expected = NULL;
+    info->error = 0;
+
+    /* test single full path */
+    info->type = 'f';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %f");
+    expected = eina_list_append(expected, "app '/tmp/absolute_path'");
+    expected = eina_list_append(expected, "app '/relative_path'");
+    expected = eina_list_append(expected, "app '/tmp/absolute_uri'");
+    expected = eina_list_append(expected, "app '/relative_uri'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+
+    /* test single uri */
+    info->type = 'u';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %u");
+    expected = eina_list_append(expected, "app 'file:///tmp/absolute_path'");
+    expected = eina_list_append(expected, "app 'file:///relative_path'");
+    expected = eina_list_append(expected, "app 'file:///tmp/absolute_uri'");
+    expected = eina_list_append(expected, "app 'file:///relative_uri'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+
+    /* test single dir */
+#if 0
+    info->type = 'd';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %d");
+    expected = eina_list_append(expected, "app '/tmp'");
+    expected = eina_list_append(expected, "app '/'");
+    expected = eina_list_append(expected, "app '/tmp'");
+    expected = eina_list_append(expected, "app '/'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+#endif
+
+
+    /* test single names */
+#if 0
+    info->type = 'n';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %n");
+    expected = eina_list_append(expected, "app 'absolute_path'");
+    expected = eina_list_append(expected, "app 'relative_path'");
+    expected = eina_list_append(expected, "app 'absolute_uri'");
+    expected = eina_list_append(expected, "app 'relative_uri'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+#endif
+
+    /* test multiple fullpaths */
+    info->type = 'F';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %F");
+    expected = eina_list_append(expected, "app '/tmp/absolute_path' '/relative_path' '/tmp/absolute_uri' '/relative_uri'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+
+    /* test multiple URIs */
+    info->type = 'U';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %U");
+    expected = eina_list_append(expected, "app 'file:///tmp/absolute_path' 'file:///relative_path' 'file:///tmp/absolute_uri' 'file:///relative_uri'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+
+    /* test multiple dirs */
+#if 0
+    info->type = 'D';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %D");
+    expected = eina_list_append(expected, "app '/tmp' '/' '/tmp' '/'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+#endif
+
+    /* test multiple names */
+#if 0
+    info->type = 'N';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %N");
+    expected = eina_list_append(expected, "app 'absolute_path' 'relative_path' 'absolute_uri' 'relative_uri'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    expected = eina_list_free(expected);
+#endif
+
+    /* test icon appending */
+    info->type = 'i';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %i");
+    expected = eina_list_append(expected, "app --icon 'icon.png'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, NULL, _cb_command, info);
+    expected = eina_list_free(expected);
+
+    /* test app name */
+    info->type = 'c';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %c");
+    expected = eina_list_append(expected, "app 'App Name'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, NULL, _cb_command, info);
+    expected = eina_list_free(expected);
+
+    /* test desktop path */
+    info->type = 'k';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %k");
+    expected = eina_list_append(expected, "app 'test.desktop'");
+
+    info->expected = expected;
+    efreet_desktop_command_get(desktop, NULL, _cb_command, info);
+    eina_list_free(expected);
+
+    /* clean up */
+    efreet_desktop_free(desktop);
+    eina_list_free(files);
+
+    if (chdir(olddir) != 0) ret = 0;
+   
+    ret = info->error > 0 ? 0 : 1;
+    free(info);
+
+    return ret;
+}
+
+static void *
+_cb_command(void *data, Efreet_Desktop *desktop __UNUSED__,
+            char *exec, int remaining __UNUSED__)
+{
+  Test_Info *info = data;
+  char *expected;
+
+  expected = eina_list_data_get(info->expected);
+  info->expected = eina_list_demote_list(info->expected, info->expected);
+  if (!expected)
+  {
+    printf("  ERROR: (%%%c) got \"%s\", expected nothing\n", info->type, exec);
+    info->error++;
+  }
+  else
+  {
+    if (strcmp(exec, expected))
+    {
+      printf("  ERROR: (%%%c) got \"%s\", expected \"%s\"\n", info->type, exec, expected);
+      info->error++;
+    }
+  }
+  free(exec);
+  return NULL;
+}
+
+static void *
+cb_type_parse(Efreet_Desktop *desktop __UNUSED__, Efreet_Ini *ini)
+{
+    const char *val;
+    val = efreet_ini_string_get(ini, "X-Test");
+    if (!val) return NULL;
+    return (void *)strdup(val);
+}
+
+int
+ef_cb_desktop_type_parse(void)
+{
+    Efreet_Desktop *desktop;
+    int my_type;
+    char *val;
+    int ret = 1;
+
+    /* add my custom desktop type to efreet */
+    my_type = efreet_desktop_type_add("My_Type", cb_type_parse, NULL,
+                                        (Efreet_Desktop_Type_Free_Cb)free);
+
+    desktop = efreet_desktop_get(PKG_DATA_DIR"/test/test_type.desktop");
+    if (!desktop)
+    {
+        printf("No desktop found.\n");
+        return 0;
+    }
+
+    if (desktop->type != my_type)
+    {
+        printf("Invalid type returned in desktop");
+        ret = 0;
+    }
+
+    val = (char *)efreet_desktop_type_data_get(desktop);
+    if (!val || strcmp(val, "Own key"))
+    {
+        printf("Invalid value of custom key (%s).\n", val);
+        ret = 0;
+    }
+
+    efreet_desktop_free(desktop);
+    return ret;
+}
diff --git a/src/tests/ef_icon_theme.c b/src/tests/ef_icon_theme.c
new file mode 100644 (file)
index 0000000..1a04cfa
--- /dev/null
@@ -0,0 +1,605 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Efreet.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+#define SIZE 128
+#define THEME "Tango"
+#define FREE(x) do { free(x); x = NULL; } while (0);
+
+static Eina_Bool _hash_keys(Eina_Hash *hash, const char *key, void *list);
+static void ef_icon_theme_themes_find(const char *search_dir,
+                                        Eina_Hash *themes);
+static void ef_icons_find(Efreet_Icon_Theme *theme, Eina_Hash *icons);
+static void ef_read_dir(const char *dir, Eina_Hash *icons);
+
+int
+ef_cb_efreet_icon_theme(void)
+{
+    int ret = 1;
+    const char *tmp;
+
+    unsetenv("XDG_DATA_HOME");
+    efreet_shutdown();
+    putenv("HOME=/var/tmp");
+    efreet_init();
+
+    tmp = efreet_icon_user_dir_get();
+    if (strcmp(tmp, "/var/tmp/.local/share/icons"))
+    {
+        printf("efreet_icon_user_dir_get() returned incorrect "
+                "value (%s) on HOME=/var/tmp\n", tmp);
+        ret = 0;
+    }
+
+    efreet_shutdown();
+    unsetenv("HOME");
+#ifdef _WIN32
+    unsetenv("USERPROFILE");
+#endif
+    efreet_init();
+
+    tmp = efreet_icon_user_dir_get();
+    if (strcmp(tmp, "/tmp/.local/share/icons"))
+    {
+        printf("efreet_icon_user_dir_get() returned incorrect "
+                "value (%s) on HOME=\n", tmp);
+        ret = 0;
+    }
+
+    return ret;
+}
+
+static Eina_Bool
+_hash_keys(Eina_Hash *hash __UNUSED__, const char *key, void *list)
+{
+  Eina_List **l = list;
+
+  *l = eina_list_append(*l, key);
+  return EINA_TRUE;
+}
+
+int
+ef_cb_efreet_icon_theme_list(void)
+{
+    int ret = 1;
+    Eina_List *themes;
+    Eina_List *icon_dirs;
+    Eina_List *l;
+    Eina_Hash *dirs;
+    Eina_Iterator *it;
+    Efreet_Icon_Theme *theme;
+    const char *dir;
+    char buf[PATH_MAX];
+
+    dirs = eina_hash_string_superfast_new(free);
+
+    icon_dirs = efreet_data_dirs_get();
+
+    ef_icon_theme_themes_find(efreet_icon_user_dir_get(), dirs);
+    EINA_LIST_FOREACH(icon_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/icons", dir);
+        ef_icon_theme_themes_find(buf, dirs);
+    }
+    EINA_LIST_FOREACH(icon_dirs, l, dir)
+    {
+        snprintf(buf, sizeof(buf), "%s/pixmaps", dir);
+        ef_icon_theme_themes_find(buf, dirs);
+    }
+    ef_icon_theme_themes_find("/usr/share/pixmaps", dirs);
+
+    themes = efreet_icon_theme_list_get();
+    EINA_LIST_FOREACH(themes, l, theme)
+    {
+        if ((eina_hash_find(dirs, theme->name.internal)))
+            eina_hash_del(dirs, theme->name.internal, NULL);
+        else
+        {
+            printf("efreet_icon_theme_list_get() returned %s which we didn't "
+                    "see when scanning the directories.\n", theme->name.internal);
+            ret = 0;
+        }
+    }
+    while (themes)
+    {
+        themes = eina_list_remove_list(themes, themes);
+    }
+
+    themes = NULL;
+    it = eina_hash_iterator_key_new(dirs);
+    eina_iterator_foreach(it, EINA_EACH_CB(_hash_keys), &themes);
+    eina_iterator_free(it);
+
+    if (eina_list_count(themes) > 0)
+    {
+        printf("efreet_icon_theme_list_get() missed: ");
+        EINA_LIST_FOREACH(themes, l, dir)
+            printf("%s ", dir);
+        printf("\n");
+
+        ret = 0;
+    }
+    while (themes)
+    {
+        themes = eina_list_remove_list(themes, themes);
+      }
+    eina_hash_free(dirs);
+
+    return ret;
+}
+
+static void
+ef_icon_theme_themes_find(const char *search_dir, Eina_Hash *themes)
+{
+    Eina_List *dirs;
+    char *dir;
+
+    if (!search_dir || !themes) return;
+
+    dirs = ecore_file_ls(search_dir);
+    if (!dirs) return;
+
+    while ((dir = eina_list_data_get(dirs)))
+    {
+        char p[PATH_MAX];
+
+        dirs = eina_list_remove_list(dirs, dirs);
+        /* if we've already added the theme we're done */
+        if (eina_hash_find(themes, dir))
+        {
+            free(dir);
+            continue;
+        }
+
+        /* if the index.theme file exists we open it and look for the hidden
+         * flag. */
+        snprintf(p, sizeof(p), "%s/%s/index.theme", search_dir, dir);
+        if (ecore_file_exists(p))
+        {
+            Efreet_Ini *ini;
+            char *d;
+            int skip = 0;
+
+            ini = efreet_ini_new(p);
+            efreet_ini_section_set(ini, "Icon Theme");
+
+            //if (efreet_ini_boolean_get(ini, "Hidden")) skip = 1;
+            if (!efreet_ini_localestring_get(ini, "Name")) skip = 1;
+            efreet_ini_free(ini);
+
+            if (!skip)
+            {
+                d = strdup(dir);
+                eina_hash_add(themes, dir, d);
+            }
+        }
+        free(dir);
+    }
+}
+
+const char *system_icons[] =
+{
+    "address-book-new",
+    "application-exit",
+    "appointment-new",
+    "contact-new",
+    "dialog-apply",
+    "dialog-cancel",
+    "dialog-close",
+    "dialog-ok",
+    "document-new",
+    "document-open",
+    "document-open-recent",
+    "document-page-setup",
+    "document-print",
+    "document-print-preview",
+    "document-properties",
+    "document-revert",
+    "document-save",
+    "document-save-as",
+    "edit-copy",
+    "edit-cut",
+    "edit-delete",
+    "edit-find",
+    "edit-find-replace",
+    "edit-paste",
+    "edit-redo",
+    "edit-select-all",
+    "edit-undo",
+    "format-indent-less",
+    "format-indent-more",
+    "format-justify-center",
+    "format-justify-fill",
+    "format-justify-left",
+    "format-justify-right",
+    "format-text-direction-ltr",
+    "format-text-direction-rtl",
+    "format-text-bold",
+    "format-text-italic",
+    "format-text-underline",
+    "format-text-strikethrough",
+    "go-bottom",
+    "go-down",
+    "go-first",
+    "go-home",
+    "go-jump",
+    "go-last",
+    "go-next",
+    "go-previous",
+    "go-top",
+    "go-up",
+    "help-about",
+    "help-contents",
+    "help-faq",
+    "insert-image",
+    "insert-link",
+    "insert-object",
+    "insert-text",
+    "list-add",
+    "list-remove",
+    "mail-forward",
+    "mail-mark-important",
+    "mail-mark-junk",
+    "mail-mark-notjunk",
+    "mail-mark-read",
+    "mail-mark-unread",
+    "mail-message-new",
+    "mail-reply-all",
+    "mail-reply-sender",
+    "mail-send-receive",
+    "media-eject",
+    "media-playback-pause",
+    "media-playback-start",
+    "media-playback-stop",
+    "media-record",
+    "media-seek-backward",
+    "media-seek-forward",
+    "media-skip-backward",
+    "media-skip-forward",
+    "system-lock-screen",
+    "system-log-out",
+    "system-run",
+    "system-search",
+    "system-search",
+    "tools-check-spelling",
+    "view-fullscreen",
+    "view-refresh",
+    "view-sort-ascending",
+    "view-sort-descending",
+    "window-close",
+    "window-new",
+    "zoom-best-fit",
+    "zoom-in",
+    "zoom-original",
+    "zoom-out",
+    "process-working",
+    "accessories-calculator",
+    "accessories-character-map",
+    "accessories-dictionary",
+    "accessories-text-editor",
+    "help-browser",
+    "multimedia-volume-control",
+#if 0
+    "preferences-desktop-accessibility",
+    "preferences-desktop-font",
+    "preferences-desktop-keyboard",
+    "preferences-desktop-locale",
+    "preferences-desktop-multimedia",
+    "preferences-desktop-screensaver",
+    "preferences-desktop-theme",
+    "preferences-desktop-wallpaper",
+    "system-file-manager",
+    "system-software-update",
+    "utilities-terminal",
+    "applications-accessories",
+    "applications-development",
+    "applications-games",
+    "applications-graphics",
+    "applications-internet",
+    "applications-multimedia",
+    "applications-office",
+    "applications-other",
+    "applications-system",
+    "applications-utilities",
+    "preferences-desktop",
+    "preferences-desktop-accessibility",
+    "preferences-desktop-peripherals",
+    "preferences-desktop-personal",
+    "preferences-other",
+    "preferences-system",
+    "preferences-system-network",
+    "system-help",
+    "audio-card",
+    "audio-input-microphone",
+    "battery",
+    "camera-photo",
+    "camera-video",
+    "computer",
+    "drive-cdrom",
+    "drive-harddisk",
+    "drive-removable-media",
+    "input-gaming",
+    "input-keyboard",
+    "input-mouse",
+    "media-cdrom",
+    "media-floppy",
+    "multimedia-player",
+    "multimedia-player",
+    "network-wired",
+    "network-wireless",
+    "printer",
+    "emblem-default",
+    "emblem-documents",
+    "emblem-downloads",
+    "emblem-favorite",
+    "emblem-important",
+    "emblem-mail",
+    "emblem-photos",
+    "emblem-readonly",
+    "emblem-shared",
+    "emblem-symbolic-link",
+    "emblem-synchronized",
+    "emblem-system",
+    "emblem-unreadable",
+    "face-angel",
+    "face-crying",
+    "face-devil-grin",
+    "face-devil-sad",
+    "face-glasses",
+    "face-kiss",
+    "face-monkey",
+    "face-plain",
+    "face-sad",
+    "face-smile",
+    "face-smile-big",
+    "face-smirk",
+    "face-surprise",
+    "face-wink",
+    "application-x-executable",
+    "audio-x-generic",
+    "font-x-generic",
+    "image-x-generic",
+    "package-x-generic",
+    "text-html",
+    "text-x-generic",
+    "text-x-generic-template",
+    "text-x-script",
+    "video-x-generic",
+    "x-office-address-book",
+    "x-office-calendar",
+    "x-office-document",
+    "x-office-presentation",
+    "x-office-spreadsheet",
+    "folder",
+    "folder-remote",
+    "network-server",
+    "network-workgroup",
+    "start-here",
+    "user-desktop",
+    "user-home",
+    "user-trash",
+    "appointment-missed",
+    "appointment-soon",
+    "audio-volume-high",
+    "audio-volume-low",
+    "audio-volume-medium",
+    "audio-volume-muted",
+    "battery-caution",
+    "battery-low",
+    "dialog-error",
+    "dialog-information",
+    "dialog-password",
+    "dialog-question",
+    "dialog-warning",
+    "folder-drag-accept",
+    "folder-open",
+    "folder-visiting",
+    "image-loading",
+    "image-missing",
+    "mail-attachment",
+    "mail-unread",
+    "mail-read",
+    "mail-replied",
+    "mail-signed",
+    "mail-signed-verified",
+    "media-playlist-repeat",
+    "media-playlist-shuffle",
+    "network-error",
+    "network-idle",
+    "network-offline",
+    "network-receive",
+    "network-transmit",
+    "network-transmit-receive",
+    "printer-error",
+    "printer-printing",
+    "software-update-available",
+    "software-update-urgent",
+    "sync-error",
+    "sync-synchronizing",
+    "task-due",
+    "task-passed-due",
+    "user-away",
+    "user-idle",
+    "user-offline",
+    "user-online",
+    "user-trash-full",
+    "weather-clear",
+    "weather-clear-night",
+    "weather-few-clouds",
+    "weather-few-clouds-night",
+    "weather-fog",
+    "weather-overcast",
+    "weather-severe-alert",
+    "weather-showers",
+    "weather-showers-scattered",
+    "weather-snow",
+    "weather-storm",
+#endif
+    NULL
+};
+
+int
+ef_cb_efreet_icon_match(void)
+{
+    int i, ret = 1;
+    Eina_Hash *icon_hash;
+    Efreet_Icon_Theme *theme;
+
+    theme = efreet_icon_theme_find(THEME);
+    icon_hash = eina_hash_string_superfast_new(free);
+
+    ef_icons_find(theme, icon_hash);
+
+    double start = ecore_time_get();
+    for (i = 0; system_icons[i]; i++)
+    {
+        const char *path;
+        char *p, *s;
+
+        path = efreet_icon_path_find(THEME, system_icons[i], SIZE);
+
+        if (!path)
+        {
+#if 1
+            if (eina_hash_find(icon_hash, system_icons[i]))
+            {
+                printf("NOT FOUND %s\n", system_icons[i]);
+                ret = 0;
+            }
+#endif
+            continue;
+        }
+        else if (!eina_hash_find(icon_hash, system_icons[i]))
+        {
+            printf("Found icon not in hash: %s\n", system_icons[i]);
+        }
+
+        p = strdup(path);
+        s = strrchr(p, '.');
+        if (s) *s = '\0';
+        s = strrchr(p, '/');
+        if (s) s++;
+
+        if (s && strcmp(s, system_icons[i]))
+        {
+            printf("Name mismatch name (%s) vs ef (%s)\n", system_icons[i], s);
+            ret = 0;
+        }
+        free(p);
+    }
+    printf("Time: %f\n", (ecore_time_get() - start));
+    eina_hash_free(icon_hash);
+
+    start = ecore_time_get();
+    for (i = 0; system_icons[i]; i++)
+    {
+        const char *path;
+        char *p, *s;
+
+        path = efreet_icon_path_find(THEME, system_icons[i], SIZE);
+
+        if (!path) continue;
+        p = strdup(path);
+
+        s = strrchr(p, '.');
+        if (s) *s = '\0';
+        s = strrchr(p, '/');
+        if (s) s++;
+
+        if (s && strcmp(s, system_icons[i]))
+        {
+            printf("Name mismatch name (%s) vs ef (%s)\n", system_icons[i], s);
+            ret = 0;
+        }
+        free(p);
+    }
+    printf("Time: %f\n", (ecore_time_get() - start));
+
+    return ret;
+}
+
+static void
+ef_icons_find(Efreet_Icon_Theme *theme, Eina_Hash *icons)
+{
+    Eina_List *l, *ll;
+    char path[PATH_MAX];
+    const char *theme_path;
+
+    if (!theme || !icons) return;
+
+    EINA_LIST_FOREACH(theme->paths, l, theme_path)
+    {
+        Efreet_Icon_Theme_Directory *dir;
+
+        EINA_LIST_FOREACH(theme->directories, ll, dir)
+        {
+            snprintf(path, sizeof(path), "%s/%s/", theme_path, dir->name);
+            ef_read_dir(path, icons);
+        }
+    }
+
+    if (theme->inherits)
+    {
+        Efreet_Icon_Theme *parent_theme;
+        char *parent;
+
+        EINA_LIST_FOREACH(theme->inherits, l, parent)
+        {
+            parent_theme = efreet_icon_theme_find(parent);
+            if (parent_theme)
+                ef_icons_find(parent_theme, icons);
+        }
+    }
+    else if (strcmp(theme->name.internal, "hicolor"))
+    {
+        Efreet_Icon_Theme *parent_theme;
+
+        parent_theme = efreet_icon_theme_find("hicolor");
+        if (parent_theme)
+            ef_icons_find(parent_theme, icons);
+    }
+
+    ef_read_dir("/usr/share/pixmaps", icons);
+}
+
+static void
+ef_read_dir(const char *dir, Eina_Hash *icons)
+{
+    Eina_List *files;
+    char *file;
+
+    if (!dir || !icons) return;
+
+    files = ecore_file_ls(dir);
+    if (!files) return;
+
+    while ((file = eina_list_data_get(files)))
+    {
+        char *p;
+
+        files = eina_list_remove_list(files, files);
+        p = strrchr(file, '.');
+        if (!p)
+        {
+            FREE(file);
+            continue;
+        }
+
+        if (!strcmp(p, ".png") || !strcmp(p, ".xpm"))
+        {
+            *p = '\0';
+
+            eina_hash_add(icons, file, strdup(file));
+        }
+
+        FREE(file);
+    }
+}
diff --git a/src/tests/ef_ini.c b/src/tests/ef_ini.c
new file mode 100644 (file)
index 0000000..00d459e
--- /dev/null
@@ -0,0 +1,174 @@
+#include "Efreet.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int
+ef_cb_ini_parse(void)
+{
+    int ret = 1;
+    Efreet_Ini *ini;
+
+    putenv("LC_ALL=en_US");
+
+    ini = efreet_ini_new(PKG_DATA_DIR"/test/test.ini");
+    if (!ini)
+    {
+        printf("efreet_ini_parse() Failed to initialize Efreet_Ini\n");
+        return 0;
+    }
+
+    if (efreet_ini_section_set(ini, "contact"))
+    {
+        const char *val;
+        int ival;
+        unsigned int bval;
+
+        val = efreet_ini_string_get(ini, "Name");
+        if (!val || strcmp(val, "Foo Bar"))
+        {
+            printf("efreet_ini_string_get() Name parsed incorrectly\n");
+            ret = 0;
+        }
+
+        val = efreet_ini_localestring_get(ini, "Name");
+        if (!val || strcmp(val, "English Foo Bar"))
+        {
+            printf("efreet_ini_localestring_get() Name parsed incorrectly\n");
+            ret = 0;
+        }
+
+        val = efreet_ini_string_get(ini, "Email");
+        if (!val || strcmp(val, "foo@bar.com"))
+        {
+            printf("efreet_ini_string_get() Email parsed incorrectly\n");
+            ret = 0;
+        }
+
+        val = efreet_ini_localestring_get(ini, "Email");
+        if (!val || strcmp(val, "foo@bar.com"))
+        {
+            printf("efreet_ini_localestring_get() Email parsed incorrectly\n");
+            ret = 0;
+        }
+
+        ival = efreet_ini_int_get(ini, "Age");
+        if (ival != 30)
+        {
+            printf("efreet_ini_int_get() Age parsed incorrectly\n");
+            ret = 0;
+        }
+
+        bval = efreet_ini_boolean_get(ini, "TrueBoolean");
+        if (!bval)
+        {
+            printf("efreet_ini_boolean_get() TrueBoolean parsed incorrectly\n");
+            ret = 0;
+        }
+
+        bval = efreet_ini_boolean_get(ini, "FalseBoolean");
+        if (bval)
+        {
+            printf("efreet_ini_boolean_get() FalseBoolean parsed incorrectly\n");
+            ret = 0;
+        }
+
+        bval = efreet_ini_boolean_get(ini, "InvalidBoolean");
+        if (bval)
+        {
+            printf("efreet_ini_boolean_get() InvalidBoolean parsed incorrectly\n");
+            ret = 0;
+        }
+
+        val = efreet_ini_string_get(ini, "Escaped");
+        if (!val || strcmp(val, "line1\nline2\r\nline3\ttabbed \\ with a backslash and spaces"))
+        {
+            printf("efreet_ini_unescape() improperly unescaped value\n");
+            ret = 0;
+        }
+    }
+    else
+    {
+        printf("efreet_ini_section_set() Failed to set 'contact' section\n");
+        ret = 0;
+    }
+
+    efreet_ini_free(ini);
+
+    return ret;
+}
+
+int
+ef_cb_ini_long_line(void)
+{
+  Efreet_Ini *ini;
+  int ret = 1;
+
+  struct
+  {
+    char *key;
+    int len;
+  } tests[] = {
+    {"key", 5099},
+    {"key2", 5099},
+    {NULL, 0}
+  };
+
+  ini = efreet_ini_new(PKG_DATA_DIR"/test/long.ini");
+  if (!ini)
+  {
+    printf("Ini failed to parse.\n");
+    ret = 0;
+  }
+
+  if (ret) ret = efreet_ini_section_set(ini, "section");
+  if (ret)
+  {
+    const char *val;
+    int i, len;
+
+    for (i = 0; tests[i].key; i++)
+    {
+      val = efreet_ini_string_get(ini, tests[i].key);
+      if (val)
+      {
+        len = strlen(val);
+        if (len != tests[i].len)
+        {
+          printf("Invalid long line parsing. Value length: %d (expected %d)\n", len, tests[i].len);
+          ret = 0;
+        }
+      }
+      else
+      {
+        printf("Key missing: %s.", tests[i].key);
+        ret = 0;
+      }
+    }
+  }
+  else
+  {
+    printf("Section missing: 'section'.");
+  }
+
+  if (ini) efreet_ini_free(ini);
+  return ret;
+}
+
+int
+ef_cb_ini_garbage(void)
+{
+  Efreet_Ini *ini;
+  int ret = 1;
+   
+  ini = efreet_ini_new(PKG_DATA_DIR"/test/test_garbage");
+  if (!ini)
+  {
+    printf("Ini failed to parse.\n");
+    return 0;
+  }
+  if (ini->data) ret = 0;
+  efreet_ini_free(ini);
+  return ret;
+}
diff --git a/src/tests/ef_locale.c b/src/tests/ef_locale.c
new file mode 100644 (file)
index 0000000..cfc50e2
--- /dev/null
@@ -0,0 +1,85 @@
+#include "Efreet.h"
+#include "efreet_private.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+ef_cb_locale(void)
+{
+    int ret = 1, i;
+    struct
+    {
+        char *lc_message;
+        char *lang;
+        char *country;
+        char *modifier;
+    } langs[] = {
+        /* these are ordered such that when we move from LANG to LC_MESSAGES
+         * the LANG env will still be effect. Same with moving from
+         * LC_MESSAGES to LANG */
+        {"LANG=", NULL, NULL, NULL},
+        {"LANG=en", "en", NULL, NULL},
+        {"LANG=en@Latn", "en", NULL, "Latn"},
+        {"LANG=en_US", "en", "US", NULL},
+        {"LANG=en_US@Latn", "en", "US", "Latn"},
+        {"LANG=en_US.blah@Latn", "en", "US", "Latn"},
+        {"LC_MESSAGES=", "en", "US", "Latn"}, /* This will fallback to LANG */
+        {"LC_MESSAGES=fr", "fr", NULL, NULL},
+        {"LC_MESSAGES=fr@Blah", "fr", NULL, "Blah"},
+        {"LC_MESSAGES=fr_FR", "fr", "FR", NULL},
+        {"LC_MESSAGES=fr_FR@Blah", "fr", "FR", "Blah"},
+        {"LC_MESSAGES=fr_FR.Foo@Blah", "fr", "FR", "Blah"},
+        {"LC_ALL=", "fr", "FR", "Blah"}, /* this will fallback to LC_MESSAGES */
+        {"LC_ALL=sr", "sr", NULL, NULL},
+        {"LC_ALL=sr@Ret", "sr", NULL, "Ret"},
+        {"LC_ALL=sr_YU", "sr", "YU", NULL},
+        {"LC_ALL=sr_YU@Ret", "sr", "YU", "Ret"},
+        {"LC_ALL=sr_YU.ssh@Ret", "sr", "YU", "Ret"},
+        {NULL, NULL, NULL, NULL}
+    };
+
+    /* reset everything to blank */
+    putenv("LC_ALL=");
+    putenv("LC_MESSAGES=");
+    putenv("LANG=");
+
+    for (i = 0; langs[i].lc_message; i++)
+    {
+        const char *tmp;
+
+        putenv(langs[i].lc_message);
+
+        tmp = efreet_lang_get();
+        if ((langs[i].lang && (!tmp || strcmp(tmp, langs[i].lang)))
+                || (!langs[i].lang && tmp))
+        {
+            printf("efreet_lang_get() is wrong (%s) with %s\n",
+                                            tmp, langs[i].lang);
+            ret = 0;
+        }
+
+        tmp = efreet_lang_country_get();
+        if ((langs[i].country && (!tmp || strcmp(tmp, langs[i].country)))
+                || (!langs[i].country && tmp))
+        {
+            printf("efreet_lang_country_get() is wrong (%s) with %s\n",
+                                                tmp, langs[i].lang);
+            ret = 0;
+        }
+
+        tmp = efreet_lang_modifier_get();
+        if ((langs[i].modifier && (!tmp || strcmp(tmp, langs[i].modifier)))
+                || (!langs[i].modifier && tmp))
+        {
+            printf("efreet_lang_modifier_get() is wrong with %s with %s\n",
+                                                tmp, langs[i].lang);
+            ret = 0;
+        }
+
+        efreet_shutdown();
+        efreet_init();
+    }
+
+    return ret;
+}
diff --git a/src/tests/ef_menu.c b/src/tests/ef_menu.c
new file mode 100644 (file)
index 0000000..a7519b7
--- /dev/null
@@ -0,0 +1,150 @@
+#include "Efreet.h"
+#include "config.h"
+#include <stdio.h>
+#include <unistd.h>
+
+#if 0
+static void
+ef_menu_desktop_exec(Efreet_Menu *menu)
+{
+    Eina_List *l;
+
+    if (menu->entries)
+    {
+        Efreet_Desktop *desktop;
+
+        EINA_LIST_FOREACH(menu->entries, l, desktop)
+            efreet_desktop_exec(desktop, NULL);
+    }
+    if (menu->sub_menus)
+    {
+        Efreet_Menu *sub_menu;
+
+        EINA_LIST_FOREACH(menu->sub_menus, l, sub_menu)
+            ef_menu_desktop_exec(sub_menu);
+    }
+}
+#endif
+
+int
+ef_cb_menu_get(void)
+{
+    Efreet_Menu *menu;
+
+    menu = efreet_menu_get();
+//    menu = efreet_menu_parse(PKG_DATA_DIR"/test/test.menu");
+    if (!menu)
+    {
+        printf("efreet_menu_get() returned NULL\n");
+        return 0;
+    }
+    printf("\n");
+    efreet_menu_dump(menu, "");
+    efreet_menu_free(menu);
+
+    return 1;
+}
+
+int
+ef_cb_menu_with_slashes(void)
+{
+    Efreet_Menu *menu;
+
+    menu = efreet_menu_parse(PKG_DATA_DIR"/test/test_menu_slash_bad.menu");
+    if (menu)
+    {
+        printf("efreet_menu_get() didn't return NULL\n");
+        return 0;
+    }
+
+    return 1;
+}
+
+int
+ef_cb_menu_save(void)
+{
+    Efreet_Menu *menu;
+    int ret;
+
+//    menu = efreet_menu_get();
+    menu = efreet_menu_parse(PKG_DATA_DIR"/test/test.menu");
+    if (!menu)
+    {
+        printf("efreet_menu_get() returned NULL\n");
+        return 0;
+    }
+    unlink("/tmp/test.menu");
+    ret = efreet_menu_save(menu, "/tmp/test.menu");
+    efreet_menu_free(menu);
+    return ret;
+}
+
+int
+ef_cb_menu_edit(void)
+{
+    Efreet_Menu *menu, *entry;
+    Efreet_Desktop *desktop;
+
+//    menu = efreet_menu_get();
+    menu = efreet_menu_parse(PKG_DATA_DIR"/test/test.menu");
+    if (!menu)
+    {
+        printf("efreet_menu_get() returned NULL\n");
+        return 0;
+    }
+#if 0
+    printf("\n");
+    efreet_menu_dump(menu, "");
+    printf("\n");
+#endif
+
+    desktop = efreet_desktop_get(PKG_DATA_DIR"/test/test.desktop");
+    if (!desktop)
+    {
+        efreet_menu_free(menu);
+        printf("No desktop found.\n");
+        return 0;
+    }
+
+    efreet_menu_desktop_insert(menu, desktop, 0);
+#if 0
+    printf("\n");
+    efreet_menu_dump(menu, "");
+    printf("\n");
+#endif
+    entry = eina_list_data_get(menu->entries);
+    if (desktop != entry->desktop)
+    {
+        efreet_menu_free(menu);
+        return 0;
+    }
+
+    efreet_menu_desktop_insert(menu, desktop, 2);
+#if 0
+    printf("\n");
+    efreet_menu_dump(menu, "");
+    printf("\n");
+#endif
+    entry = eina_list_nth(menu->entries, 2);
+    if (desktop != entry->desktop)
+    {
+        efreet_menu_free(menu);
+        return 0;
+    }
+
+    efreet_menu_desktop_insert(menu, desktop, -1);
+#if 0
+    printf("\n");
+    efreet_menu_dump(menu, "");
+    printf("\n");
+#endif
+    entry = eina_list_data_get(eina_list_last(menu->entries));
+    if (desktop != entry->desktop)
+    {
+        efreet_menu_free(menu);
+        return 0;
+    }
+
+    efreet_menu_free(menu);
+    return 1;
+}
diff --git a/src/tests/ef_mime.c b/src/tests/ef_mime.c
new file mode 100644 (file)
index 0000000..2cb9e21
--- /dev/null
@@ -0,0 +1,57 @@
+#include "Efreet.h"
+#include "Efreet_Mime.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <Ecore.h>
+
+#define THEME "Tango"
+#define SIZE 128
+
+int
+ef_mime_cb_get(void)
+{
+    const char *mime = NULL, *icon;
+    int misses = 0, i = 0;
+    struct
+    {
+        char *file;
+        char *mime;
+    } files[] = {
+        {PKG_DATA_DIR"/test/test_type.desktop", "application/x-desktop"},
+        {PKG_DATA_DIR"/test/entry.png", "image/png"},
+        {PKG_DATA_DIR"/test/entry", "image/png"},
+        {PKG_DATA_DIR"/test/sub", "inode/directory"},
+        {NULL, NULL}
+    };
+    double start;
+
+    if (!efreet_mime_init())
+    {
+        printf("Could not init efreet\n");
+        return 1;
+    }
+
+    for (i = 0; files[i].file; ++i)
+    {
+        mime = efreet_mime_type_get(files[i].file);
+        if (!mime)
+        {
+            printf("Got %s as null instead of %s\n", files[i].file, files[i].mime);
+            misses ++;
+        }
+        else if (strcmp(mime, files[i].mime))
+        {
+            printf("Got %s as %s instead of %s\n", files[i].file, mime, files[i].mime);
+            misses ++;
+        }
+        start = ecore_time_get();
+        icon = efreet_mime_type_icon_get(files[i].mime, THEME, SIZE);
+        printf("mime icon: %s %s %f\n", files[i].mime, icon, ecore_time_get() - start);
+    }
+
+    efreet_mime_shutdown();
+
+    return !misses;
+}
diff --git a/src/tests/ef_test.h b/src/tests/ef_test.h
new file mode 100644 (file)
index 0000000..2e88c68
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef EF_TEST
+#define EF_TEST
+
+#include "config.h"
+
+#include <eina_types.h>
+
+#define IF_FREE(x) do { if (x) free(x); x = NULL; } while (0);
+#define NEW(x, c) calloc(c, sizeof(x))
+
+#endif
diff --git a/src/tests/ef_utils.c b/src/tests/ef_utils.c
new file mode 100644 (file)
index 0000000..0647ef0
--- /dev/null
@@ -0,0 +1,28 @@
+#include "Efreet.h"
+#include <stdio.h>
+
+int
+ef_cb_utils(void)
+{
+    Efreet_Desktop *desktop;
+    const char *tmp2;
+
+    printf("\n");
+
+    tmp2 = efreet_util_path_to_file_id("/usr/share/applications/this/tmp/test.desktop");
+    if (tmp2) printf("%s\n", tmp2);
+
+    desktop = efreet_util_desktop_file_id_find("kde-kresources.desktop");
+    printf("kde-kresources.desktop: %p\n", desktop);
+    efreet_desktop_free(desktop);
+
+    desktop = efreet_util_desktop_file_id_find("mplayer.desktop");
+    printf("mplayer.desktop: %p\n", desktop);
+    efreet_desktop_free(desktop);
+
+    desktop = efreet_util_desktop_file_id_find("nautilus-computer.desktop");
+    printf("nautilus-computer.desktop: %p\n", desktop);
+    efreet_desktop_free(desktop);
+
+    return 1;
+}
diff --git a/src/tests/efreet_icon_cache_dump.c b/src/tests/efreet_icon_cache_dump.c
new file mode 100644 (file)
index 0000000..d1cb126
--- /dev/null
@@ -0,0 +1,120 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <limits.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <Eina.h>
+#include <Eet.h>
+#include <Ecore.h>
+#include <Ecore_File.h>
+
+#define EFREET_MODULE_LOG_DOM /* no logging in this file */
+
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "efreet_cache_private.h"
+
+int verbose = 0;
+
+static void
+dump(Efreet_Icon_Theme *theme)
+{
+    Eet_File *ef;
+    unsigned int count = 0;
+    double start, avg;
+    char **keys;
+    int num, i;
+
+    start = ecore_time_get();
+    ef = eet_open(efreet_icon_cache_file(theme->name.internal), EET_FILE_MODE_READ);
+    printf("open: %s %f\n", theme->name.internal, ecore_time_get() - start);
+
+    start = ecore_time_get();
+    keys = eet_list(ef, "*", &num);
+    printf("list: %s %f\n", theme->name.internal, ecore_time_get() - start);
+    if (!keys) return;
+
+    start = ecore_time_get();
+    for (i = 0; i < num; i++)
+    {
+        Efreet_Cache_Icon *icon;
+        unsigned int j;
+
+        icon = eet_data_read(ef, efreet_icon_edd(), keys[i]);
+        if (!icon) continue;
+
+        for (j = 0; j < icon->icons_count; ++j)
+            count += icon->icons[j]->paths_count;
+    }
+    free(keys);
+
+    start = ecore_time_get() - start;
+    avg = start / count;
+    printf("read: %s - %u paths (time: %f) (avg %f)\n", theme->name.internal, count, start, avg);
+    eet_close(ef);
+    eet_clearcache();
+}
+
+int
+main(int argc, char **argv)
+{
+    Eet_File *theme_ef;
+    Eina_List *l = NULL;
+    Efreet_Icon_Theme *theme;
+    int i;
+
+    efreet_cache_update = 0;
+
+    if (!efreet_init()) return -1;
+
+    theme_ef = eet_open(efreet_icon_theme_cache_file(), EET_FILE_MODE_READ);
+    if (!theme_ef) return -1;
+
+    if (argc > 1)
+    {
+        for (i = 1; i < argc; i++)
+        {
+            theme = eet_data_read(theme_ef, efreet_icon_theme_edd(EINA_FALSE), argv[i]);
+            if (theme) l = eina_list_append(l, theme);
+        }
+    }
+    else
+    {
+        char **keys;
+        int num;
+
+        keys = eet_list(theme_ef, "*", &num);
+        if (keys)
+        {
+            for (i = 0; i < num; i++)
+            {
+                theme = eet_data_read(theme_ef, efreet_icon_theme_edd(EINA_FALSE), keys[i]);
+                if (theme) l = eina_list_append(l, theme);
+            }
+            free(keys);
+        }
+    }
+
+    EINA_LIST_FREE(l, theme)
+    {
+        void *data;
+
+        dump(theme);
+
+        /* free theme */
+        eina_list_free(theme->paths);
+        eina_list_free(theme->inherits);
+        EINA_LIST_FREE(theme->directories, data)
+            free(data);
+        free(theme);
+    }
+
+    efreet_shutdown();
+    return 0;
+}
diff --git a/src/tests/efreet_spec_test.c b/src/tests/efreet_spec_test.c
new file mode 100644 (file)
index 0000000..7c414b5
--- /dev/null
@@ -0,0 +1,57 @@
+#include <Efreet.h>
+#include <stdio.h>
+#include <limits.h>
+#include "ef_test.h"
+
+static void dump(Efreet_Menu *menu, const char *path);
+
+int
+main(int argc __UNUSED__, char **argv __UNUSED__)
+{
+    Efreet_Menu *menu;
+
+    if (!efreet_init())
+    {
+        fprintf(stderr, "Failed to init Efreet\n");
+        return 1;
+    }
+
+    menu = efreet_menu_get();
+    if (!menu)
+    {
+        fprintf(stderr, "Failed to read menu\n");
+        return 1;
+    }
+
+    dump(menu, "");
+
+    efreet_menu_free(menu);
+    efreet_shutdown();
+    return 0;
+}
+
+static void
+dump(Efreet_Menu *menu, const char *path)
+{
+    Efreet_Menu *entry;
+    Eina_List *l;
+
+    if (!menu || !menu->entries) return;
+
+    EINA_LIST_FOREACH(menu->entries, l, entry)
+    {
+        if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
+        {
+            if (!path || !*path) path = "/";
+            printf("%s\t%s\t%s\n", path, entry->id,
+                                    entry->desktop->orig_path);
+        }
+        else if (entry->type == EFREET_MENU_ENTRY_MENU)
+        {
+            char new_path[PATH_MAX];
+
+            snprintf(new_path, sizeof(new_path), "%s%s/", path, entry->name);
+            dump(entry, new_path);
+        }
+    }
+}
diff --git a/src/tests/efreet_suite.c b/src/tests/efreet_suite.c
new file mode 100644 (file)
index 0000000..7424435
--- /dev/null
@@ -0,0 +1,103 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <Efreet.h>
+
+#include "efreet_suite.h"
+
+typedef struct _Efreet_Test_Case Efreet_Test_Case;
+
+struct _Efreet_Test_Case
+{
+   const char *test_case;
+   void      (*build)(TCase *tc);
+};
+
+static const Efreet_Test_Case etc[] = {
+  { "Efreet", efreet_test_efreet },
+  { "Efreet Cache", efreet_test_efreet_cache },
+  { NULL, NULL }
+};
+
+static void
+_list_tests(void)
+{
+  const Efreet_Test_Case *itr;
+
+   itr = etc;
+   fputs("Available Test Cases:\n", stderr);
+   for (; itr->test_case; itr++)
+     fprintf(stderr, "\t%s\n", itr->test_case);
+}
+
+static Eina_Bool
+_use_test(int argc, const char **argv, const char *test_case)
+{
+   if (argc < 1)
+     return 1;
+
+   for (; argc > 0; argc--, argv++)
+     if (strcmp(test_case, *argv) == 0)
+       return 1;
+   return 0;
+}
+
+static Suite *
+efreet_suite_build(int argc, const char **argv)
+{
+   TCase *tc;
+   Suite *s;
+   int i;
+
+   s = suite_create("Efreet");
+
+   for (i = 0; etc[i].test_case; ++i)
+     {
+       if (!_use_test(argc, argv, etc[i].test_case)) continue;
+       tc = tcase_create(etc[i].test_case);
+
+       etc[i].build(tc);
+
+       suite_add_tcase(s, tc);
+       tcase_set_timeout(tc, 0);
+     }
+
+   return s;
+}
+
+int
+main(int argc, char **argv)
+{
+   Suite *s;
+   SRunner *sr;
+   int i, failed_count;
+
+   for (i = 1; i < argc; i++)
+     if ((strcmp(argv[i], "-h") == 0) ||
+        (strcmp(argv[i], "--help") == 0))
+       {
+         fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n",
+                 argv[0]);
+         _list_tests();
+         return 0;
+       }
+     else if ((strcmp(argv[i], "-l") == 0) ||
+             (strcmp(argv[i], "--list") == 0))
+       {
+         _list_tests();
+         return 0;
+       }
+
+   s = efreet_suite_build(argc - 1, (const char **)argv + 1);
+   sr = srunner_create(s);
+
+   srunner_run_all(sr, CK_NORMAL);
+   failed_count = srunner_ntests_failed(sr);
+   srunner_free(sr);
+
+   return (failed_count == 0) ? 0 : 255;
+}
diff --git a/src/tests/efreet_suite.h b/src/tests/efreet_suite.h
new file mode 100644 (file)
index 0000000..bfb47f9
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _EFREET_SUITE_H
+#define _EFREET_SUITE_H
+
+#include <check.h>
+
+void efreet_test_efreet(TCase *tc);
+void efreet_test_efreet_cache(TCase *tc);
+
+
+#endif /* _EFREET_SUITE_H */
diff --git a/src/tests/efreet_test_efreet.c b/src/tests/efreet_test_efreet.c
new file mode 100644 (file)
index 0000000..d35ec9e
--- /dev/null
@@ -0,0 +1,25 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Efreet.h>
+
+#include "efreet_suite.h"
+
+
+START_TEST(efreet_test_efreet_init)
+{
+   int ret;
+
+   ret = efreet_init();
+   fail_if(ret != 1);
+
+   ret = efreet_shutdown();
+   fail_if(ret != 0);
+}
+END_TEST
+
+void efreet_test_efreet(TCase *tc)
+{
+   tcase_add_test(tc, efreet_test_efreet_init);
+}
diff --git a/src/tests/efreet_test_efreet_cache.c b/src/tests/efreet_test_efreet_cache.c
new file mode 100644 (file)
index 0000000..2f8972d
--- /dev/null
@@ -0,0 +1,25 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Efreet.h>
+
+#include "efreet_suite.h"
+
+
+START_TEST(efreet_test_efreet_cache_init)
+{
+   int ret;
+
+   ret = efreet_init();
+   fail_if(ret != 1);
+
+   ret = efreet_shutdown();
+   fail_if(ret != 0);
+}
+END_TEST
+
+void efreet_test_efreet_cache(TCase *tc)
+{
+   tcase_add_test(tc, efreet_test_efreet_cache_init);
+}
diff --git a/src/tests/main.c b/src/tests/main.c
new file mode 100644 (file)
index 0000000..de6cf37
--- /dev/null
@@ -0,0 +1,188 @@
+#include "Efreet.h"
+/* no logging */
+#define EFREET_MODULE_LOG_DOM
+#include "efreet_private.h"
+#include "Efreet_Mime.h"
+#include "config.h"
+#include <Ecore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int ef_cb_efreet_data_home(void);
+int ef_cb_efreet_config_home(void);
+int ef_cb_efreet_cache_home(void);
+int ef_cb_efreet_data_dirs(void);
+int ef_cb_efreet_config_dirs(void);
+int ef_cb_efreet_icon_theme(void);
+int ef_cb_efreet_icon_theme_list(void);
+int ef_cb_efreet_icon_match(void);
+int ef_cb_ini_parse(void);
+int ef_cb_ini_long_line(void);
+int ef_cb_ini_garbage(void);
+#if DEFAULT_VISIBILITY
+int ef_cb_locale(void);
+#endif
+int ef_cb_desktop_parse(void);
+int ef_cb_desktop_save(void);
+int ef_cb_desktop_command_get(void);
+int ef_cb_desktop_type_parse(void);
+#if 0
+int ef_cb_desktop_file_id(void);
+#endif
+int ef_cb_menu_get(void);
+int ef_cb_menu_with_slashes(void);
+int ef_cb_menu_save(void);
+#if 0
+int ef_cb_menu_edit(void);
+#endif
+int ef_cb_utils(void);
+int ef_mime_cb_get(void);
+
+typedef struct Efreet_Test Efreet_Test;
+struct Efreet_Test
+{
+    char *name;
+    int (*cb)(void);
+};
+
+static Efreet_Test tests[] = {
+    {"Data Home", ef_cb_efreet_data_home},
+    {"Config Home", ef_cb_efreet_config_home},
+    {"Cache Home", ef_cb_efreet_cache_home},
+    {"Data Directories", ef_cb_efreet_data_dirs},
+    {"Config Directories", ef_cb_efreet_config_dirs},
+    {"Icon Theme Basic", ef_cb_efreet_icon_theme},
+    {"Icon Theme List", ef_cb_efreet_icon_theme_list},
+    {"Icon Matching", ef_cb_efreet_icon_match},
+    {"INI Parsing", ef_cb_ini_parse},
+    {"INI Long Line Parsing", ef_cb_ini_long_line},
+    {"INI Garbage Parsing", ef_cb_ini_garbage},
+#if DEFAULT_VISIBILITY
+    {"Locale Parsing", ef_cb_locale},
+#endif
+    {"Desktop Parsing", ef_cb_desktop_parse},
+    {"Desktop Type Parsing", ef_cb_desktop_type_parse},
+    {"Desktop Save", ef_cb_desktop_save},
+    {"Desktop Command", ef_cb_desktop_command_get},
+#if 0
+    {"Desktop File ID", ef_cb_desktop_file_id},
+#endif
+    {"Menu Parsing", ef_cb_menu_get},
+    {"Menu Incorrect Names", ef_cb_menu_with_slashes},
+    {"Menu Save", ef_cb_menu_save},
+#if 0
+    {"Menu Edit", ef_cb_menu_edit},
+#endif
+    {"Utils", ef_cb_utils},
+    {"Mime", ef_mime_cb_get},
+    {NULL, NULL}
+};
+
+extern char **environ;
+static Eina_List *environment = NULL;
+
+void
+environment_store(void)
+{
+    char *env;
+    char **e;
+#ifdef HAVE_CLEARENV
+    EINA_LIST_FREE(environment, env)
+        free(env);
+    for (e = environ; *e; e++)
+        environment = eina_list_append(environment, strdup(*e));
+#endif   
+}
+
+void
+environment_restore(void)
+{
+    Eina_List *l;
+    char *e;
+    if (!environment) return;
+#ifdef HAVE_CLEARENV
+    clearenv();
+    EINA_LIST_FOREACH(environment, l, e)
+        putenv(e);
+#endif
+}
+
+int
+main(int argc, char ** argv)
+{
+    int i, passed = 0, num_tests = 0;
+    Eina_List *run = NULL;
+    double total;
+    char *env;
+
+    eina_init();
+    ecore_init();
+
+    total = ecore_time_get();
+    if (argc > 1)
+    {
+        for (i = 1; i < argc; i++)
+        {
+            if ((!strcmp(argv[i], "-h")) ||
+                (!strcmp(argv[i], "--help")))
+            {
+                for (i = 0; tests[i].name; i++)
+                {
+                    printf("%s\n", tests[i].name);
+                }
+                return 1;
+            }
+            run = eina_list_append(run, argv[i]);
+        }
+    }
+
+    efreet_cache_update = 0;
+    environment_store();
+    for (i = 0; tests[i].name; i++)
+    {
+        int ret;
+        double start;
+
+        /* we've been given specific tests and it isn't in the list */
+        if (run && !eina_list_search_unsorted(run, EINA_COMPARE_CB(strcasecmp),
+                                                        tests[i].name))
+            continue;
+
+        if (!efreet_init())
+        {
+            printf("Error initializing Efreet\n");
+            continue;
+        }
+
+        num_tests ++;
+
+        printf("%s:\t\t", tests[i].name);
+        fflush(stdout);
+        start = ecore_time_get();
+        ret = tests[i].cb();
+        printf("%s in %.3f seconds\n", (ret ? "PASSED" : "FAILED"),
+                                            ecore_time_get() - start);
+        passed += ret;
+
+        efreet_shutdown();
+        environment_restore();
+    }
+
+    printf("\n-----------------\n");
+#ifdef HAVE_CLEARENV
+    clearenv();
+    EINA_LIST_FREE(environment, env)
+        free(env);
+#endif    
+    printf("Passed %d of %d tests.\n", passed, num_tests);
+
+    while (run)
+        run = eina_list_remove_list(run, run);
+
+    printf("Total run: %.3f seconds\n", ecore_time_get() - total);
+
+    ecore_shutdown();
+    eina_shutdown();
+    return 0;
+}