summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/Makefile.in618
-rw-r--r--src/bin/Makefile.am29
-rw-r--r--src/bin/Makefile.in622
-rw-r--r--src/bin/efreet_desktop_cache_create.c541
-rw-r--r--src/bin/efreet_icon_cache_create.c1131
-rw-r--r--src/lib/Efreet.h107
-rw-r--r--src/lib/Efreet_Mime.h125
-rw-r--r--src/lib/Efreet_Trash.h102
-rw-r--r--src/lib/Makefile.am69
-rw-r--r--src/lib/Makefile.in821
-rw-r--r--src/lib/efreet.c366
-rw-r--r--src/lib/efreet_base.c387
-rw-r--r--src/lib/efreet_base.h86
-rw-r--r--src/lib/efreet_cache.c1396
-rw-r--r--src/lib/efreet_cache_private.h63
-rw-r--r--src/lib/efreet_desktop.c1085
-rw-r--r--src/lib/efreet_desktop.h370
-rw-r--r--src/lib/efreet_desktop_command.c919
-rw-r--r--src/lib/efreet_icon.c910
-rw-r--r--src/lib/efreet_icon.h249
-rw-r--r--src/lib/efreet_ini.c629
-rw-r--r--src/lib/efreet_ini.h181
-rw-r--r--src/lib/efreet_menu.c3887
-rw-r--r--src/lib/efreet_menu.h138
-rw-r--r--src/lib/efreet_mime.c1622
-rw-r--r--src/lib/efreet_private.h227
-rw-r--r--src/lib/efreet_trash.c288
-rw-r--r--src/lib/efreet_uri.c119
-rw-r--r--src/lib/efreet_uri.h62
-rw-r--r--src/lib/efreet_utils.c488
-rw-r--r--src/lib/efreet_utils.h157
-rw-r--r--src/lib/efreet_xml.c609
-rw-r--r--src/lib/efreet_xml.h59
-rw-r--r--src/tests/Makefile.am66
-rw-r--r--src/tests/Makefile.in847
-rw-r--r--src/tests/compare/Makefile.am15
-rw-r--r--src/tests/compare/Makefile.in598
-rw-r--r--src/tests/compare/comp.h527
-rw-r--r--src/tests/compare/efreet_alloc.c30
-rw-r--r--src/tests/compare/efreet_menu_alloc.c23
-rw-r--r--src/tests/data/Makefile.am18
-rw-r--r--src/tests/data/Makefile.in679
-rw-r--r--src/tests/data/entrybin0 -> 647 bytes
-rw-r--r--src/tests/data/entry.pngbin0 -> 648 bytes
-rw-r--r--src/tests/data/long.ini3
-rw-r--r--src/tests/data/preferences.menu41
-rw-r--r--src/tests/data/sub/Makefile.am8
-rw-r--r--src/tests/data/sub/Makefile.in468
-rw-r--r--src/tests/data/sub/test.desktop5
-rw-r--r--src/tests/data/test.desktop8
-rw-r--r--src/tests/data/test.ini21
-rw-r--r--src/tests/data/test.menu52
-rw-r--r--src/tests/data/test_garbage2341
-rw-r--r--src/tests/data/test_menu_slash_bad.menu11
-rw-r--r--src/tests/data/test_type.desktop8
-rw-r--r--src/tests/ef_cache.c199
-rw-r--r--src/tests/ef_data_dirs.c330
-rw-r--r--src/tests/ef_desktop.c401
-rw-r--r--src/tests/ef_icon_theme.c605
-rw-r--r--src/tests/ef_ini.c174
-rw-r--r--src/tests/ef_locale.c85
-rw-r--r--src/tests/ef_menu.c150
-rw-r--r--src/tests/ef_mime.c57
-rw-r--r--src/tests/ef_test.h11
-rw-r--r--src/tests/ef_utils.c28
-rw-r--r--src/tests/efreet_icon_cache_dump.c120
-rw-r--r--src/tests/efreet_spec_test.c57
-rw-r--r--src/tests/efreet_suite.c103
-rw-r--r--src/tests/efreet_test_efreet.c25
-rw-r--r--src/tests/efreet_test_efreet_cache.c25
-rw-r--r--src/tests/main.c188
72 files changed, 26793 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..add035f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,4 @@
+
+SUBDIRS = lib bin tests
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..e07c928
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,618 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+SUBDIRS = lib bin tests
+MAINTAINERCLEANFILES = Makefile.in
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
+ install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-generic clean-libtool \
+ ctags ctags-recursive distclean distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+ uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
new file mode 100644
index 0000000..3040a20
--- /dev/null
+++ b/src/bin/Makefile.am
@@ -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/Makefile.in b/src/bin/Makefile.in
new file mode 100644
index 0000000..bd9ba1f
--- /dev/null
+++ b/src/bin/Makefile.in
@@ -0,0 +1,622 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+internal_bin_PROGRAMS = efreet_desktop_cache_create$(EXEEXT) \
+ efreet_icon_cache_create$(EXEEXT)
+subdir = src/bin
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(internal_bindir)"
+PROGRAMS = $(internal_bin_PROGRAMS)
+am_efreet_desktop_cache_create_OBJECTS = \
+ efreet_desktop_cache_create.$(OBJEXT)
+efreet_desktop_cache_create_OBJECTS = \
+ $(am_efreet_desktop_cache_create_OBJECTS)
+efreet_desktop_cache_create_DEPENDENCIES = \
+ $(top_builddir)/src/lib/libefreet.la
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am_efreet_icon_cache_create_OBJECTS = \
+ efreet_icon_cache_create.$(OBJEXT)
+efreet_icon_cache_create_OBJECTS = \
+ $(am_efreet_icon_cache_create_OBJECTS)
+efreet_icon_cache_create_DEPENDENCIES = \
+ $(top_builddir)/src/lib/libefreet.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(efreet_desktop_cache_create_SOURCES) \
+ $(efreet_icon_cache_create_SOURCES)
+DIST_SOURCES = $(efreet_desktop_cache_create_SOURCES) \
+ $(efreet_icon_cache_create_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+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
+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
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/bin/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/bin/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-internal_binPROGRAMS: $(internal_bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(internal_bindir)" || $(MKDIR_P) "$(DESTDIR)$(internal_bindir)"
+ @list='$(internal_bin_PROGRAMS)'; test -n "$(internal_bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(internal_bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(internal_bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-internal_binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(internal_bin_PROGRAMS)'; test -n "$(internal_bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(internal_bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(internal_bindir)" && rm -f $$files
+
+clean-internal_binPROGRAMS:
+ @list='$(internal_bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+efreet_desktop_cache_create$(EXEEXT): $(efreet_desktop_cache_create_OBJECTS) $(efreet_desktop_cache_create_DEPENDENCIES)
+ @rm -f efreet_desktop_cache_create$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_desktop_cache_create_OBJECTS) $(efreet_desktop_cache_create_LDADD) $(LIBS)
+efreet_icon_cache_create$(EXEEXT): $(efreet_icon_cache_create_OBJECTS) $(efreet_icon_cache_create_DEPENDENCIES)
+ @rm -f efreet_icon_cache_create$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_icon_cache_create_OBJECTS) $(efreet_icon_cache_create_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_desktop_cache_create.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_icon_cache_create.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(internal_bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-internal_binPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-internal_binPROGRAMS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-internal_binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-internal_binPROGRAMS clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-internal_binPROGRAMS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-internal_binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/bin/efreet_desktop_cache_create.c b/src/bin/efreet_desktop_cache_create.c
new file mode 100644
index 0000000..c397022
--- /dev/null
+++ b/src/bin/efreet_desktop_cache_create.c
@@ -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
index 0000000..e8d100c
--- /dev/null
+++ b/src/bin/efreet_icon_cache_create.c
@@ -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
index 0000000..c5b8104
--- /dev/null
+++ b/src/lib/Efreet.h
@@ -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
index 0000000..d6873fc
--- /dev/null
+++ b/src/lib/Efreet_Mime.h
@@ -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
index 0000000..3f86aa2
--- /dev/null
+++ b/src/lib/Efreet_Trash.h
@@ -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
index 0000000..824c8de
--- /dev/null
+++ b/src/lib/Makefile.am
@@ -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/Makefile.in b/src/lib/Makefile.in
new file mode 100644
index 0000000..b3ef936
--- /dev/null
+++ b/src/lib/Makefile.in
@@ -0,0 +1,821 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib
+DIST_COMMON = $(includes_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includesdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libefreet_la_DEPENDENCIES =
+am__objects_1 = libefreet_la-efreet.lo libefreet_la-efreet_base.lo \
+ libefreet_la-efreet_icon.lo libefreet_la-efreet_xml.lo \
+ libefreet_la-efreet_ini.lo libefreet_la-efreet_desktop.lo \
+ libefreet_la-efreet_desktop_command.lo \
+ libefreet_la-efreet_menu.lo libefreet_la-efreet_utils.lo \
+ libefreet_la-efreet_uri.lo libefreet_la-efreet_cache.lo
+am_libefreet_la_OBJECTS = $(am__objects_1)
+libefreet_la_OBJECTS = $(am_libefreet_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libefreet_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libefreet_la_LDFLAGS) $(LDFLAGS) -o $@
+libefreet_mime_la_DEPENDENCIES = libefreet.la
+am_libefreet_mime_la_OBJECTS = libefreet_mime_la-efreet_mime.lo
+libefreet_mime_la_OBJECTS = $(am_libefreet_mime_la_OBJECTS)
+libefreet_mime_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libefreet_mime_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libefreet_trash_la_DEPENDENCIES = libefreet.la
+am_libefreet_trash_la_OBJECTS = libefreet_trash_la-efreet_trash.lo
+libefreet_trash_la_OBJECTS = $(am_libefreet_trash_la_OBJECTS)
+libefreet_trash_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libefreet_trash_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libefreet_la_SOURCES) $(libefreet_mime_la_SOURCES) \
+ $(libefreet_trash_la_SOURCES)
+DIST_SOURCES = $(libefreet_la_SOURCES) $(libefreet_mime_la_SOURCES) \
+ $(libefreet_trash_la_SOURCES)
+HEADERS = $(includes_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+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
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/lib/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/lib/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libefreet.la: $(libefreet_la_OBJECTS) $(libefreet_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libefreet_la_LINK) -rpath $(libdir) $(libefreet_la_OBJECTS) $(libefreet_la_LIBADD) $(LIBS)
+libefreet_mime.la: $(libefreet_mime_la_OBJECTS) $(libefreet_mime_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libefreet_mime_la_LINK) -rpath $(libdir) $(libefreet_mime_la_OBJECTS) $(libefreet_mime_la_LIBADD) $(LIBS)
+libefreet_trash.la: $(libefreet_trash_la_OBJECTS) $(libefreet_trash_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libefreet_trash_la_LINK) -rpath $(libdir) $(libefreet_trash_la_OBJECTS) $(libefreet_trash_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_base.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_cache.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_desktop.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_desktop_command.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_icon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_ini.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_menu.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_uri.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_la-efreet_xml.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_mime_la-efreet_mime.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libefreet_trash_la-efreet_trash.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+libefreet_la-efreet.lo: efreet.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet.Tpo -c -o libefreet_la-efreet.lo `test -f 'efreet.c' || echo '$(srcdir)/'`efreet.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet.Tpo $(DEPDIR)/libefreet_la-efreet.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet.c' object='libefreet_la-efreet.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet.lo `test -f 'efreet.c' || echo '$(srcdir)/'`efreet.c
+
+libefreet_la-efreet_base.lo: efreet_base.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_base.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_base.Tpo -c -o libefreet_la-efreet_base.lo `test -f 'efreet_base.c' || echo '$(srcdir)/'`efreet_base.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_base.Tpo $(DEPDIR)/libefreet_la-efreet_base.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_base.c' object='libefreet_la-efreet_base.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_base.lo `test -f 'efreet_base.c' || echo '$(srcdir)/'`efreet_base.c
+
+libefreet_la-efreet_icon.lo: efreet_icon.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_icon.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_icon.Tpo -c -o libefreet_la-efreet_icon.lo `test -f 'efreet_icon.c' || echo '$(srcdir)/'`efreet_icon.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_icon.Tpo $(DEPDIR)/libefreet_la-efreet_icon.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_icon.c' object='libefreet_la-efreet_icon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_icon.lo `test -f 'efreet_icon.c' || echo '$(srcdir)/'`efreet_icon.c
+
+libefreet_la-efreet_xml.lo: efreet_xml.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_xml.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_xml.Tpo -c -o libefreet_la-efreet_xml.lo `test -f 'efreet_xml.c' || echo '$(srcdir)/'`efreet_xml.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_xml.Tpo $(DEPDIR)/libefreet_la-efreet_xml.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_xml.c' object='libefreet_la-efreet_xml.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_xml.lo `test -f 'efreet_xml.c' || echo '$(srcdir)/'`efreet_xml.c
+
+libefreet_la-efreet_ini.lo: efreet_ini.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_ini.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_ini.Tpo -c -o libefreet_la-efreet_ini.lo `test -f 'efreet_ini.c' || echo '$(srcdir)/'`efreet_ini.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_ini.Tpo $(DEPDIR)/libefreet_la-efreet_ini.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_ini.c' object='libefreet_la-efreet_ini.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_ini.lo `test -f 'efreet_ini.c' || echo '$(srcdir)/'`efreet_ini.c
+
+libefreet_la-efreet_desktop.lo: efreet_desktop.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_desktop.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_desktop.Tpo -c -o libefreet_la-efreet_desktop.lo `test -f 'efreet_desktop.c' || echo '$(srcdir)/'`efreet_desktop.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_desktop.Tpo $(DEPDIR)/libefreet_la-efreet_desktop.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_desktop.c' object='libefreet_la-efreet_desktop.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_desktop.lo `test -f 'efreet_desktop.c' || echo '$(srcdir)/'`efreet_desktop.c
+
+libefreet_la-efreet_desktop_command.lo: efreet_desktop_command.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_desktop_command.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_desktop_command.Tpo -c -o libefreet_la-efreet_desktop_command.lo `test -f 'efreet_desktop_command.c' || echo '$(srcdir)/'`efreet_desktop_command.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_desktop_command.Tpo $(DEPDIR)/libefreet_la-efreet_desktop_command.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_desktop_command.c' object='libefreet_la-efreet_desktop_command.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_desktop_command.lo `test -f 'efreet_desktop_command.c' || echo '$(srcdir)/'`efreet_desktop_command.c
+
+libefreet_la-efreet_menu.lo: efreet_menu.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_menu.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_menu.Tpo -c -o libefreet_la-efreet_menu.lo `test -f 'efreet_menu.c' || echo '$(srcdir)/'`efreet_menu.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_menu.Tpo $(DEPDIR)/libefreet_la-efreet_menu.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_menu.c' object='libefreet_la-efreet_menu.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_menu.lo `test -f 'efreet_menu.c' || echo '$(srcdir)/'`efreet_menu.c
+
+libefreet_la-efreet_utils.lo: efreet_utils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_utils.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_utils.Tpo -c -o libefreet_la-efreet_utils.lo `test -f 'efreet_utils.c' || echo '$(srcdir)/'`efreet_utils.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_utils.Tpo $(DEPDIR)/libefreet_la-efreet_utils.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_utils.c' object='libefreet_la-efreet_utils.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_utils.lo `test -f 'efreet_utils.c' || echo '$(srcdir)/'`efreet_utils.c
+
+libefreet_la-efreet_uri.lo: efreet_uri.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_uri.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_uri.Tpo -c -o libefreet_la-efreet_uri.lo `test -f 'efreet_uri.c' || echo '$(srcdir)/'`efreet_uri.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_uri.Tpo $(DEPDIR)/libefreet_la-efreet_uri.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_uri.c' object='libefreet_la-efreet_uri.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_uri.lo `test -f 'efreet_uri.c' || echo '$(srcdir)/'`efreet_uri.c
+
+libefreet_la-efreet_cache.lo: efreet_cache.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_la-efreet_cache.lo -MD -MP -MF $(DEPDIR)/libefreet_la-efreet_cache.Tpo -c -o libefreet_la-efreet_cache.lo `test -f 'efreet_cache.c' || echo '$(srcdir)/'`efreet_cache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_la-efreet_cache.Tpo $(DEPDIR)/libefreet_la-efreet_cache.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_cache.c' object='libefreet_la-efreet_cache.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_la-efreet_cache.lo `test -f 'efreet_cache.c' || echo '$(srcdir)/'`efreet_cache.c
+
+libefreet_mime_la-efreet_mime.lo: efreet_mime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_mime_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_mime_la-efreet_mime.lo -MD -MP -MF $(DEPDIR)/libefreet_mime_la-efreet_mime.Tpo -c -o libefreet_mime_la-efreet_mime.lo `test -f 'efreet_mime.c' || echo '$(srcdir)/'`efreet_mime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_mime_la-efreet_mime.Tpo $(DEPDIR)/libefreet_mime_la-efreet_mime.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_mime.c' object='libefreet_mime_la-efreet_mime.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_mime_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_mime_la-efreet_mime.lo `test -f 'efreet_mime.c' || echo '$(srcdir)/'`efreet_mime.c
+
+libefreet_trash_la-efreet_trash.lo: efreet_trash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_trash_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libefreet_trash_la-efreet_trash.lo -MD -MP -MF $(DEPDIR)/libefreet_trash_la-efreet_trash.Tpo -c -o libefreet_trash_la-efreet_trash.lo `test -f 'efreet_trash.c' || echo '$(srcdir)/'`efreet_trash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libefreet_trash_la-efreet_trash.Tpo $(DEPDIR)/libefreet_trash_la-efreet_trash.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='efreet_trash.c' object='libefreet_trash_la-efreet_trash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libefreet_trash_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libefreet_trash_la-efreet_trash.lo `test -f 'efreet_trash.c' || echo '$(srcdir)/'`efreet_trash.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-includesHEADERS: $(includes_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includesdir)" || $(MKDIR_P) "$(DESTDIR)$(includesdir)"
+ @list='$(includes_HEADERS)'; test -n "$(includesdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includesdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includesdir)" || exit $$?; \
+ done
+
+uninstall-includesHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(includes_HEADERS)'; test -n "$(includesdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(includesdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(includesdir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includesdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includesHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includesHEADERS uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-includesHEADERS install-info \
+ install-info-am install-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-includesHEADERS \
+ uninstall-libLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib/efreet.c b/src/lib/efreet.c
new file mode 100644
index 0000000..c48223f
--- /dev/null
+++ b/src/lib/efreet.c
@@ -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
index 0000000..afb5920
--- /dev/null
+++ b/src/lib/efreet_base.c
@@ -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
index 0000000..0eb3d52
--- /dev/null
+++ b/src/lib/efreet_base.h
@@ -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
index 0000000..b3b9855
--- /dev/null
+++ b/src/lib/efreet_cache.c
@@ -0,0 +1,1396 @@
+#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_del(desktop_cache_timer);
+ 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_del(icon_cache_timer);
+ 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
index 0000000..e281e61
--- /dev/null
+++ b/src/lib/efreet_cache_private.h
@@ -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
index 0000000..9293f94
--- /dev/null
+++ b/src/lib/efreet_desktop.c
@@ -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
index 0000000..f7dfe51
--- /dev/null
+++ b/src/lib/efreet_desktop.h
@@ -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
index 0000000..bf97f8f
--- /dev/null
+++ b/src/lib/efreet_desktop_command.c
@@ -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
index 0000000..526e0ec
--- /dev/null
+++ b/src/lib/efreet_icon.c
@@ -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
index 0000000..e6454ad
--- /dev/null
+++ b/src/lib/efreet_icon.h
@@ -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
index 0000000..375fda1
--- /dev/null
+++ b/src/lib/efreet_ini.c
@@ -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
index 0000000..f5b8a9e
--- /dev/null
+++ b/src/lib/efreet_ini.h
@@ -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
index 0000000..e612ba1
--- /dev/null
+++ b/src/lib/efreet_menu.c
@@ -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
index 0000000..8531cd8
--- /dev/null
+++ b/src/lib/efreet_menu.h
@@ -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
index 0000000..9343d90
--- /dev/null
+++ b/src/lib/efreet_mime.c
@@ -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
index 0000000..ac43c4a
--- /dev/null
+++ b/src/lib/efreet_private.h
@@ -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
index 0000000..27cc2b1
--- /dev/null
+++ b/src/lib/efreet_trash.c
@@ -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
index 0000000..20ebe39
--- /dev/null
+++ b/src/lib/efreet_uri.c
@@ -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
index 0000000..32aaeee
--- /dev/null
+++ b/src/lib/efreet_uri.h
@@ -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
index 0000000..dab85ef
--- /dev/null
+++ b/src/lib/efreet_utils.c
@@ -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
index 0000000..6a5e69e
--- /dev/null
+++ b/src/lib/efreet_utils.h
@@ -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
index 0000000..9767d75
--- /dev/null
+++ b/src/lib/efreet_xml.c
@@ -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
index 0000000..77473cc
--- /dev/null
+++ b/src/lib/efreet_xml.h
@@ -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
index 0000000..13c1274
--- /dev/null
+++ b/src/tests/Makefile.am
@@ -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/Makefile.in b/src/tests/Makefile.in
new file mode 100644
index 0000000..b57b113
--- /dev/null
+++ b/src/tests/Makefile.in
@@ -0,0 +1,847 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = efreet_test$(EXEEXT) efreet_spec_test$(EXEEXT) \
+ efreet_cache_test$(EXEEXT) efreet_icon_cache_dump$(EXEEXT)
+@DEFAULT_VISIBILITY_TRUE@am__append_1 = \
+@DEFAULT_VISIBILITY_TRUE@ef_locale.c
+
+@EFL_ENABLE_TESTS_TRUE@check_PROGRAMS = efreet_suite$(EXEEXT)
+subdir = src/tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_efreet_cache_test_OBJECTS = ef_cache.$(OBJEXT)
+efreet_cache_test_OBJECTS = $(am_efreet_cache_test_OBJECTS)
+efreet_cache_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am_efreet_icon_cache_dump_OBJECTS = efreet_icon_cache_dump.$(OBJEXT)
+efreet_icon_cache_dump_OBJECTS = $(am_efreet_icon_cache_dump_OBJECTS)
+efreet_icon_cache_dump_DEPENDENCIES = \
+ $(top_builddir)/src/lib/libefreet.la
+am_efreet_spec_test_OBJECTS = efreet_spec_test.$(OBJEXT)
+efreet_spec_test_OBJECTS = $(am_efreet_spec_test_OBJECTS)
+efreet_spec_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+am__efreet_suite_SOURCES_DIST = efreet_suite.c efreet_test_efreet.c \
+ efreet_test_efreet_cache.c
+@EFL_ENABLE_TESTS_TRUE@am_efreet_suite_OBJECTS = \
+@EFL_ENABLE_TESTS_TRUE@ efreet_suite.$(OBJEXT) \
+@EFL_ENABLE_TESTS_TRUE@ efreet_test_efreet.$(OBJEXT) \
+@EFL_ENABLE_TESTS_TRUE@ efreet_test_efreet_cache.$(OBJEXT)
+efreet_suite_OBJECTS = $(am_efreet_suite_OBJECTS)
+@EFL_ENABLE_TESTS_TRUE@efreet_suite_DEPENDENCIES = \
+@EFL_ENABLE_TESTS_TRUE@ $(top_builddir)/src/lib/libefreet.la
+am__efreet_test_SOURCES_DIST = 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 ef_locale.c
+@DEFAULT_VISIBILITY_TRUE@am__objects_1 = ef_locale.$(OBJEXT)
+am_efreet_test_OBJECTS = ef_data_dirs.$(OBJEXT) \
+ ef_icon_theme.$(OBJEXT) ef_ini.$(OBJEXT) ef_utils.$(OBJEXT) \
+ ef_desktop.$(OBJEXT) ef_menu.$(OBJEXT) ef_mime.$(OBJEXT) \
+ main.$(OBJEXT) $(am__objects_1)
+efreet_test_OBJECTS = $(am_efreet_test_OBJECTS)
+efreet_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la \
+ $(top_builddir)/src/lib/libefreet_mime.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(efreet_cache_test_SOURCES) \
+ $(efreet_icon_cache_dump_SOURCES) $(efreet_spec_test_SOURCES) \
+ $(efreet_suite_SOURCES) $(efreet_test_SOURCES)
+DIST_SOURCES = $(efreet_cache_test_SOURCES) \
+ $(efreet_icon_cache_dump_SOURCES) $(efreet_spec_test_SOURCES) \
+ $(am__efreet_suite_SOURCES_DIST) \
+ $(am__efreet_test_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+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@
+
+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 \
+ $(am__append_1)
+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
+
+@EFL_ENABLE_TESTS_TRUE@efreet_suite_SOURCES = \
+@EFL_ENABLE_TESTS_TRUE@efreet_suite.c \
+@EFL_ENABLE_TESTS_TRUE@efreet_test_efreet.c \
+@EFL_ENABLE_TESTS_TRUE@efreet_test_efreet_cache.c
+
+@EFL_ENABLE_TESTS_TRUE@efreet_suite_LDADD = @CHECK_LIBS@ $(top_builddir)/src/lib/libefreet.la @EFREET_LIBS@
+efreet_icon_cache_dump_LDADD = \
+$(top_builddir)/src/lib/libefreet.la \
+@EFREET_LIBS@
+
+efreet_icon_cache_dump_SOURCES = \
+efreet_icon_cache_dump.c
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+efreet_cache_test$(EXEEXT): $(efreet_cache_test_OBJECTS) $(efreet_cache_test_DEPENDENCIES)
+ @rm -f efreet_cache_test$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_cache_test_OBJECTS) $(efreet_cache_test_LDADD) $(LIBS)
+efreet_icon_cache_dump$(EXEEXT): $(efreet_icon_cache_dump_OBJECTS) $(efreet_icon_cache_dump_DEPENDENCIES)
+ @rm -f efreet_icon_cache_dump$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_icon_cache_dump_OBJECTS) $(efreet_icon_cache_dump_LDADD) $(LIBS)
+efreet_spec_test$(EXEEXT): $(efreet_spec_test_OBJECTS) $(efreet_spec_test_DEPENDENCIES)
+ @rm -f efreet_spec_test$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_spec_test_OBJECTS) $(efreet_spec_test_LDADD) $(LIBS)
+efreet_suite$(EXEEXT): $(efreet_suite_OBJECTS) $(efreet_suite_DEPENDENCIES)
+ @rm -f efreet_suite$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_suite_OBJECTS) $(efreet_suite_LDADD) $(LIBS)
+efreet_test$(EXEEXT): $(efreet_test_OBJECTS) $(efreet_test_DEPENDENCIES)
+ @rm -f efreet_test$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_test_OBJECTS) $(efreet_test_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_cache.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_data_dirs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_desktop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_icon_theme.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_ini.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_locale.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_menu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_mime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ef_utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_icon_cache_dump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_spec_test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_suite.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_test_efreet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_test_efreet_cache.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) check-am \
+ ctags-recursive install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-binPROGRAMS \
+ clean-checkPROGRAMS clean-generic clean-libtool ctags \
+ ctags-recursive distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tests/compare/Makefile.am b/src/tests/compare/Makefile.am
new file mode 100644
index 0000000..bb43591
--- /dev/null
+++ b/src/tests/compare/Makefile.am
@@ -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/Makefile.in b/src/tests/compare/Makefile.in
new file mode 100644
index 0000000..f813d18
--- /dev/null
+++ b/src/tests/compare/Makefile.in
@@ -0,0 +1,598 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = efreet_alloc$(EXEEXT) efreet_menu_alloc$(EXEEXT)
+subdir = src/tests/compare
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_efreet_alloc_OBJECTS = efreet_alloc.$(OBJEXT)
+efreet_alloc_OBJECTS = $(am_efreet_alloc_OBJECTS)
+efreet_alloc_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am_efreet_menu_alloc_OBJECTS = efreet_menu_alloc.$(OBJEXT)
+efreet_menu_alloc_OBJECTS = $(am_efreet_menu_alloc_OBJECTS)
+efreet_menu_alloc_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(efreet_alloc_SOURCES) $(efreet_menu_alloc_SOURCES)
+DIST_SOURCES = $(efreet_alloc_SOURCES) $(efreet_menu_alloc_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+MAINTAINERCLEANFILES = Makefile.in
+AM_CPPFLAGS = \
+-I. \
+-I$(top_srcdir)/src/lib \
+@EFREET_CFLAGS@
+
+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@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/tests/compare/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/tests/compare/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+efreet_alloc$(EXEEXT): $(efreet_alloc_OBJECTS) $(efreet_alloc_DEPENDENCIES)
+ @rm -f efreet_alloc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_alloc_OBJECTS) $(efreet_alloc_LDADD) $(LIBS)
+efreet_menu_alloc$(EXEEXT): $(efreet_menu_alloc_OBJECTS) $(efreet_menu_alloc_DEPENDENCIES)
+ @rm -f efreet_menu_alloc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(efreet_menu_alloc_OBJECTS) $(efreet_menu_alloc_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_alloc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efreet_menu_alloc.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tests/compare/comp.h b/src/tests/compare/comp.h
new file mode 100644
index 0000000..c02eebe
--- /dev/null
+++ b/src/tests/compare/comp.h
@@ -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
index 0000000..1527f77
--- /dev/null
+++ b/src/tests/compare/efreet_alloc.c
@@ -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
index 0000000..fcfb75b
--- /dev/null
+++ b/src/tests/compare/efreet_menu_alloc.c
@@ -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
index 0000000..50eef85
--- /dev/null
+++ b/src/tests/data/Makefile.am
@@ -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/Makefile.in b/src/tests/data/Makefile.in
new file mode 100644
index 0000000..7bf1261
--- /dev/null
+++ b/src/tests/data/Makefile.in
@@ -0,0 +1,679 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/tests/data
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(testdir)"
+DATA = $(test_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+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)
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/tests/data/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/tests/data/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-testDATA: $(test_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(testdir)" || $(MKDIR_P) "$(DESTDIR)$(testdir)"
+ @list='$(test_DATA)'; test -n "$(testdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(testdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(testdir)" || exit $$?; \
+ done
+
+uninstall-testDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(test_DATA)'; test -n "$(testdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(testdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(testdir)" && rm -f $$files
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(DATA)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(testdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-testDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-testDATA
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
+ install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-generic clean-libtool \
+ ctags ctags-recursive distclean distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip install-testDATA \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am uninstall-testDATA
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tests/data/entry b/src/tests/data/entry
new file mode 100644
index 0000000..503d8e5
--- /dev/null
+++ b/src/tests/data/entry
Binary files differ
diff --git a/src/tests/data/entry.png b/src/tests/data/entry.png
new file mode 100644
index 0000000..82e5cbe
--- /dev/null
+++ b/src/tests/data/entry.png
Binary files differ
diff --git a/src/tests/data/long.ini b/src/tests/data/long.ini
new file mode 100644
index 0000000..32154dd
--- /dev/null
+++ b/src/tests/data/long.ini
@@ -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
index 0000000..904dbd2
--- /dev/null
+++ b/src/tests/data/preferences.menu
@@ -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
index 0000000..7aaf7fc
--- /dev/null
+++ b/src/tests/data/sub/Makefile.am
@@ -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/Makefile.in b/src/tests/data/sub/Makefile.in
new file mode 100644
index 0000000..340e1ea
--- /dev/null
+++ b/src/tests/data/sub/Makefile.in
@@ -0,0 +1,468 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/tests/data/sub
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ac_attribute.m4 \
+ $(top_srcdir)/m4/efl_compiler_flag.m4 \
+ $(top_srcdir)/m4/efl_coverage.m4 \
+ $(top_srcdir)/m4/efl_doxygen.m4 \
+ $(top_srcdir)/m4/efl_path_max.m4 $(top_srcdir)/m4/efl_tests.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(testdir)"
+DATA = $(test_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EFL_COVERAGE_CFLAGS = @EFL_COVERAGE_CFLAGS@
+EFL_COVERAGE_LIBS = @EFL_COVERAGE_LIBS@
+EFL_EFREET_BUILD = @EFL_EFREET_BUILD@
+EFL_EFREET_MIME_BUILD = @EFL_EFREET_MIME_BUILD@
+EFL_EFREET_TRASH_BUILD = @EFL_EFREET_TRASH_BUILD@
+EFREET_CFLAGS = @EFREET_CFLAGS@
+EFREET_LIBS = @EFREET_LIBS@
+EGREP = @EGREP@
+EINA_CFLAGS = @EINA_CFLAGS@
+EINA_LIBS = @EINA_LIBS@
+EVIL_CFLAGS = @EVIL_CFLAGS@
+EVIL_LIBS = @EVIL_LIBS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALE_DIR = @LOCALE_DIR@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VMAJ = @VMAJ@
+WIN32_LIBS = @WIN32_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+efl_doxygen = @efl_doxygen@
+efl_have_doxygen = @efl_have_doxygen@
+exec_prefix = @exec_prefix@
+have_lcov = @have_lcov@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+lt_enable_auto_import = @lt_enable_auto_import@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfig_requires_private = @pkgconfig_requires_private@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+release_info = @release_info@
+requirement_efreet = @requirement_efreet@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+version_info = @version_info@
+MAINTAINERCLEANFILES = Makefile.in
+testdir = $(pkgdatadir)/test/sub
+test_DATA = \
+test.desktop
+
+EXTRA_DIST = $(test_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/tests/data/sub/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/tests/data/sub/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-testDATA: $(test_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(testdir)" || $(MKDIR_P) "$(DESTDIR)$(testdir)"
+ @list='$(test_DATA)'; test -n "$(testdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(testdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(testdir)" || exit $$?; \
+ done
+
+uninstall-testDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(test_DATA)'; test -n "$(testdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(testdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(testdir)" && rm -f $$files
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(testdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-testDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-testDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ distclean distclean-generic distclean-libtool distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ install-testDATA installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ uninstall uninstall-am uninstall-testDATA
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tests/data/sub/test.desktop b/src/tests/data/sub/test.desktop
new file mode 100644
index 0000000..7abf2ae
--- /dev/null
+++ b/src/tests/data/sub/test.desktop
@@ -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
index 0000000..412601f
--- /dev/null
+++ b/src/tests/data/test.desktop
@@ -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
index 0000000..7f73d81
--- /dev/null
+++ b/src/tests/data/test.ini
@@ -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
index 0000000..7ae21ff
--- /dev/null
+++ b/src/tests/data/test.menu
@@ -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
index 0000000..7d178fd
--- /dev/null
+++ b/src/tests/data/test_garbage
@@ -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_slash_bad.menu b/src/tests/data/test_menu_slash_bad.menu
new file mode 100644
index 0000000..8f7b6f0
--- /dev/null
+++ b/src/tests/data/test_menu_slash_bad.menu
@@ -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
index 0000000..1b67ff5
--- /dev/null
+++ b/src/tests/data/test_type.desktop
@@ -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
index 0000000..3cbee9e
--- /dev/null
+++ b/src/tests/ef_cache.c
@@ -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
index 0000000..a99b2ae
--- /dev/null
+++ b/src/tests/ef_data_dirs.c
@@ -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
index 0000000..39f942a
--- /dev/null
+++ b/src/tests/ef_desktop.c
@@ -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
index 0000000..1a04cfa
--- /dev/null
+++ b/src/tests/ef_icon_theme.c
@@ -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
index 0000000..00d459e
--- /dev/null
+++ b/src/tests/ef_ini.c
@@ -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
index 0000000..cfc50e2
--- /dev/null
+++ b/src/tests/ef_locale.c
@@ -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
index 0000000..a7519b7
--- /dev/null
+++ b/src/tests/ef_menu.c
@@ -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
index 0000000..2cb9e21
--- /dev/null
+++ b/src/tests/ef_mime.c
@@ -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
index 0000000..2e88c68
--- /dev/null
+++ b/src/tests/ef_test.h
@@ -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
index 0000000..0647ef0
--- /dev/null
+++ b/src/tests/ef_utils.c
@@ -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
index 0000000..d1cb126
--- /dev/null
+++ b/src/tests/efreet_icon_cache_dump.c
@@ -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
index 0000000..7c414b5
--- /dev/null
+++ b/src/tests/efreet_spec_test.c
@@ -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
index 0000000..7424435
--- /dev/null
+++ b/src/tests/efreet_suite.c
@@ -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_test_efreet.c b/src/tests/efreet_test_efreet.c
new file mode 100644
index 0000000..d35ec9e
--- /dev/null
+++ b/src/tests/efreet_test_efreet.c
@@ -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
index 0000000..2f8972d
--- /dev/null
+++ b/src/tests/efreet_test_efreet_cache.c
@@ -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
index 0000000..de6cf37
--- /dev/null
+++ b/src/tests/main.c
@@ -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;
+}