From: raster Date: Thu, 30 Aug 2012 09:54:57 +0000 (+0000) Subject: EFL 1.7 svn doobies X-Git-Tag: accepted/2.0alpha-wayland/20121219.185841~14 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=082b16efe143acbf5115aa002f5a2346d88f4beb;p=profile%2Fivi%2Fefreet.git EFL 1.7 svn doobies git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/branches/efreet-1.7@75862 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- 082b16efe143acbf5115aa002f5a2346d88f4beb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99c665b --- /dev/null +++ b/.gitignore @@ -0,0 +1,152 @@ +*.o +*.lo +*.la +.deps +.libs +ABOUT-NLS +Makefile +Makefile.in +/aclocal.m4 +autom4te.cache/ +/config.cache +/config.cache-env +/config.guess +/config.h +/config.h.in +/config.log +/config.status +/config.sub +/config.rpath +/configure +/depcomp +doc/Doxyfile +doc/Makefile +doc/Makefile.in +/doc/efreet_doxy_warnings.txt +/doc/html/ +/doc/latex/ +/efreet-mime.pc +/efreet-trash.pc +/efreet.pc +/efreet.spec +/install-sh +/libtool +/ltmain.sh +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +/missing +src/bin/efreet_desktop_cache_create +src/bin/efreet_icon_cache_create +src/tests/compare/efreet_alloc +src/tests/compare/efreet_menu_alloc +src/tests/efreet_cache_test +src/tests/efreet_icon_cache_dump +src/tests/efreet_spec_test +src/tests/efreet_test +/m4/codeset.m4 +/m4/gettext.m4 +/m4/glibc21.m4 +/m4/iconv.m4 +/m4/intdiv0.m4 +/m4/intmax.m4 +/m4/inttypes-pri.m4 +/m4/inttypes.m4 +/m4/inttypes_h.m4 +/m4/isc-posix.m4 +/m4/lcmessage.m4 +/m4/lib-ld.m4 +/m4/lib-link.m4/m4/codeset.m4 +/m4/gettext.m4 +/m4/glibc21.m4 +/m4/iconv.m4 +/m4/intdiv0.m4 +/m4/intmax.m4 +/m4/inttypes-pri.m4 +/m4/inttypes.m4 +/m4/inttypes_h.m4 +/m4/isc-posix.m4 +/m4/lcmessage.m4 +/m4/lib-ld.m4 +/m4/lib-link.m4 +/m4/lib-prefix.m4 +/m4/libtool.m4 +/m4/longdouble.m4 +/m4/longlong.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/m4/nls.m4 +/m4/po.m4 +/m4/printf-posix.m4 +/m4/progtest.m4 +/m4/signed.m4 +/m4/size_max.m4 +/m4/stdint_h.m4 +/m4/uintmax_t.m4 +/m4/ulonglong.m4 +/m4/wchar_t.m4 +/m4/wint_t.m4 +/m4/xsize.m4 +/m4/glibc2.m4 +/m4/lib-prefix.m4 +/m4/libtool.m4 +/m4/longdouble.m4 +/m4/longlong.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/m4/nls.m4 +/m4/po.m4 +/m4/printf-posix.m4 +/m4/progtest.m4 +/m4/signed.m4 +/m4/size_max.m4 +/m4/stdint_h.m4 +/m4/uintmax_t.m4 +/m4/ulonglong.m4 +/m4/wchar_t.m4 +/m4/wint_t.m4 +/m4/xsize.m4 +/m4/glibc2.m4 +/m4/intl.m4 +/m4/intldir.m4 +/m4/intlmacosx.m4 +/m4/lock.m4 +/m4/visibility.m4/m4/intl.m4 +/m4/intldir.m4 +/m4/intlmacosx.m4 +/m4/lock.m4 +/m4/visibility.m4 +/po/Makefile.in.in +/po/Makevars.template +/po/POTFILES +/po/Rules-quot +/po/boldquot.sed +/po/ecore.pot +/po/en@boldquot.header +/po/en@quot.header +/po/insert-header.sin +/po/quot.sed +/po/remove-potcdate.sed +/po/remove-potcdate.sin +/po/stamp-po +/po/Makefile.in.in +/po/Makevars.template +/po/POTFILES +/po/Rules-quot +/po/boldquot.sed +/po/ecore.pot +/po/en@boldquot.header +/po/en@quot.header +/po/insert-header.sin +/po/quot.sed +/po/remove-potcdate.sed +/po/remove-potcdate.sin +/po/*.gmo +/po/stamp-po +/stamp-h1 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ad4bc4c --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +dan 'dj2' sinclair +Brian 'rephorm' Mattern +Sebastian Dransfeld +Nathan 'RbdPngn' Ingersoll +Nicholas 'Mekius' Hughart +Albin "Lutin" Tonnerre +Mathieu Taillefumier diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..91c13e7 --- /dev/null +++ b/COPYING @@ -0,0 +1,25 @@ +Copyright notice for Efreet: + +Copyright (C) 2006-2011 Dan Sinclair and various contributors (see AUTHORS) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..46f0995 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,132 @@ +2011-01-29 Carsten Haitzler (The Rasterman) + + 1.0.0 release + +2011-01-29 Brian Mattern + + Fix bug when you have an empty in a menu + +2011-01-30 Carsten Haitzler (The Rasterman) + + Fix tests to not build clearenv related code if not available + +2011-01-31 Sebastian Dransfeld + + * Fix memleak, free cache icons after retrival + * Add temporary memory cache of eet file data + * Always use icon cache, remove old code + * Make check for stat return more explicit + * Check whether dirs is NULL + * Create internal struct for theme cache + * Check if theme has changed when building cache + * Prefix internal efreet cache keys with __ + * Create one cache file for each theme + * Before doing strcmp, check if pointers are equal. They might be + because of mmap and stringshare. + * Add local icon cache, so we wont hit the cache file for each icon + to be retrieved + +2011-02-03 Sebastian Dransfeld + + * Fix leak in efreet_mime_type_icon_get() + +2011-02-05 Sebastian Dransfeld + + * Remove global log domain and make all files define log domain + before including efreet_private.h + * Remove efreet_xml_{init,shutdown}() from efreet_menu.c, it is done + in efreet_init() + * Set EFREET_MODULE_LOG_DOM variable to -1 after unregister + * Remove duplicate logging macros + +2011-02-05 Hannes Janetzek + + * Clear mem cache when closing eet file + +2011-02-06 Sebastian Dransfeld + + * Fix doc for efreet_utils.{h,c},efreet_mime.c + * Add comment for all internal EAPI functions + * Remove doc for removed _efreet_log_domain_global + +2011-02-07 Sebastian Dransfeld + + * Don't create cache dir several times + * Do efreet_init before using efreet_*() functions + * Move lock creation to own function for desktop cache + * Set file owner to calling user + +2011-02-09 Sebastian Dransfeld + + * Move theme hash from efreet_icon.c to efreet_cache.c + * Don't free data returned by efreet_util_cache_names + * Add free callback to eet hashes + * Remove unneeded header + * Check if pattern equals "*" before doing pattern match + * Remove EAPI from efreet_home_dir_get + * Make efreet_cache_icon_theme_free only used in efreet_cache.c static + * Move struct only used for cache to efreet_cache_private.h + +2011-02-10 Sebastian Dransfeld + + * Move all eet cache handling to efreet_cache.c + * Free hashes on init error + * efreet_cache_icon -> efreet_icon for functions in efreet_icon.c + * Fix memleak in desktop cache create + * Pass dirs to desktop cache process as arguments + * Delay cache recreation with a timer + * Move desktop cache to efreet_cache.c, and cache all requests which + hit the eet cache + +2011-03-18 Mike Blumenkrantz + + * Use eina_log more effectively + +2011-04-04 Tom Hacohen (TAsn) + + * Fixed uri encoding when opening files. + +2011-08-16 Sebastian Dransfeld + + * Always rebuild cache from scratch when needed, but rely on correct + spec behaviour to check for theme changes. This will considerably + speed up the cache process when there is no change, and improve the + correctness of the cache when changes occur. For example didn't the + previous behaviour handle file removal gracefully. + +2011-08-16 Sebastian Dransfeld + + * Save whether cache changed in update file, and propagate to update + event. + +2011-12-02 Carsten Haitzler (The Rasterman) + + 1.1.0 release + +2012-01-19 Carsten Haitzler (The Rasterman) + + * use eina_file_direct_ls not opendir() + +2012-01-19 Jérôme Pinot + + * Documentation fixes. + +2012-04-26 Carsten Haitzler (The Rasterman) + + 1.2.0 release + +2012-05-11 Cedric Bail + + * Faster loading of mime type. + +2012-06-25 Sebastian Dransfeld + + * Added efreet_lang_reset() to reset language dependent variables and caches + +2012-06-25 Sebastian Dransfeld + + * Support XDG_DESKTOP_DIR and read user-dirs.dirs + +2012-07-02 Sebastian Dransfeld + + * Fix minor memory leak in cache update handler diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..23e5f25 --- /dev/null +++ b/INSTALL @@ -0,0 +1,236 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). Here is a another example: + + /bin/bash ./configure CONFIG_SHELL=/bin/bash + +Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +configuration-related scripts to be executed by `/bin/bash'. + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..a159015 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,139 @@ +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src doc + +MAINTAINERCLEANFILES = \ +Makefile.in \ +aclocal.m4 \ +config.guess \ +config.h.in \ +config.h.in~ \ +config.rpath \ +config.sub \ +configure \ +depcomp \ +install-sh \ +ltmain.sh \ +missing \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc.tar.bz2 \ +m4/libtool.m4 \ +m4/lt~obsolete.m4 \ +m4/ltoptions.m4 \ +m4/ltsugar.m4 \ +m4/ltversion.m4 \ +m4/codeset.m4 \ +m4/gettext.m4* \ +m4/glibc2*.m4 \ +m4/iconv.m4 \ +m4/intdiv0.m4 \ +m4/intldir.m4 \ +m4/intl.m4 \ +m4/intlmacosx.m4 \ +m4/intmax.m4* \ +m4/inttypes_h.m4 \ +m4/inttypes.m4 \ +m4/inttypes-pri.m4 \ +m4/isc-posix.m4 \ +m4/lcmessage.m4 \ +m4/lib-ld.m4* \ +m4/lib-link.m4 \ +m4/lib-prefix.m4* \ +m4/lock.m4 \ +m4/longdouble.m4* \ +m4/longlong.m4* \ +m4/nls.m4 \ +m4/po.m4* \ +m4/printf-posix.m4* \ +m4/progtest.m4 \ +m4/signed.m4 \ +m4/size_max.m4* \ +m4/stdint_h.m4 \ +m4/uintmax_t.m4 \ +m4/ulonglong.m4* \ +m4/visibility.m4 \ +m4/wchar_t.m4 \ +m4/wint_t.m4* \ +m4/xsize.m4* + +if HAVE_PO +SUBDIRS += po + +MAINTAINERCLEANFILES += \ + po/boldquot.sed \ + po/en@boldquot.header \ + po/en@quot.header \ + po/insert-header.sin \ + po/Makefile.in.in* \ + po/Makevars.template \ + po/quot.sed \ + po/remove-potcdate.sin \ + po/Rules-quot* + +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = efreet.pc efreet-mime.pc efreet-trash.pc + +EXTRA_DIST = \ +AUTHORS \ +COPYING \ +autogen.sh \ +efreet.pc.in \ +efreet-mime.pc.in \ +efreet.spec.in \ +efreet.spec \ +m4/efl_coverage.m4\ +m4/efl_tests.m4\ +README + +.PHONY: doc coverage + +doc: + @echo "entering doc/" + make -C doc doc + +# Unit tests + +if EFL_ENABLE_TESTS + +check-local: + @./src/tests/efreet_suite + +else + +check-local: + @echo "reconfigure with --enable-tests" + +endif + +# Coverage report + +if EFL_ENABLE_COVERAGE +lcov-reset: + @rm -rf $(top_builddir)/coverage + @find $(top_builddir) -name "*.gcda" -delete + @lcov --zerocounters --directory $(top_builddir) + +lcov-report: + @mkdir $(top_builddir)/coverage + lcov --capture --compat-libtool --output-file $(top_builddir)/coverage/coverage.info --directory $(top_builddir) + lcov --remove $(top_builddir)/coverage/coverage.info '*.h' --output-file $(top_builddir)/coverage/coverage.cleaned.info + genhtml -t "$(PACKAGE_STRING)" -o $(top_builddir)/coverage/html $(top_builddir)/coverage/coverage.cleaned.info + @echo "Coverage Report at $(top_builddir)/coverage/html" + +coverage: + @$(MAKE) lcov-reset + @$(MAKE) check + @$(MAKE) lcov-report +else +lcov-reset: + @echo "reconfigure with --enable-coverage" + +lcov-report: + @echo "reconfigure with --enable-coverage" + +coverage: + @echo "reconfigure with --enable-tests --enable-coverage" +endif diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..12d5666 --- /dev/null +++ b/NEWS @@ -0,0 +1,58 @@ +Efreet 1.7.0 + +Changes since Efreet 1.2.0: +--------------------------- + +Improvements: + + * Faster mime type loading. + + +Additions: + + * Support XDG_DESKTOP_DIR + * efreet_lang_reset() for refreshing language variables and caches after locale switches + +Efreet 1.2.0 + +Changes since Efreet 1.1.0: +--------------------------- + +Improvements: + + * use eina_file_direct_ls() instead of opendir(). + * compilation warnings fixed. + * documentation improvements/fixes + +Changes since Efreet 1.0.0: +--------------------------- + +Fixes: + + * when you have an empty in a menu + * tests don't do clearenv when it is not available + * memory leak fixed in cache icon retrieval + * check for NULL dirs in caches + * check for theme change when rebuilding cache + * leak in efreet_mime_type_icon_get + * reset log domain to -1 on unregister + * clear memory cache when closing eet file + * fixed documentation + * remove EAPI from efreet_home_dir_get as it wasn't exported in .h's + * don't leak hashes on init error + * no more memleak in desktop cache creation + * fixed uri encoding when opening files + +Improvements: + + * added temporary memory cache for eet file data + * always use icon cache + * faster string comapre with poter equality for strings + * local log domains per file + * remove exess init functions for xml + * stop creating cache dir multiple times + * change ownership of cache files to calling user + * fast path for "*" pattern match + * delay cache creation with a timer to avoid storms + * always rebuild cache from 0 when needed and be more correct + * store cache change flags in update file and propagate to update event diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..485be66 --- /dev/null +++ b/NOTES @@ -0,0 +1,48 @@ + +rephorm one thing. if you plan to add .desktop editing, according to the +spec you need to keep EVERYTHING around (even sections / keys you don't know +/ care about) +dj2 k +rephorm but for most usage (read only) thats a waste of memory +dj2 hm, i guess the best way to do that is keep everything in a hash +dj2 and write accessor functions +dj2 or pull the common stuff out to functions and leve the rest in the hash +rephorm and maybe have a flag for 'readonly' to kill the hash +dj2 yea, will have to put some tought into the api for that + +rephorm ooh. comments need to be preserved also in edits + + + + +rephorm efreet_xml has one bug that i see +rephorm if you have something like beforeafter, you +can't get to the 'after' text +dj2 hm, will have to look at that +rephorm (it stores 'before' as the text on 'tag') +rephorm it should probably create child nodes with the text +rephorm so tag would have 3 children, text: before, tag: child and text: after + + +efreet_desktop_string_list_parse() needs to optionally use comma (',') as +the separator if the desktop version is < 1.0 (but, what if it isn't set??) + + +desktop_command_get: + check for needed types (nNfFuU) + + get lists of needed types (dirs, fullpaths, urls, etc) + + if type in uU: + create Pending struct + start downloads, pass Pending in as data + else: + if tyep in UF... + exec + +download cb: + if type in fF + + + + diff --git a/README b/README new file mode 100644 index 0000000..05ea494 --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +Efreet 1.7.0 + +****************************************************************************** + + FOR ANY ISSUES PLEASE EMAIL: + enlightenment-devel@lists.sourceforge.net + +****************************************************************************** + +Requirements: +------------- + +Must: + libc + eina (at least 1.1.0) + ecore (at least 1.1.0) + ecore-file (at least 1.1.0) + eet (at least 1.5.0) + +An implementation of several specifications from freedesktop.org intended for +use in Enlightenment DR17 (e17) and other applications using the Enlightenment +Foundation Libraries (EFL). Currently, the following specifications are +included: + * Base Directory + * Desktop Entry + * Icon Theme + * Menu + * Trash + * Mime + +------------------------------------------------------------------------------ +COMPILING AND INSTALLING: + + ./configure + make +(as root unless you are installing in your users directories): + make install + diff --git a/TODO b/TODO new file mode 100644 index 0000000..a40fb31 --- /dev/null +++ b/TODO @@ -0,0 +1,24 @@ +TODO +---- +- efreet_desktop_command_* should free the exec strings for you (set free cb on execs list in _build()). need to make sure users of this function are updated when this change is made (e.g. e17's exec cb) +- Efreet_Menu should setup an Ecore_File_Monitor on the .menu files and the + app_dir and reload the menu when needed + +- The move handling is wrong when moving to a destination with /'s in the + name. We should be creating intermediate menus for each of the /'d items + instead of just making the name as we do now + +- Add support for searching for a list of fallback icons in the current theme + BEFORE looking in the inherited themes (e.g. for mime-types that go from + specific to general). + +- If a theme has several icon types (png, xpm..) prefer the first in the + extension list. + +- Add some ref/free tracking to check if users aren't abusing the system. + +Notes from the Menu Spec test +----------------------------- +- We're appending the / to the menu names in the test case. We should + possibly be doing that in the efreet code itself. + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..2a59cac --- /dev/null +++ b/autogen.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +rm -rf autom4te.cache +rm -f aclocal.m4 ltmain.sh + +touch ABOUT-NLS + +echo "Running autopoint..." ; autopoint -f || : +echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS -I m4 || exit 1 +echo "Running autoheader..." ; autoheader || exit 1 +echo "Running autoconf..." ; autoconf || exit 1 +echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1 +echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1 + +W=0 + +rm -f config.cache-env.tmp +echo "OLD_PARM=\"$@\"" >> config.cache-env.tmp +echo "OLD_CFLAGS=\"$CFLAGS\"" >> config.cache-env.tmp +echo "OLD_PATH=\"$PATH\"" >> config.cache-env.tmp +echo "OLD_PKG_CONFIG_PATH=\"$PKG_CONFIG_PATH\"" >> config.cache-env.tmp +echo "OLD_LDFLAGS=\"$LDFLAGS\"" >> config.cache-env.tmp + +cmp config.cache-env.tmp config.cache-env >> /dev/null +if [ $? -ne 0 ]; then + W=1; +fi + +if [ $W -ne 0 ]; then + echo "Cleaning configure cache..."; + rm -f config.cache config.cache-env + mv config.cache-env.tmp config.cache-env +else + rm -f config.cache-env.tmp +fi + +if [ -z "$NOCONFIGURE" ]; then + ./configure -C "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..9535101 --- /dev/null +++ b/configure.ac @@ -0,0 +1,285 @@ +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +m4_define([v_maj], [1]) +m4_define([v_min], [7]) +m4_define([v_mic], [0]) +m4_define([v_rev], m4_esyscmd([(svnversion "${SVN_REPO_PATH:-.}" | grep -v '\(export\|Unversioned directory\)' || echo 0) | awk -F : '{printf("%s\n", $1);}' | tr -d ' :MSP\n'])) +m4_if(v_rev, [0], [m4_define([v_rev], m4_esyscmd([git log 2> /dev/null | (grep -m1 git-svn-id || echo 0) | sed -e 's/.*@\([0-9]*\).*/\1/' | tr -d '\n']))]) +##-- When released, remove the dnl on the below line +m4_undefine([v_rev]) +##-- When doing snapshots - change soname. remove dnl on below line +dnl m4_define([relname], [ver-pre-svn-07]) +dnl m4_define([v_rel], [-release relname]) +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +m4_ifdef([v_rev], [m4_define([v_ver], [v_maj.v_min.v_mic.v_rev])], [m4_define([v_ver], [v_maj.v_min.v_mic])]) +m4_define([lt_cur], m4_eval(v_maj + v_min)) +m4_define([lt_rev], v_mic) +m4_define([lt_age], v_min) +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## + +AC_INIT([efreet], [v_ver], [enlightenment-devel@lists.sourceforge.net]) +AC_PREREQ([2.52]) +AC_CONFIG_SRCDIR([configure.ac]) +AC_CONFIG_MACRO_DIR([m4]) + +AC_CONFIG_HEADERS([config.h]) +AH_TOP([ +#ifndef EFL_CONFIG_H__ +#define EFL_CONFIG_H__ +]) +AH_BOTTOM([ +#endif /* EFL_CONFIG_H__ */ +]) + +AM_INIT_AUTOMAKE([1.6 dist-bzip2]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_LIBTOOL_WIN32_DLL +define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl +define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl +AC_PROG_LIBTOOL + +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +m4_ifdef([v_rev], , [m4_define([v_rev], [0])]) +m4_ifdef([v_rel], , [m4_define([v_rel], [])]) +AC_DEFINE_UNQUOTED(VMAJ, [v_maj], [Major version]) +AC_DEFINE_UNQUOTED(VMIN, [v_min], [Minor version]) +AC_DEFINE_UNQUOTED(VMIC, [v_mic], [Micro version]) +AC_DEFINE_UNQUOTED(VREV, [v_rev], [Revison]) +version_info="lt_cur:lt_rev:lt_age" +release_info="v_rel" +AC_SUBST(version_info) +AC_SUBST(release_info) +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## +VMAJ=v_maj +AC_SUBST(VMAJ) + +AM_GNU_GETTEXT_VERSION(0.17) + +m4_ifdef([AM_GNU_GETTEXT], [ +AM_GNU_GETTEXT([external]) +po_makefile_in=po/Makefile.in +have_po="yes" +],[ +have_po="no" +]) +AC_SUBST(LTLIBINTL) +LOCALE_DIR="${localedir}" +AC_SUBST(LOCALE_DIR) + +if test "x${POSUB}" = "x" ; then + have_po="no" +fi + +AM_CONDITIONAL([HAVE_PO], [test "x${have_po}" = "xyes"]) + +### Needed information + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST + + +### Additional options to configure + +AC_ARG_ENABLE([strict-spec], + [AC_HELP_STRING([--enable-strict-spec], [Enable strict spec compliance @<:@default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + enable_strict_spec="yes" + else + enable_strict_spec="no" + fi + ], + [enable_strict_spec="no"]) + +if test "x${enable_strict_spec}" = "xyes" ; then + AC_DEFINE([STRICT_SPEC], [1], [Strict Spec Compliance]) +fi + +AC_ARG_ENABLE([sloppy-spec], + [AC_HELP_STRING([--disable-sloppy-spec], [Enable sloppy spec compliance @<:@default=enabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + enable_sloppy_spec="yes" + else + enable_sloppy_spec="no" + fi + ], + [enable_sloppy_spec="yes"]) + +if test "x${enable_sloppy_spec}" = "xyes" ; then + AC_DEFINE([SLOPPY_SPEC], [1], [Sloppy Spec Compliance]) +fi + + +#AC_ARG_ENABLE(hidden-visibility, +# [AC_HELP_STRING([--enable-hidden-visibility],[Enable hidden visibility])], +# [enable_hidden_visibility=$enableval], [enable_hidden_visibility="auto"]) +#if test "x$enable_hidden_visibility" = "xyes" ; then +# CPPFLAGS="$CPPFLAGS -fvisibility=hidden" +#else +# AC_DEFINE(DEFAULT_VISIBLITY, 1, [Default visibility]) +#fi +#AM_CONDITIONAL(DEFAULT_VISIBILITY, test "x$enable_hidden_visibility" != "xyes") +AM_CONDITIONAL(DEFAULT_VISIBILITY, false) + + +### Checks for programs +AC_PROG_CC + +# pkg-config +PKG_PROG_PKG_CONFIG + +# Check whether pkg-config supports Requires.private +if $PKG_CONFIG --atleast-pkgconfig-version 0.22; then + pkgconfig_requires_private="Requires.private" +else + pkgconfig_requires_private="Requires" +fi +AC_SUBST(pkgconfig_requires_private) + +# doxygen program for documentation building +EFL_CHECK_DOXYGEN([build_doc="yes"], [build_doc="no"]) + + +### Checks for libraries + +# Evil library for compilation on Windows CE + +EFL_EFREET_BUILD="" +EFL_EFREET_MIME_BUILD="" +EFL_EFREET_TRASH_BUILD="" +case "$host_os" in + mingw*) + PKG_CHECK_MODULES([EVIL], [evil >= 1.6.99]) + AC_DEFINE(HAVE_EVIL, 1, [Set to 1 if evil package is installed]) + requirement_efreet="evil" + EFL_EFREET_BUILD="-DEFL_EFREET_BUILD" + EFL_EFREET_MIME_BUILD="-DEFL_EFREET_MIME_BUILD" + EFL_EFREET_TRASH_BUILD="-DEFL_EFREET_TRASH_BUILD" + ;; +esac +AC_SUBST(EFL_EFREET_BUILD) +AC_SUBST(EFL_EFREET_MIME_BUILD) +AC_SUBST(EFL_EFREET_TRASH_BUILD) + +requirement_efreet="ecore-file >= 1.6.99 ecore >= 1.6.99 eet >= 1.6.99 eina >= 1.6.99 ${requirement_efreet}" +PKG_CHECK_MODULES(EFREET, [${requirement_efreet}]) +PKG_CHECK_MODULES(EINA, [eina >= 1.6.99]) + + +### Checks for header files + +AC_CHECK_HEADERS([netinet/in.h arpa/inet.h]) + +### Checks for types + + +### Checks for structures + + +### Checks for compiler characteristics + +AC_C_CONST +AC_C___ATTRIBUTE__ +AM_PROG_CC_STDC +AC_HEADER_STDC + +case "${host_os}" in + mingw*) + EFREET_CFLAGS="${EFREET_CFLAGS} ${EVIL_CFLAGS}" + ;; +esac + +if ! test "x${VMIC}" = "x" ; then + EFL_COMPILER_FLAG([-Wall]) + EFL_COMPILER_FLAG([-W]) +fi + +EFL_COMPILER_FLAG([-Wshadow]) + +EFL_CHECK_PATH_MAX + + +### Checks for linker characteristics +lt_enable_auto_import="" +WIN32_LIBS="" +case "${host_os}" in + mingw*) + WIN32_LIBS="-lws2_32" + lt_enable_auto_import="-Wl,--enable-auto-import" + ;; +esac +AC_SUBST(WIN32_LIBS) +AC_SUBST(lt_enable_auto_import) + + +### Checks for library functions +AC_ISC_POSIX +AC_FUNC_ALLOCA +AC_CHECK_FUNCS(strlcpy clearenv) + +### Unit tests, coverage + +EFL_CHECK_TESTS([enable_tests="yes"], [enable_tests="no"]) + +EFL_CHECK_COVERAGE([${enable_tests}], [enable_coverage="yes"], [enable_coverage="no"]) +EFREET_CFLAGS="${EFREET_CFLAGS} ${EFL_COVERAGE_CFLAGS}" +EFREET_LIBS="${EFREET_LIBS} ${EFL_COVERAGE_LIBS}" +if test "x$enable_coverage" = "xyes" ; then + EFREET_CFLAGS="${EFREET_CFLAGS} ${EFL_DEBUG_CFLAGS}" +fi + +AC_SUBST(requirement_efreet) + +AC_OUTPUT([ +efreet.spec +efreet.pc +efreet-mime.pc +efreet-trash.pc +Makefile +doc/Makefile +doc/Doxyfile +src/Makefile +src/lib/Makefile +src/bin/Makefile +src/tests/Makefile +src/tests/data/Makefile +src/tests/data/sub/Makefile +src/tests/compare/Makefile +$po_makefile_in +]) + + +##################################################################### +## Info + +echo +echo +echo +echo "------------------------------------------------------------------------" +echo "$PACKAGE $VERSION" +echo "------------------------------------------------------------------------" +echo +echo "Configuration Options Summary:" +echo +echo " Specification compliance:" +echo " Strict.............: ${enable_strict_spec}" +echo " Sloppy.............: ${enable_sloppy_spec}" +echo +echo " Tests................: ${enable_tests}" +echo " Coverage.............: ${enable_coverage}" +echo +echo " Documentation........: ${build_doc}" +echo +echo "Compilation............: make (or gmake)" +echo " CPPFLAGS.............: $CPPFLAGS" +echo " CFLAGS...............: $CFLAGS" +echo " LDFLAGS..............: $LDFLAGS" +echo +echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" +echo " prefix...............: $prefix" +echo diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000..cd56fea --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,1692 @@ +# Doxyfile 1.7.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Efreet + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description for a project that appears at the top of each page and should give viewer a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @top_srcdir@/src/lib + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = @top_srcdir@/doc/images + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = efreet_ \ + Efreet_ \ + _efreet_ \ + _Efreet_ \ + EFREET_ \ + _EFREET_ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = @srcdir@/head.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = @srcdir@/foot.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = @srcdir@/e.css + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = YES + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = YES + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.enlightenment.Efreet + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.enlightenment.Efreet + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Enlightenment + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.enlightenment.Efreet + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.enlightenment.Efreet + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = YES + +# This tag can be used to set the number of enum values (range [0,1..20]) +# that doxygen will group on one line in the generated HTML documentation. +# Note that a value of 0 will completely suppress the enum values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 1 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = NO + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = EINA_MAGIC_DEBUG \ + __UNUSED__= \ + EINA_ARG_NONNULL()= \ + EINA_MALLOC= \ + EINA_WARN_UNUSED_RESULT= \ + EAPI= \ + EINA_PURE= \ + EINA_CONST= + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, svg, gif or svg. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..ad5250b --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,32 @@ +MAINTAINERCLEANFILES = Makefile.in Doxyfile efreet_doxy_warnings.txt + +.PHONY: doc + +PACKAGE_DOCNAME = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-doc + +if EFL_BUILD_DOC + +doc-clean: + rm -rf html/ latex/ man/ xml/ $(top_builddir)/$(PACKAGE_DOCNAME).tar* + +doc: all doc-clean + $(efl_doxygen) + cp $(srcdir)/images/* html/ + rm -rf $(PACKAGE_DOCNAME).tar* + mkdir -p $(PACKAGE_DOCNAME)/doc + cp -R html/ latex/ $(PACKAGE_DOCNAME)/doc + tar cf $(PACKAGE_DOCNAME).tar $(PACKAGE_DOCNAME)/ + bzip2 -9 $(PACKAGE_DOCNAME).tar + rm -rf $(PACKAGE_DOCNAME)/ + mv $(PACKAGE_DOCNAME).tar.bz2 $(top_builddir) + +clean-local: doc-clean + +else + +doc: + @echo "Documentation not built. Run ./configure --help" + +endif + +EXTRA_DIST = Doxyfile.in $(wildcard images/*.*) e.css head.html foot.html diff --git a/doc/e.css b/doc/e.css new file mode 100644 index 0000000..8697a3a --- /dev/null +++ b/doc/e.css @@ -0,0 +1,218 @@ +/* + Author: + Andres Blanc + DaveMDS Andreoli + + Supported Browsers: + ie7, opera9, konqueror4 and firefox3 + + Please use a different file for ie6, ie5, etc. hacks. +*/ + + +/* Necessary to place the footer at the bottom of the page */ +html, body { + height: 100%; + margin: 0px; + padding: 0px; +} + +#container { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -53px; +} + +#footer, #push { + height: 53px; +} + + +* html #container { + height: 100%; +} + +/* Prevent floating elements overflowing containers */ +.clear { + clear: both; + width: 0px; + height: 0px; +} + +/* Flexible & centered layout from 750 to 960 pixels */ +.layout { + max-width: 960px; + min-width: 760px; + margin-left: auto; + margin-right: auto; +} + +body { + /*font-family: Lucida Grande, Helvetica, sans-serif;*/ + font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif +} + +/* Prevent design overflowing the viewport in small resolutions */ +#container { + padding-right: 17px; + padding-left: 17px; + background-image: url(head_bg.png); + background-repeat: repeat-x; +} + +#header { + width: 100%; + height: 102px; +} + +#header h1 { + width: 63px; + height: 63px; + background-image: url(e.png); + background-repeat: no-repeat; + position: absolute; + margin: 0px; +} + +#header h1 span { + display: none; +} + +#header h2 { + display: none; +} + +/* .menu-container is used to set properties common to .menu and .submenu */ +#header .menu-container { +} + +#header .menu-container ul { + list-style-type: none; + list-style-position: inside; + margin: 0; +} + +#header .menu-container li { + display: block; + float: right; +} + +#header .menu { + height: 63px; + display: block; + background-image: url(menu_bg.png); + background-repeat: repeat-x; +} + +#header .menu ul { + height: 100%; + display: block; + background-image: url(menu_bg_last.png); + background-repeat: no-repeat; + background-position: top right; + padding-right: 17px; +} + +#header .menu li { + height: 100%; + text-align: center; + background-image: url(menu_bg_unsel.png); + background-repeat: no-repeat; +} + +#header .menu a { + height: 100%; + display: block; + color: #cdcdcd; + text-decoration: none; + font-size: 10pt; + line-height: 59px; + text-align: center; + padding: 0px 15px 0px 15px; +} + +#header .menu li:hover { + background-image: url(menu_bg_hover.png); + background-repeat: no-repeat; +} + +#header .menu li:hover a { + color: #FFFFFF; +} + +#header .menu li.current { + background-image: url(menu_bg_current.png); + background-repeat: no-repeat; +} + +#header .menu li.current a { + color: #646464; +} + + +/* Hide all the submenus but the current */ +#header .submenu ul { + display: none; +} + +#header .submenu .current { + display: block; +} + +#header .submenu { + font: bold 10px verdana,'Bitstream Vera Sans',helvetica,arial,sans-serif; + margin-top: 10px; +} + +#header .submenu a { + color: #888888; + text-decoration: none; + font-size: 0.9em; + line-height: 15px; + padding:0px 5px 0px 5px; +} + +#header .submenu a:hover { + color: #444444; +} + +#header .submenu li { + border-left: 1px solid #DDDDDD; +} + +#header .submenu li:last-child { + border-left: 0; +} + +#header .doxytitle { + position: absolute; + font-size: 1.8em; + font-weight: bold; + color: #444444; + line-height: 35px; +} + +#header small { + font-size: 0.4em; +} + +#footer { + background-image: url(foot_bg.png); + width: 100%; +} + +#footer table { + width: 100%; + text-align: center; + white-space: nowrap; + padding: 5px 30px 5px 30px; + font-size: 0.8em; + font-family: "Bitstream Vera","Vera","Trebuchet MS",Trebuchet,Tahoma,sans-serif; + color: #888888; +} + +#footer td.copyright { + width: 100%; +} + diff --git a/doc/foot.html b/doc/foot.html new file mode 100644 index 0000000..78ef911 --- /dev/null +++ b/doc/foot.html @@ -0,0 +1,19 @@ + +
+ + + + + + + + + + + diff --git a/doc/head.html b/doc/head.html new file mode 100644 index 0000000..f9be916 --- /dev/null +++ b/doc/head.html @@ -0,0 +1,68 @@ + + + $title + + + + + + + + + + + + + + +
+ + + +
+
diff --git a/doc/images/e.png b/doc/images/e.png new file mode 100644 index 0000000..b3884a5 Binary files /dev/null and b/doc/images/e.png differ diff --git a/doc/images/edoxy.css b/doc/images/edoxy.css new file mode 100644 index 0000000..3caf7a9 --- /dev/null +++ b/doc/images/edoxy.css @@ -0,0 +1,483 @@ +/* + * This file contain a custom doxygen style to match e.org graphics + */ + + + +/* BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { + font-family: Geneva, Arial, Helvetica, sans-serif; +}*/ +BODY, TD { + font-size: 12px; +} +H1 { + text-align: center; + font-size: 160%; +} +H2 { + font-size: 120%; +} +H3 { + font-size: 100%; +} +CAPTION { + font-weight: bold +} +DIV.qindex { + width: 100%; + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.navpath { + width: 100%; + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.navtab { + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} +TD.navtab { + font-size: 70%; +} +A.qindex { + text-decoration: none; + font-weight: bold; + color: #1A419D; +} +A.qindex:visited { + text-decoration: none; + font-weight: bold; + color: #1A419D +} +A.qindex:hover { + text-decoration: none; + background-color: #ddddff; +} +A.qindexHL { + text-decoration: none; + font-weight: bold; + background-color: #6666cc; + color: #ffffff; + border: 1px double #9295C2; +} +A.qindexHL:hover { + text-decoration: none; + background-color: #6666cc; + color: #ffffff; +} +A.qindexHL:visited { + text-decoration: none; + background-color: #6666cc; + color: #ffffff +} +A.el { + text-decoration: none; + font-weight: bold +} +A.elRef { + font-weight: bold +} +A.code:link { + text-decoration: none; + font-weight: normal; + color: #0000FF +} +A.code:visited { + text-decoration: none; + font-weight: normal; + color: #0000FF +} +A.codeRef:link { + font-weight: normal; + color: #0000FF +} +A.codeRef:visited { + font-weight: normal; + color: #0000FF +} +A:hover, A:visited:hover { + text-decoration: none; + /* background-color: #f2f2ff; */ + color: #000055; +} +A.anchor { + color: #000; +} +DL.el { + margin-left: -1cm +} +.fragment { + font-family: monospace, fixed; + font-size: 95%; +} +PRE.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + margin-top: 4px; + margin-bottom: 4px; + margin-left: 2px; + margin-right: 8px; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 4px; +} +DIV.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px +} + +DIV.groupHeader { + margin-left: 16px; + margin-top: 12px; + margin-bottom: 6px; + font-weight: bold; +} +DIV.groupText { + margin-left: 16px; + font-style: italic; + font-size: 90% +} +/*BODY { + background: white; + color: black; + margin-right: 20px; + margin-left: 20px; +}*/ +TD.indexkey { + background-color: #e8eef2; + font-weight: bold; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TD.indexvalue { + background-color: #e8eef2; + font-style: italic; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TR.memlist { + background-color: #f0f0f0; +} +P.formulaDsp { + text-align: center; +} +IMG.formulaDsp { +} +IMG.formulaInl { + vertical-align: middle; +} +SPAN.keyword { color: #008000 } +SPAN.keywordtype { color: #604020 } +SPAN.keywordflow { color: #e08000 } +SPAN.comment { color: #800000 } +SPAN.preprocessor { color: #806020 } +SPAN.stringliteral { color: #002080 } +SPAN.charliteral { color: #008080 } +SPAN.vhdldigit { color: #ff00ff } +SPAN.vhdlchar { color: #000000 } +SPAN.vhdlkeyword { color: #700070 } +SPAN.vhdllogic { color: #ff0000 } + +.mdescLeft { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.mdescRight { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.memItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplParams { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + color: #606060; + background-color: #FAFAFA; + font-size: 80%; +} +.search { + color: #003399; + font-weight: bold; +} +FORM.search { + margin-bottom: 0px; + margin-top: 0px; +} +INPUT.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +TD.tiny { + font-size: 75%; +} +a { + color: #1A41A8; +} +a:visited { + color: #2A3798; +} +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #84b0c7; +} +TH.dirtab { + background: #e8eef2; + font-weight: bold; +} +HR { + height: 1px; + border: none; + border-top: 1px solid black; +} + +/* Style for detailed member documentation */ +.memtemplate { + font-size: 80%; + color: #606060; + font-weight: normal; + margin-left: 3px; +} +.memnav { + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} +.memitem { + padding: 4px; + background-color: #eef3f5; + border-width: 1px; + border-style: solid; + border-color: #dedeee; + -moz-border-radius: 8px 8px 8px 8px; +} +.memname { + white-space: nowrap; + font-weight: bold; +} +.memdoc{ + padding-left: 10px; +} +.memproto { + background-color: #d5e1e8; + width: 100%; + border-width: 1px; + border-style: solid; + border-color: #84b0c7; + font-weight: bold; + -moz-border-radius: 8px 8px 8px 8px; +} +.paramkey { + text-align: right; +} +.paramtype { + white-space: nowrap; +} +.paramname { + color: #602020; + font-style: italic; + white-space: nowrap; +} +/* End Styling for detailed member documentation */ + +/* for the tree view */ +.ftvtree { + font-family: sans-serif; + margin:0.5em; +} +/* these are for tree view when used as main index */ +.directory { + font-size: 9pt; + font-weight: bold; +} +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* The following two styles can be used to replace the root node title */ +/* with an image of your choice. Simply uncomment the next two styles, */ +/* specify the name of your image and be sure to set 'height' to the */ +/* proper pixel height of your image. */ + +/* .directory h3.swap { */ +/* height: 61px; */ +/* background-repeat: no-repeat; */ +/* background-image: url("yourimage.gif"); */ +/* } */ +/* .directory h3.swap span { */ +/* display: none; */ +/* } */ + +.directory > h3 { + margin-top: 0; +} +.directory p { + margin: 0px; + white-space: nowrap; +} +.directory div { + display: none; + margin: 0px; +} +.directory img { + vertical-align: -30%; +} +/* these are for tree view when not used as main index */ +.directory-alt { + font-size: 100%; + font-weight: bold; +} +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} +.directory-alt > h3 { + margin-top: 0; +} +.directory-alt p { + margin: 0px; + white-space: nowrap; +} +.directory-alt div { + display: none; + margin: 0px; +} +.directory-alt img { + vertical-align: -30%; +} + diff --git a/doc/images/foot_bg.png b/doc/images/foot_bg.png new file mode 100644 index 0000000..b24f3a4 Binary files /dev/null and b/doc/images/foot_bg.png differ diff --git a/doc/images/head_bg.png b/doc/images/head_bg.png new file mode 100644 index 0000000..081dc13 Binary files /dev/null and b/doc/images/head_bg.png differ diff --git a/doc/images/menu_bg.png b/doc/images/menu_bg.png new file mode 100644 index 0000000..e978743 Binary files /dev/null and b/doc/images/menu_bg.png differ diff --git a/doc/images/menu_bg_current.png b/doc/images/menu_bg_current.png new file mode 100644 index 0000000..de97c92 Binary files /dev/null and b/doc/images/menu_bg_current.png differ diff --git a/doc/images/menu_bg_hover.png b/doc/images/menu_bg_hover.png new file mode 100644 index 0000000..3fd851d Binary files /dev/null and b/doc/images/menu_bg_hover.png differ diff --git a/doc/images/menu_bg_last.png b/doc/images/menu_bg_last.png new file mode 100644 index 0000000..88c116c Binary files /dev/null and b/doc/images/menu_bg_last.png differ diff --git a/doc/images/menu_bg_unsel.png b/doc/images/menu_bg_unsel.png new file mode 100644 index 0000000..50e5fd8 Binary files /dev/null and b/doc/images/menu_bg_unsel.png differ diff --git a/efreet-mime.pc.in b/efreet-mime.pc.in new file mode 100644 index 0000000..d651017 --- /dev/null +++ b/efreet-mime.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: efreet-mime +Description: Freedesktop Shared Mime Info standard implementation for the EFL +@pkgconfig_requires_private@: @requirement_efreet@ +Version: @VERSION@ +Libs: -L${libdir} -lefreet_mime +Cflags: -I${includedir}/efreet-@VMAJ@ diff --git a/efreet-trash.pc.in b/efreet-trash.pc.in new file mode 100644 index 0000000..5a4ccf8 --- /dev/null +++ b/efreet-trash.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: efreet-trash +Description: Freedesktop Shared Trash implementation for the EFL +@pkgconfig_requires_private@: @requirement_efreet@ +Version: @VERSION@ +Libs: -L${libdir} -lefreet_trash +Cflags: -I${includedir}/efreet-@VMAJ@ diff --git a/efreet.pc.in b/efreet.pc.in new file mode 100644 index 0000000..2f4db8e --- /dev/null +++ b/efreet.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: efreet +Description: Freedesktop standards implementation for the EFL +@pkgconfig_requires_private@: @requirement_efreet@ +Version: @VERSION@ +Libs: -L${libdir} -lefreet +Cflags: -I${includedir}/efreet-@VMAJ@ diff --git a/efreet.spec.in b/efreet.spec.in new file mode 100644 index 0000000..72c8ee9 --- /dev/null +++ b/efreet.spec.in @@ -0,0 +1,69 @@ +%define _missing_doc_files_terminate_build 0 + +%{!?_rel:%{expand:%%global _rel 0.enl%{?dist}}} + +Summary: FreeDesktop.Org Compatibility Library +Name: @PACKAGE@ +Version: @VERSION@ +Release: %{_rel} +License: BSD +Group: System Environment/Libraries +URL: http://www.enlightenment.org/ +Source: %{name}-%{version}.tar.gz +Packager: %{?_packager:%{_packager}}%{!?_packager:Michael Jennings } +Vendor: %{?_vendorinfo:%{_vendorinfo}}%{!?_vendorinfo:The Enlightenment Project (http://www.enlightenment.org/)} +Distribution: %{?_distribution:%{_distribution}}%{!?_distribution:%{_vendor}} +Obsoletes: ecore-desktop <= 0.9.9.040 +BuildRoot: %{_tmppath}/%{name}-%{version}-root + +%description +Efreet implements the FreeDesktop.Org application and MIME-handling +standards. + +%package devel +Summary: Efreet headers, static libraries, documentation and test programs +Group: System Environment/Libraries +Requires: %{name} = %{version} + +%description devel +Headers, static libraries, test programs and documentation for Efreet + +%prep +%setup -q + +%build +%{configure} --prefix=%{_prefix} +%{__make} %{?_smp_mflags} %{?mflags} + +%install +%{__make} %{?mflags_install} DESTDIR=$RPM_BUILD_ROOT install + +# Get rid of unneeded testing cruft. +%{__rm} -rf $RPM_BUILD_ROOT%{_datadir}/%{name} + +%clean +test "x$RPM_BUILD_ROOT" != "x/" && rm -rf $RPM_BUILD_ROOT + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-, root, root) +%doc AUTHORS COPYING* README +%{_bindir}/%{name}* +%{_libdir}/*.so.* +%{_libdir}/efreet/ +%{_datadir}/locale/*/LC_MESSAGES/*.mo + +%files devel +%defattr(-, root, root) +%{_includedir}/%{name}-1 +%{_libdir}/*.so +%{_libdir}/*.la +%{_libdir}/*.a +%{_libdir}/pkgconfig/* + +%changelog diff --git a/m4/ac_attribute.m4 b/m4/ac_attribute.m4 new file mode 100644 index 0000000..23479a9 --- /dev/null +++ b/m4/ac_attribute.m4 @@ -0,0 +1,47 @@ +dnl Copyright (C) 2004-2008 Kim Woelders +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. +dnl Originally snatched from somewhere... + +dnl Macro for checking if the compiler supports __attribute__ + +dnl Usage: AC_C___ATTRIBUTE__ +dnl call AC_DEFINE for HAVE___ATTRIBUTE__ and __UNUSED__ +dnl if the compiler supports __attribute__, HAVE___ATTRIBUTE__ is +dnl defined to 1 and __UNUSED__ is defined to __attribute__((unused)) +dnl otherwise, HAVE___ATTRIBUTE__ is not defined and __UNUSED__ is +dnl defined to nothing. + +AC_DEFUN([AC_C___ATTRIBUTE__], +[ + +AC_MSG_CHECKING([for __attribute__]) + +AC_CACHE_VAL([ac_cv___attribute__], + [AC_TRY_COMPILE( + [ +#include + +int func(int x); +int foo(int x __attribute__ ((unused))) +{ + exit(1); +} + ], + [], + [ac_cv___attribute__="yes"], + [ac_cv___attribute__="no"] + )]) + +AC_MSG_RESULT($ac_cv___attribute__) + +if test "x${ac_cv___attribute__}" = "xyes" ; then + AC_DEFINE([HAVE___ATTRIBUTE__], [1], [Define to 1 if your compiler has __attribute__]) + AC_DEFINE([__UNUSED__], [__attribute__((unused))], [Macro declaring a function argument to be unused]) + else + AC_DEFINE([__UNUSED__], [], [Macro declaring a function argument to be unused]) +fi + +]) + +dnl End of ac_attribute.m4 diff --git a/m4/ac_define_dir.m4 b/m4/ac_define_dir.m4 new file mode 100644 index 0000000..f3d8734 --- /dev/null +++ b/m4/ac_define_dir.m4 @@ -0,0 +1,47 @@ +##### http://autoconf-archive.cryp.to/ac_define_dir.html +# +# SYNOPSIS +# +# AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) +# +# DESCRIPTION +# +# This macro sets VARNAME to the expansion of the DIR variable, +# taking care of fixing up ${prefix} and such. +# +# VARNAME is then offered as both an output variable and a C +# preprocessor symbol. +# +# Example: +# +# AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.]) +# +# LAST MODIFICATION +# +# 2006-10-13 +# +# COPYLEFT +# +# Copyright (c) 2006 Stepan Kasal +# Copyright (c) 2006 Andreas Schwab +# Copyright (c) 2006 Guido U. Draheim +# Copyright (c) 2006 Alexandre Oliva +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty provided +# the copyright notice and this notice are preserved. + +AC_DEFUN([AC_DEFINE_DIR], [ + prefix_NONE= + exec_prefix_NONE= + test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix + test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix +dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn +dnl refers to ${prefix}. Thus we have to use `eval' twice. + eval ac_define_dir="\"[$]$2\"" + eval ac_define_dir="\"$ac_define_dir\"" + AC_SUBST($1, "$ac_define_dir") + AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) + test "$prefix_NONE" && prefix=NONE + test "$exec_prefix_NONE" && exec_prefix=NONE +]) diff --git a/m4/efl_compiler_flag.m4 b/m4/efl_compiler_flag.m4 new file mode 100644 index 0000000..618c6a6 --- /dev/null +++ b/m4/efl_compiler_flag.m4 @@ -0,0 +1,57 @@ +dnl Copyright (C) 2010 Vincent Torri +dnl and Albin Tonnerre +dnl That code is public domain and can be freely used or copied. + +dnl Macro that checks if a compiler flag is supported by the compiler. + +dnl Usage: EFL_COMPILER_FLAG(flag) +dnl flag is added to CFLAGS if supported. + +AC_DEFUN([EFL_COMPILER_FLAG], +[ + +CFLAGS_save="${CFLAGS}" +CFLAGS="${CFLAGS} $1" + +AC_LANG_PUSH([C]) +AC_MSG_CHECKING([whether the compiler supports $1]) + +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]])], + [have_flag="yes"], + [have_flag="no"]) +AC_MSG_RESULT([${have_flag}]) + +if test "x${have_flag}" = "xno" ; then + CFLAGS="${CFLAGS_save}" +fi +AC_LANG_POP([C]) + +]) + +dnl Macro that checks if a linker flag is supported by the compiler. + +dnl Usage: EFL_LINKER_FLAG(flag) +dnl flag is added to CFLAGS if supported (will be passed to ld anyway). + +AC_DEFUN([EFL_LINKER_FLAG], +[ + +CFLAGS_save="${CFLAGS}" +CFLAGS="${CFLAGS} $1" + +AC_LANG_PUSH([C]) +AC_MSG_CHECKING([whether the compiler supports $1]) + +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[]])], + [have_flag="yes"], + [have_flag="no"]) +AC_MSG_RESULT([${have_flag}]) + +if test "x${have_flag}" = "xno" ; then + CFLAGS="${CFLAGS_save}" +fi +AC_LANG_POP([C]) + +]) diff --git a/m4/efl_coverage.m4 b/m4/efl_coverage.m4 new file mode 100644 index 0000000..85d0321 --- /dev/null +++ b/m4/efl_coverage.m4 @@ -0,0 +1,62 @@ +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that check if coverage support is wanted and, if yes, if +dnl lcov is available. + +dnl Usage: EFL_CHECK_COVERAGE(tests [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl The parameter 'tests' is used if a dependency is needed. If set to "yes", +dnl the dependency is available. +dnl Defines EFL_COVERAGE_CFLAGS and EFL_COVERAGE_LIBS variables +dnl Defines the automake conditionnal EFL_ENABLE_COVERAGE + +AC_DEFUN([EFL_CHECK_COVERAGE], +[ + +dnl configure option + +AC_ARG_ENABLE([coverage], + [AC_HELP_STRING([--enable-coverage], [enable coverage profiling instrumentation @<:@default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + _efl_enable_coverage="yes" + else + _efl_enable_coverage="no" + fi + ], + [_efl_enable_coverage="no"]) + +AC_MSG_CHECKING([whether to use profiling instrumentation]) +AC_MSG_RESULT([$_efl_enable_coverage]) + +dnl lcov check + +if test "x$_efl_enable_coverage" = "xyes" && test ! "x$1" = "xyes" ; then + AC_MSG_WARN([Coverage report requested but tests not being built, disable profiling instrumentation.]) + AC_MSG_WARN([Run configure with --enable-tests]) + _efl_enable_coverage="no" +fi + +if test "x$_efl_enable_coverage" = "xyes" ; then + AC_CHECK_PROG(have_lcov, [lcov], [yes], [no]) + if test "x$have_lcov" = "xyes" ; then + EFL_COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage" + EFL_COVERAGE_LIBS="-lgcov" +# remove any optimisation flag and force debug symbols + EFL_DEBUG_CFLAGS="-g -O0 -DDEBUG" + else + AC_MSG_WARN([lcov is not found, disable profiling instrumentation]) + _efl_enable_coverage="no" + fi +fi + +dnl Substitution +AC_SUBST(EFL_COVERAGE_CFLAGS) +AC_SUBST(EFL_COVERAGE_LIBS) + +AM_CONDITIONAL(EFL_ENABLE_COVERAGE, test "x${_efl_enable_coverage}" = "xyes") + +AS_IF([test "x$_efl_enable_coverage" = "xyes"], [$2], [$3]) +]) + +dnl End of efl_coverage.m4 diff --git a/m4/efl_doxygen.m4 b/m4/efl_doxygen.m4 new file mode 100644 index 0000000..d83ed68 --- /dev/null +++ b/m4/efl_doxygen.m4 @@ -0,0 +1,97 @@ +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that check if doxygen is available or not. + +dnl EFL_CHECK_DOXYGEN([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for the doxygen program +dnl Defines efl_doxygen +dnl Defines the automake conditionnal EFL_BUILD_DOC +dnl +AC_DEFUN([EFL_CHECK_DOXYGEN], +[ + +dnl +dnl Disable the build of the documentation +dnl +AC_ARG_ENABLE([doc], + [AC_HELP_STRING( + [--disable-doc], + [Disable documentation build @<:@default=enabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + efl_enable_doc="yes" + else + efl_enable_doc="no" + fi + ], + [efl_enable_doc="yes"]) + +AC_MSG_CHECKING([whether to build documentation]) +AC_MSG_RESULT([${efl_enable_doc}]) + +if test "x${efl_enable_doc}" = "xyes" ; then + +dnl Specify the file name, without path + + efl_doxygen="doxygen" + + AC_ARG_WITH([doxygen], + [AC_HELP_STRING( + [--with-doxygen=FILE], + [doxygen program to use @<:@default=doxygen@:>@])], + +dnl Check the given doxygen program. + + [efl_doxygen=${withval} + AC_CHECK_PROG([efl_have_doxygen], + [${efl_doxygen}], + [yes], + [no]) + if test "x${efl_have_doxygen}" = "xno" ; then + echo "WARNING:" + echo "The doxygen program you specified:" + echo "${efl_doxygen}" + echo "was not found. Please check the path and make sure " + echo "the program exists and is executable." + AC_MSG_WARN([no doxygen detected. Documentation will not be built]) + fi + ], + [AC_CHECK_PROG([efl_have_doxygen], + [${efl_doxygen}], + [yes], + [no]) + if test "x${efl_have_doxygen}" = "xno" ; then + echo "WARNING:" + echo "The doxygen program was not found in your execute path." + echo "You may have doxygen installed somewhere not covered by your path." + echo "" + echo "If this is the case make sure you have the packages installed, AND" + echo "that the doxygen program is in your execute path (see your" + echo "shell manual page on setting the \$PATH environment variable), OR" + echo "alternatively, specify the program to use with --with-doxygen." + AC_MSG_WARN([no doxygen detected. Documentation will not be built]) + fi + ]) +fi + +dnl +dnl Substitution +dnl +AC_SUBST([efl_doxygen]) + +if ! test "x${efl_have_doxygen}" = "xyes" ; then + efl_enable_doc="no" +fi + +AM_CONDITIONAL(EFL_BUILD_DOC, test "x${efl_enable_doc}" = "xyes") + +if test "x${efl_enable_doc}" = "xyes" ; then + m4_default([$1], [:]) +else + m4_default([$2], [:]) +fi + +]) + +dnl End of efl_doxygen.m4 diff --git a/m4/efl_path_max.m4 b/m4/efl_path_max.m4 new file mode 100644 index 0000000..3ac29bd --- /dev/null +++ b/m4/efl_path_max.m4 @@ -0,0 +1,37 @@ +dnl Check for PATH_MAX in limits.h, and define a default value if not found +dnl This is a workaround for systems not providing PATH_MAX, like GNU/Hurd + +dnl EFL_CHECK_PATH_MAX([DEFAULT_VALUE_IF_NOT_FOUND]) +dnl +dnl If PATH_MAX is not defined in , defines it +dnl to DEFAULT_VALUE_IF_NOT_FOUND if it exists, or fallback +dnl to using 4096 + +AC_DEFUN([EFL_CHECK_PATH_MAX], +[ + +default_max=m4_default([$1], "4096") + +AC_LANG_PUSH([C]) + +AC_MSG_CHECKING([for PATH_MAX in limits.h]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ +#include + ]], + [[ +int i = PATH_MAX; + ]])], + [AC_MSG_RESULT([yes])], + [ + AC_DEFINE_UNQUOTED([PATH_MAX], + [${default_max}], + [default value since PATH_MAX is not defined]) + AC_MSG_RESULT([no: using ${default_max}]) + ]) + +AC_LANG_POP([C]) + +]) +dnl end of efl_path_max.m4 diff --git a/m4/efl_tests.m4 b/m4/efl_tests.m4 new file mode 100644 index 0000000..3a4dfe2 --- /dev/null +++ b/m4/efl_tests.m4 @@ -0,0 +1,43 @@ +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that check if tests programs are wanted and if yes, if +dnl the Check library is available. + +dnl Usage: EFL_CHECK_TESTS([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Define the automake conditionnal EFL_ENABLE_TESTS + +AC_DEFUN([EFL_CHECK_TESTS], +[ + +dnl configure option + +AC_ARG_ENABLE([tests], + [AC_HELP_STRING([--enable-tests], [enable tests @<:@default=disabled@:>@])], + [ + if test "x${enableval}" = "xyes" ; then + _efl_enable_tests="yes" + else + _efl_enable_tests="no" + fi + ], + [_efl_enable_tests="no"]) + +AC_MSG_CHECKING([whether tests are built]) +AC_MSG_RESULT([${_efl_enable_tests}]) + +AC_REQUIRE([PKG_PROG_PKG_CONFIG]) + +if test "x${_efl_enable_tests}" = "xyes" ; then + PKG_CHECK_MODULES([CHECK], + [check >= 0.9.5], + [dummy="yes"], + [_efl_enable_tests="no"]) +fi + +AM_CONDITIONAL(EFL_ENABLE_TESTS, test "x${_efl_enable_tests}" = "xyes") + +AS_IF([test "x$_efl_enable_tests" = "xyes"], [$1], [$2]) +]) + +dnl End of efl_tests.m4 diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..6e30c56 --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,12 @@ +cs +de +el +es +fr +it +ja +ko +nl +pt +ru +sl diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 0000000..22837ab --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --from-code=UTF-8 --foreign-user + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Enlightenment development team + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = enlightenment-devel@lists.sourceforge.net + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..68cb9d8 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1 @@ +src/lib/efreet_base.c diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..b82c806 --- /dev/null +++ b/po/cs.po @@ -0,0 +1,20 @@ +# Czech translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Plocha" diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..20395bd --- /dev/null +++ b/po/de.po @@ -0,0 +1,20 @@ +# German translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Arbeitsfläche" diff --git a/po/efreet.pot b/po/efreet.pot new file mode 100644 index 0000000..23abe0b --- /dev/null +++ b/po/efreet.pot @@ -0,0 +1,22 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Enlightenment development team +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: efreet 1.7.0\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "" diff --git a/po/el.po b/po/el.po new file mode 100644 index 0000000..5c6ac3c --- /dev/null +++ b/po/el.po @@ -0,0 +1,20 @@ +# Greek translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Επιφάνεια εργασίας" diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..5fdf7d8 --- /dev/null +++ b/po/es.po @@ -0,0 +1,20 @@ +# Spanish translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Escritorio" diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..1edbecc --- /dev/null +++ b/po/fr.po @@ -0,0 +1,20 @@ +# French translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Bureau" diff --git a/po/it.po b/po/it.po new file mode 100644 index 0000000..89cc64c --- /dev/null +++ b/po/it.po @@ -0,0 +1,20 @@ +# Italian translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Scrivania" diff --git a/po/ja.po b/po/ja.po new file mode 100644 index 0000000..5aacd28 --- /dev/null +++ b/po/ja.po @@ -0,0 +1,20 @@ +# Japanese translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "デスクトップ" diff --git a/po/ko.po b/po/ko.po new file mode 100644 index 0000000..6d4e439 --- /dev/null +++ b/po/ko.po @@ -0,0 +1,20 @@ +# Korean translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "바탕화면" diff --git a/po/nl.po b/po/nl.po new file mode 100644 index 0000000..c88443c --- /dev/null +++ b/po/nl.po @@ -0,0 +1,20 @@ +# Dutch translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Bureaublad" diff --git a/po/pt.po b/po/pt.po new file mode 100644 index 0000000..59e2827 --- /dev/null +++ b/po/pt.po @@ -0,0 +1,20 @@ +# Portuguese translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Área de Trabalho" diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 0000000..442bec1 --- /dev/null +++ b/po/ru.po @@ -0,0 +1,20 @@ +# Russian translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Рабочий стол" diff --git a/po/sl.po b/po/sl.po new file mode 100644 index 0000000..a5c6772 --- /dev/null +++ b/po/sl.po @@ -0,0 +1,20 @@ +# Slovenian translation for Efreet. +# Copyright (C) 2012 Enlightenment development team +# This file is put in the public domain. +# +msgid "" +msgstr "" +"Project-Id-Version: Efreet\n" +"Report-Msgid-Bugs-To: enlightenment-devel@lists.sourceforge.net\n" +"POT-Creation-Date: 2012-08-27 19:16+0900\n" +"PO-Revision-Date: 2012-06-24 17:10+0900\n" +"Last-Translator: Jerome Pinot \n" +"Language-Team: Enlightenment Team\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/lib/efreet_base.c:127 +msgid "Desktop" +msgstr "Namizje" diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..add035f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,4 @@ + +SUBDIRS = lib bin tests + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am new file mode 100644 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/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 +#endif +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#endif +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + +#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/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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include +#include +#include + +#include +#include +#include + +/* 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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include + +#ifdef _WIN32 +# include +#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..a53c8e1 --- /dev/null +++ b/src/lib/efreet_cache.c @@ -0,0 +1,1398 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +/* TODO: Consider flushing local icons cache after idling. + * Icon requests will probably come in batches, f.ex. during menu + * browsing. + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* define macros and variable for using the eina logging system */ +#define EFREET_MODULE_LOG_DOM _efreet_cache_log_dom +static int _efreet_cache_log_dom = -1; + +#include "Efreet.h" +#include "efreet_private.h" +#include "efreet_cache_private.h" + +#define NON_EXISTING (void *)-1 + +typedef struct _Efreet_Old_Cache Efreet_Old_Cache; + +struct _Efreet_Old_Cache +{ + Eina_Hash *hash; + Eet_File *ef; +}; + +/** + * Data for cache files + */ +static Eet_Data_Descriptor *directory_edd = NULL; +static Eet_Data_Descriptor *icon_theme_edd = NULL; +static Eet_Data_Descriptor *icon_theme_directory_edd = NULL; + +static Eet_Data_Descriptor *icon_fallback_edd = NULL; +static Eet_Data_Descriptor *icon_element_pointer_edd = NULL; +static Eet_Data_Descriptor *icon_element_edd = NULL; +static Eet_Data_Descriptor *icon_edd = NULL; + +static Eet_File *icon_cache = NULL; +static Eet_File *fallback_cache = NULL; +static Eet_File *icon_theme_cache = NULL; + +static Eina_Hash *themes = NULL; +static Eina_Hash *icons = NULL; +static Eina_Hash *fallbacks = NULL; + +static const char *icon_theme_cache_file = NULL; + +static const char *theme_name = NULL; + +static Eet_Data_Descriptor *version_edd = NULL; +static Eet_Data_Descriptor *desktop_edd = NULL; +static Eet_Data_Descriptor *hash_array_string_edd = NULL; +static Eet_Data_Descriptor *array_string_edd = NULL; +static Eet_Data_Descriptor *hash_string_edd = NULL; + +static Eina_Hash *desktops = NULL; +static Eina_List *desktop_dirs_add = NULL; +static Eet_File *desktop_cache = NULL; +static const char *desktop_cache_file = NULL; + +static Ecore_File_Monitor *cache_monitor = NULL; + +static Ecore_Event_Handler *cache_exe_handler = NULL; +static Ecore_Timer *icon_cache_timer = NULL; +static Ecore_Exe *icon_cache_exe = NULL; +static int icon_cache_exe_lock = -1; +static Ecore_Timer *desktop_cache_timer = NULL; +static Ecore_Exe *desktop_cache_exe = NULL; +static int desktop_cache_exe_lock = -1; + +static Eina_List *old_desktop_caches = NULL; + +static const char *util_cache_file = NULL; +static Eet_File *util_cache = NULL; +static Efreet_Cache_Hash *util_cache_hash = NULL; +static const char *util_cache_hash_key = NULL; +static Efreet_Cache_Array_String *util_cache_names = NULL; +static const char *util_cache_names_key = NULL; + +static void efreet_cache_edd_shutdown(void); +static void efreet_cache_icon_free(Efreet_Cache_Icon *icon); +static void efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon); +static void efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme); + +static Eina_Bool efreet_cache_check(Eet_File **ef, const char *path, int major); +static void *efreet_cache_close(Eet_File *ef); + +static Eina_Bool cache_exe_cb(void *data, int type, void *event); +static Eina_Bool cache_check_change(const char *path); +static void cache_update_cb(void *data, Ecore_File_Monitor *em, + Ecore_File_Event event, const char *path); + +static Eina_Bool desktop_cache_update_cache_cb(void *data); +static Eina_Bool icon_cache_update_cache_cb(void *data); +static void desktop_cache_update_free(void *data, void *ev); +static void icon_cache_update_free(void *data, void *ev); + +static void *hash_array_string_add(void *hash, const char *key, void *data); + +EAPI int EFREET_EVENT_ICON_CACHE_UPDATE = 0; +EAPI int EFREET_EVENT_DESKTOP_CACHE_UPDATE = 0; +EAPI int EFREET_EVENT_DESKTOP_CACHE_BUILD = 0; + +int +efreet_cache_init(void) +{ + char buf[PATH_MAX]; + + _efreet_cache_log_dom = eina_log_domain_register("efreet_cache", EFREET_DEFAULT_LOG_COLOR); + if (_efreet_cache_log_dom < 0) + return 0; + + EFREET_EVENT_ICON_CACHE_UPDATE = ecore_event_type_new(); + EFREET_EVENT_DESKTOP_CACHE_UPDATE = ecore_event_type_new(); + EFREET_EVENT_DESKTOP_CACHE_BUILD = ecore_event_type_new(); + + themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free)); + icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free)); + fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free)); + desktops = eina_hash_string_superfast_new(NULL); + + if (efreet_cache_update) + { + snprintf(buf, sizeof(buf), "%s/efreet", efreet_cache_home_get()); + if (!ecore_file_exists(buf)) + { + if (!ecore_file_mkpath(buf)) + { + ERR("Failed to create directory '%s'", buf); + goto error; + } + efreet_setowner(buf); + } + + cache_exe_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + cache_exe_cb, NULL); + if (!cache_exe_handler) + { + ERR("Failed to add exe del handler"); + goto error; + } + + cache_monitor = ecore_file_monitor_add(buf, + cache_update_cb, + NULL); + if (!cache_monitor) + { + ERR("Failed to set up ecore file monitor for '%s'", buf); + goto error; + } + + efreet_cache_icon_update(); + efreet_cache_desktop_update(); + } + + return 1; +error: + if (themes) eina_hash_free(themes); + themes = NULL; + if (icons) eina_hash_free(icons); + icons = NULL; + if (fallbacks) eina_hash_free(fallbacks); + fallbacks = NULL; + if (desktops) eina_hash_free(desktops); + desktops = NULL; + + if (cache_exe_handler) ecore_event_handler_del(cache_exe_handler); + cache_exe_handler = NULL; + if (cache_monitor) ecore_file_monitor_del(cache_monitor); + cache_monitor = NULL; + efreet_cache_edd_shutdown(); + return 0; +} + +void +efreet_cache_shutdown(void) +{ + Efreet_Old_Cache *d; + void *data; + + IF_RELEASE(theme_name); + + icon_cache = efreet_cache_close(icon_cache); + icon_theme_cache = efreet_cache_close(icon_theme_cache); + + IF_FREE_HASH(themes); + IF_FREE_HASH(icons); + IF_FREE_HASH(fallbacks); + + IF_FREE_HASH_CB(desktops, EINA_FREE_CB(efreet_cache_desktop_free)); + EINA_LIST_FREE(desktop_dirs_add, data) + eina_stringshare_del(data); + desktop_cache = efreet_cache_close(desktop_cache); + IF_RELEASE(desktop_cache_file); + + if (cache_exe_handler) ecore_event_handler_del(cache_exe_handler); + cache_exe_handler = NULL; + if (cache_monitor) ecore_file_monitor_del(cache_monitor); + cache_monitor = NULL; + + efreet_cache_edd_shutdown(); + if (desktop_cache_timer) + { + ecore_timer_del(desktop_cache_timer); + desktop_cache_timer = NULL; + } + IF_RELEASE(icon_theme_cache_file); + if (icon_cache_exe_lock > 0) + { + close(icon_cache_exe_lock); + icon_cache_exe_lock = -1; + } + + if (desktop_cache_exe_lock > 0) + { + close(desktop_cache_exe_lock); + desktop_cache_exe_lock = -1; + } + + if (old_desktop_caches) + ERR("This application has not properly closed all its desktop references!"); + EINA_LIST_FREE(old_desktop_caches, d) + { + eina_hash_free(d->hash); + eet_close(d->ef); + free(d); + } + + IF_RELEASE(util_cache_names_key); + efreet_cache_array_string_free(util_cache_names); + util_cache_names = NULL; + + IF_RELEASE(util_cache_hash_key); + if (util_cache_hash) + { + eina_hash_free(util_cache_hash->hash); + free(util_cache_hash); + util_cache_hash = NULL; + } + + util_cache = efreet_cache_close(util_cache); + IF_RELEASE(util_cache_file); + + eina_log_domain_unregister(_efreet_cache_log_dom); + _efreet_cache_log_dom = -1; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI const char * +efreet_icon_cache_file(const char *theme) +{ + static char cache_file[PATH_MAX] = { '\0' }; + const char *cache; + + EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL); + + cache = efreet_cache_home_get(); + + snprintf(cache_file, sizeof(cache_file), "%s/efreet/icons_%s_%s.eet", cache, theme, efreet_hostname_get()); + + return cache_file; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI const char * +efreet_icon_theme_cache_file(void) +{ + char tmp[PATH_MAX] = { '\0' }; + + if (icon_theme_cache_file) return icon_theme_cache_file; + + snprintf(tmp, sizeof(tmp), "%s/efreet/icon_themes_%s.eet", + efreet_cache_home_get(), efreet_hostname_get()); + icon_theme_cache_file = eina_stringshare_add(tmp); + + return icon_theme_cache_file; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI const char * +efreet_desktop_util_cache_file(void) +{ + char tmp[PATH_MAX] = { '\0' }; + const char *cache_dir, *lang, *country, *modifier; + + if (util_cache_file) return util_cache_file; + + cache_dir = efreet_cache_home_get(); + lang = efreet_lang_get(); + country = efreet_lang_country_get(); + modifier = efreet_lang_modifier_get(); + + if (lang && country && modifier) + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s@%s.eet", cache_dir, efreet_hostname_get(), lang, country, modifier); + else if (lang && country) + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s.eet", cache_dir, efreet_hostname_get(), lang, country); + else if (lang) + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s.eet", cache_dir, efreet_hostname_get(), lang); + else + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s.eet", cache_dir, efreet_hostname_get()); + + util_cache_file = eina_stringshare_add(tmp); + return util_cache_file; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_version_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (version_edd) return version_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Version); + version_edd = eet_data_descriptor_file_new(&eddc); + if (!version_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version, + "minor", minor, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version, + "major", major, EET_T_UCHAR); + + return version_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_hash_array_string_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (hash_array_string_edd) return hash_array_string_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash); + eddc.func.hash_add = hash_array_string_add; + hash_array_string_edd = eet_data_descriptor_file_new(&eddc); + if (!hash_array_string_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_HASH(hash_array_string_edd, Efreet_Cache_Hash, + "hash", hash, efreet_array_string_edd()); + + return hash_array_string_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_hash_string_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (hash_string_edd) return hash_string_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash); + hash_string_edd = eet_data_descriptor_file_new(&eddc); + if (!hash_string_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_HASH_STRING(hash_string_edd, Efreet_Cache_Hash, + "hash", hash); + + return hash_string_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_array_string_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (array_string_edd) return array_string_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Array_String); + array_string_edd = eet_data_descriptor_file_new(&eddc); + if (!array_string_edd) return NULL; + EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(array_string_edd, Efreet_Cache_Array_String, + "array", array); + + return array_string_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI const char * +efreet_desktop_cache_file(void) +{ + char tmp[PATH_MAX] = { '\0' }; + const char *cache, *lang, *country, *modifier; + + if (desktop_cache_file) return desktop_cache_file; + + cache = efreet_cache_home_get(); + lang = efreet_lang_get(); + country = efreet_lang_country_get(); + modifier = efreet_lang_modifier_get(); + + if (lang && country && modifier) + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s@%s.eet", cache, efreet_hostname_get(), lang, country, modifier); + else if (lang && country) + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s.eet", cache, efreet_hostname_get(), lang, country); + else if (lang) + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s.eet", cache, efreet_hostname_get(), lang); + else + snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s.eet", cache, efreet_hostname_get()); + + desktop_cache_file = eina_stringshare_add(tmp); + return desktop_cache_file; +} + +#define EDD_SHUTDOWN(Edd) \ + if (Edd) eet_data_descriptor_free(Edd); \ +Edd = NULL; + +static void +efreet_cache_edd_shutdown(void) +{ + EDD_SHUTDOWN(version_edd); + EDD_SHUTDOWN(desktop_edd); + EDD_SHUTDOWN(hash_array_string_edd); + EDD_SHUTDOWN(array_string_edd); + EDD_SHUTDOWN(hash_string_edd); + EDD_SHUTDOWN(icon_theme_edd); + EDD_SHUTDOWN(icon_theme_directory_edd); + EDD_SHUTDOWN(directory_edd); + EDD_SHUTDOWN(icon_fallback_edd); + EDD_SHUTDOWN(icon_element_pointer_edd); + EDD_SHUTDOWN(icon_element_edd); + EDD_SHUTDOWN(icon_edd); +} + +#define EFREET_POINTER_TYPE(Edd_Dest, Edd_Source, Type) \ +{ \ + typedef struct _Efreet_##Type##_Pointer Efreet_##Type##_Pointer; \ + struct _Efreet_##Type##_Pointer \ + { \ + Efreet_##Type *pointer; \ + }; \ + \ + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_##Type##_Pointer); \ + Edd_Dest = eet_data_descriptor_file_new(&eddc); \ + EET_DATA_DESCRIPTOR_ADD_SUB(Edd_Dest, Efreet_##Type##_Pointer, \ + "pointer", pointer, Edd_Source); \ +} + +static Eet_Data_Descriptor * +efreet_icon_directory_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (directory_edd) return directory_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Directory); + directory_edd = eet_data_descriptor_file_new(&eddc); + if (!directory_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory, + "modified_time", modified_time, EET_T_LONG_LONG); + + return directory_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_icon_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (icon_edd) return icon_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Element); + icon_element_edd = eet_data_descriptor_file_new(&eddc); + if (!icon_element_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element, + "type", type, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element, + "normal", normal, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element, + "normal", normal, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element, + "min", min, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element, + "max", max, EET_T_USHORT); + EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_element_edd, Efreet_Cache_Icon_Element, + "paths", paths); + + EFREET_POINTER_TYPE(icon_element_pointer_edd, icon_element_edd, Cache_Icon_Element); + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon); + icon_edd = eet_data_descriptor_file_new(&eddc); + if (!icon_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_edd, Efreet_Cache_Icon, + "theme", theme, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(icon_edd, Efreet_Cache_Icon, + "icons", icons, icon_element_pointer_edd); + + return icon_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_icon_theme_edd(Eina_Bool cache) +{ + Eet_Data_Descriptor_Class eddc; + + if (icon_theme_edd) return icon_theme_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Icon_Theme_Directory); + icon_theme_directory_edd = eet_data_descriptor_file_new(&eddc); + if (!icon_theme_directory_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "name", name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "context", context, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "type", type, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "size.normal", size.normal, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "size.min", size.min, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "size.max", size.max, EET_T_UINT); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory, + "size.threshold", size.threshold, EET_T_UINT); + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Theme); + icon_theme_edd = eet_data_descriptor_file_new(&eddc); + if (!icon_theme_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme, + "name.internal", theme.name.internal, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme, + "name.name", theme.name.name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme, + "comment", theme.comment, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme, + "example_icon", theme.example_icon, EET_T_STRING); + + eet_data_descriptor_element_add(icon_theme_edd, "paths", EET_T_STRING, EET_G_LIST, + offsetof(Efreet_Cache_Icon_Theme, theme.paths), 0, NULL, NULL); + eet_data_descriptor_element_add(icon_theme_edd, "inherits", EET_T_STRING, EET_G_LIST, + offsetof(Efreet_Cache_Icon_Theme, theme.inherits), 0, NULL, NULL); + EET_DATA_DESCRIPTOR_ADD_LIST(icon_theme_edd, Efreet_Cache_Icon_Theme, + "directories", theme.directories, icon_theme_directory_edd); + + if (cache) + { + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme, + "last_cache_check", last_cache_check, EET_T_LONG_LONG); + + EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme, + "path", path, EET_T_STRING); + + EET_DATA_DESCRIPTOR_ADD_HASH(icon_theme_edd, Efreet_Cache_Icon_Theme, + "dirs", dirs, efreet_icon_directory_edd()); + } + + return icon_theme_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_icon_fallback_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (icon_fallback_edd) return icon_fallback_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Fallback_Icon); + icon_fallback_edd = eet_data_descriptor_file_new(&eddc); + if (!icon_fallback_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_fallback_edd, + Efreet_Cache_Fallback_Icon, "icons", icons); + + return icon_fallback_edd; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI Eet_Data_Descriptor * +efreet_desktop_edd(void) +{ + Eet_Data_Descriptor_Class eddc; + + if (desktop_edd) return desktop_edd; + + EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Desktop); + desktop_edd = eet_data_descriptor_file_new(&eddc); + if (!desktop_edd) return NULL; + + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "type", desktop.type, EET_T_INT); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "version", desktop.version, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "orig_path", desktop.orig_path, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "load_time", desktop.load_time, EET_T_LONG_LONG); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "name", desktop.name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "generic_name", desktop.generic_name, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "comment", desktop.comment, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "icon", desktop.icon, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "try_exec", desktop.try_exec, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "exec", desktop.exec, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "path", desktop.path, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_wm_class", desktop.startup_wm_class, EET_T_STRING); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "url", desktop.url, EET_T_STRING); + eet_data_descriptor_element_add(desktop_edd, "only_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.only_show_in), 0, NULL, NULL); + eet_data_descriptor_element_add(desktop_edd, "not_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.not_show_in), 0, NULL, NULL); + eet_data_descriptor_element_add(desktop_edd, "categories", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.categories), 0, NULL, NULL); + eet_data_descriptor_element_add(desktop_edd, "mime_types", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.mime_types), 0, NULL, NULL); + eet_data_descriptor_element_add(desktop_edd, "x", EET_T_STRING, EET_G_HASH, offsetof(Efreet_Cache_Desktop, desktop.x), 0, NULL, NULL); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "no_display", desktop.no_display, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "hidden", desktop.hidden, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "terminal", desktop.terminal, EET_T_UCHAR); + EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_notify", desktop.startup_notify, EET_T_UCHAR); + + return desktop_edd; +} + +Efreet_Cache_Icon * +efreet_cache_icon_find(Efreet_Icon_Theme *theme, const char *icon) +{ + Efreet_Cache_Icon *cache = NULL; + + if (theme_name && strcmp(theme_name, theme->name.internal)) + { + /* FIXME: this is bad if people have pointer to this cache, things will go wrong */ + INF("theme_name change from `%s` to `%s`", theme_name, theme->name.internal); + IF_RELEASE(theme_name); + icon_cache = efreet_cache_close(icon_cache); + eina_hash_free(icons); + icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free)); + } + + if (!efreet_cache_check(&icon_cache, efreet_icon_cache_file(theme->name.internal), EFREET_ICON_CACHE_MAJOR)) return NULL; + if (!theme_name) + theme_name = eina_stringshare_add(theme->name.internal); + + cache = eina_hash_find(icons, icon); + if (cache == NON_EXISTING) return NULL; + if (cache) return cache; + + cache = eet_data_read(icon_cache, efreet_icon_edd(), icon); + if (cache) + eina_hash_add(icons, icon, cache); + else + eina_hash_add(icons, icon, NON_EXISTING); + return cache; +} + +Efreet_Cache_Fallback_Icon * +efreet_cache_icon_fallback_find(const char *icon) +{ + Efreet_Cache_Fallback_Icon *cache; + + if (!efreet_cache_check(&fallback_cache, efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EFREET_ICON_CACHE_MAJOR)) return NULL; + + cache = eina_hash_find(fallbacks, icon); + if (cache == NON_EXISTING) return NULL; + if (cache) return cache; + + cache = eet_data_read(fallback_cache, efreet_icon_fallback_edd(), icon); + if (cache) + eina_hash_add(fallbacks, icon, cache); + else + eina_hash_add(fallbacks, icon, NON_EXISTING); + return cache; +} + +Efreet_Icon_Theme * +efreet_cache_icon_theme_find(const char *theme) +{ + Efreet_Cache_Icon_Theme *cache; + + if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL; + + cache = eina_hash_find(themes, theme); + if (cache == NON_EXISTING) return NULL; + if (cache) return &(cache->theme); + + cache = eet_data_read(icon_theme_cache, efreet_icon_theme_edd(EINA_FALSE), theme); + if (cache) + { + eina_hash_add(themes, theme, cache); + return &(cache->theme); + } + else + eina_hash_add(themes, theme, NON_EXISTING); + return NULL; +} + +static void +efreet_cache_icon_free(Efreet_Cache_Icon *icon) +{ + unsigned int i; + + if (!icon) return; + if (icon == NON_EXISTING) return; + + for (i = 0; i < icon->icons_count; ++i) + { + free(icon->icons[i]->paths); + free(icon->icons[i]); + } + + free(icon->icons); + free(icon); +} + +static void +efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon) +{ + if (!icon) return; + if (icon == NON_EXISTING) return; + + free(icon->icons); + free(icon); +} + +static void +efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme) +{ + void *data; + + if (!theme) return; + if (theme == NON_EXISTING) return; + + eina_list_free(theme->paths); + eina_list_free(theme->inherits); + EINA_LIST_FREE(theme->directories, data) + free(data); + + free(theme); +} + +Eina_List * +efreet_cache_icon_theme_list(void) +{ + Eina_List *ret = NULL; + char **keys; + int i, num; + + if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL; + keys = eet_list(icon_theme_cache, "*", &num); + for (i = 0; i < num; i++) + { + Efreet_Icon_Theme *theme; + if (!strncmp(keys[i], "__efreet", 8)) continue; + + theme = eina_hash_find(themes, keys[i]); + if (!theme) + theme = efreet_cache_icon_theme_find(keys[i]); + if (theme && theme != NON_EXISTING) + ret = eina_list_append(ret, theme); + } + free(keys); + return ret; +} + +/* + * Needs EAPI because of helper binaries + */ +EAPI void +efreet_cache_array_string_free(Efreet_Cache_Array_String *array) +{ + if (!array) return; + free(array->array); + free(array); +} + +Efreet_Desktop * +efreet_cache_desktop_find(const char *file) +{ + Efreet_Cache_Desktop *cache; + char rp[PATH_MAX]; + + if (!realpath(file, rp)) return NULL; + + if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) return NULL; + + cache = eina_hash_find(desktops, rp); + if (cache == NON_EXISTING) return NULL; + if (cache) + { + /* If less than one second since last stat, return desktop */ + if ((ecore_time_get() - cache->check_time) < 1) + { + INF("Return without stat %f %f", ecore_time_get(), cache->check_time); + return &cache->desktop; + } + if (cache->desktop.load_time == ecore_file_mod_time(cache->desktop.orig_path)) + { + INF("Return with stat %f %f", ecore_time_get(), cache->check_time); + cache->check_time = ecore_time_get(); + return &cache->desktop; + } + + /* We got stale data. The desktop will be free'd eventually as + * users will call efreet_desktop_free */ + eina_hash_set(desktops, rp, NON_EXISTING); + cache = NULL; + } + + cache = eet_data_read(desktop_cache, efreet_desktop_edd(), rp); + if (cache) + { + if (cache->desktop.load_time != ecore_file_mod_time(cache->desktop.orig_path)) + { + /* Don't return stale data */ + INF("We got stale data in the desktop cache"); + efreet_cache_desktop_free(&cache->desktop); + eina_hash_set(desktops, rp, NON_EXISTING); + } + else + { + cache->desktop.eet = 1; + cache->check_time = ecore_time_get(); + eina_hash_set(desktops, cache->desktop.orig_path, cache); + return &cache->desktop; + } + } + else + eina_hash_set(desktops, rp, NON_EXISTING); + return NULL; +} + +void +efreet_cache_desktop_free(Efreet_Desktop *desktop) +{ + Efreet_Old_Cache *d; + Efreet_Desktop *curr; + Eina_List *l; + + if (!desktop || + desktop == NON_EXISTING || + !desktop->eet) return; + + curr = eina_hash_find(desktops, desktop->orig_path); + if (curr == desktop) + { + INF("Found in current cache, purge\n"); + eina_hash_del_by_key(desktops, desktop->orig_path); + } + + EINA_LIST_FOREACH(old_desktop_caches, l, d) + { + curr = eina_hash_find(d->hash, desktop->orig_path); + if (curr == desktop) + { + INF("Found in old cache, purge\n"); + eina_hash_del_by_key(d->hash, desktop->orig_path); + if (eina_hash_population(d->hash) == 0) + { + INF("Cache empty, close file\n"); + eina_hash_free(d->hash); + eet_close(d->ef); + free(d); + old_desktop_caches = eina_list_remove_list(old_desktop_caches, l); + } + break; + } + } + + eina_list_free(desktop->only_show_in); + eina_list_free(desktop->not_show_in); + eina_list_free(desktop->categories); + eina_list_free(desktop->mime_types); + IF_FREE_HASH(desktop->x); + free(desktop); +} + +void +efreet_cache_desktop_add(Efreet_Desktop *desktop) +{ + char buf[PATH_MAX]; + char *dir; + Efreet_Cache_Array_String *arr; + + /* + * Read file from disk, save path in cache so it will be included in next + * cache update + */ + strncpy(buf, desktop->orig_path, PATH_MAX); + buf[PATH_MAX - 1] = '\0'; + dir = dirname(buf); + arr = efreet_cache_desktop_dirs(); + if (arr) + { + unsigned int i; + + for (i = 0; i < arr->array_count; i++) + { + /* Check if we already have this dir in cache */ + if (!strcmp(dir, arr->array[i])) + return; + } + efreet_cache_array_string_free(arr); + } + if (!eina_list_search_unsorted_list(desktop_dirs_add, EINA_COMPARE_CB(strcmp), dir)) + desktop_dirs_add = eina_list_append(desktop_dirs_add, eina_stringshare_add(dir)); + + efreet_cache_desktop_update(); +} + +Efreet_Cache_Array_String * +efreet_cache_desktop_dirs(void) +{ + if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) return NULL; + + return eet_data_read(desktop_cache, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS); +} + +void +efreet_cache_desktop_update(void) +{ + if (!efreet_cache_update) return; + + if (desktop_cache_timer) + ecore_timer_delay(desktop_cache_timer, 0.2); + else + desktop_cache_timer = ecore_timer_add(0.2, desktop_cache_update_cache_cb, NULL); +} + +void +efreet_cache_desktop_close(void) +{ + IF_RELEASE(util_cache_names_key); + IF_RELEASE(util_cache_hash_key); + + if ((desktop_cache) && (desktop_cache != NON_EXISTING)) + { + Efreet_Old_Cache *d = NEW(Efreet_Old_Cache, 1); + if (d) + { + d->hash = desktops; + d->ef = desktop_cache; + old_desktop_caches = eina_list_append(old_desktop_caches, d); + } + + desktops = eina_hash_string_superfast_new(NULL); + } + desktop_cache = NULL; + + efreet_cache_array_string_free(util_cache_names); + util_cache_names = NULL; + + if (util_cache_hash) + { + eina_hash_free(util_cache_hash->hash); + free(util_cache_hash); + util_cache_hash = NULL; + } + + util_cache = efreet_cache_close(util_cache); + + IF_RELEASE(desktop_cache_file); + IF_RELEASE(util_cache_file); +} + +void +efreet_cache_icon_update(void) +{ + if (!efreet_cache_update) return; + + if (icon_cache_timer) + ecore_timer_delay(icon_cache_timer, 0.2); + else + icon_cache_timer = ecore_timer_add(0.2, icon_cache_update_cache_cb, NULL); +} + +static Eina_Bool +efreet_cache_check(Eet_File **ef, const char *path, int major) +{ + Efreet_Cache_Version *version; + + if (*ef == NON_EXISTING) return EINA_FALSE; + if (*ef) return EINA_TRUE; + if (!*ef) + *ef = eet_open(path, EET_FILE_MODE_READ); + if (!*ef) + { + *ef = NON_EXISTING; + return EINA_FALSE; + } + + version = eet_data_read(*ef, efreet_version_edd(), EFREET_CACHE_VERSION); + if ((!version) || (version->major != major)) + { + IF_FREE(version); + eet_close(*ef); + *ef = NON_EXISTING; + return EINA_FALSE; + } + free(version); + return EINA_TRUE; +} + +static void * +efreet_cache_close(Eet_File *ef) +{ + if (ef && ef != NON_EXISTING) + eet_close(ef); + return NULL; +} + +Efreet_Cache_Hash * +efreet_cache_util_hash_string(const char *key) +{ + if (util_cache_hash_key && !strcmp(key, util_cache_hash_key)) + return util_cache_hash; + if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL; + + if (util_cache_hash) + { + /* free previous util_cache */ + IF_RELEASE(util_cache_hash_key); + eina_hash_free(util_cache_hash->hash); + free(util_cache_hash); + } + util_cache_hash_key = eina_stringshare_add(key); + util_cache_hash = eet_data_read(util_cache, efreet_hash_string_edd(), key); + return util_cache_hash; +} + +Efreet_Cache_Hash * +efreet_cache_util_hash_array_string(const char *key) +{ + if (util_cache_hash_key && !strcmp(key, util_cache_hash_key)) + return util_cache_hash; + if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL; + + IF_RELEASE(util_cache_hash_key); + if (util_cache_hash) + { + /* free previous cache */ + eina_hash_free(util_cache_hash->hash); + free(util_cache_hash); + } + util_cache_hash_key = eina_stringshare_add(key); + util_cache_hash = eet_data_read(util_cache, efreet_hash_array_string_edd(), key); + return util_cache_hash; +} + +Efreet_Cache_Array_String * +efreet_cache_util_names(const char *key) +{ + if (util_cache_names_key && !strcmp(key, util_cache_names_key)) + return util_cache_names; + if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL; + + if (util_cache_names) + { + /* free previous util_cache */ + IF_RELEASE(util_cache_names_key); + efreet_cache_array_string_free(util_cache_names); + } + util_cache_names_key = eina_stringshare_add(key); + util_cache_names = eet_data_read(util_cache, efreet_array_string_edd(), key); + return util_cache_names; +} + +static Eina_Bool +cache_exe_cb(void *data __UNUSED__, int type __UNUSED__, void *event) +{ + Ecore_Exe_Event_Del *ev; + + ev = event; + if (ev->exe == desktop_cache_exe) + { + if (desktop_cache_exe_lock > 0) + { + close(desktop_cache_exe_lock); + desktop_cache_exe_lock = -1; + } + desktop_cache_exe = NULL; + } + else if (ev->exe == icon_cache_exe) + { + if (icon_cache_exe_lock > 0) + { + close(icon_cache_exe_lock); + icon_cache_exe_lock = -1; + } + icon_cache_exe = NULL; + } + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +cache_check_change(const char *path) +{ + const char *data; + Eina_Bool changed = EINA_TRUE; + Eina_File *f; + + f = eina_file_open(path, EINA_FALSE); + if (!f) return EINA_TRUE; + if (eina_file_size_get(f) < 1) return EINA_TRUE; + data = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (*data == 'n') changed = EINA_FALSE; + eina_file_close(f); + return changed; +} + +static void +cache_update_cb(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__, + Ecore_File_Event event, const char *path) +{ + const char *file; + Efreet_Event_Cache_Update *ev = NULL; + Efreet_Old_Cache *d = NULL; + Eina_List *l = NULL; + + if (event != ECORE_FILE_EVENT_CLOSED) + return; + + file = ecore_file_file_get(path); + if (!file) return; + if (!strcmp(file, "desktop_data.update")) + { + if (cache_check_change(path)) + { + ev = NEW(Efreet_Event_Cache_Update, 1); + if (!ev) goto error; + + efreet_cache_desktop_close(); + + ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_UPDATE, ev, desktop_cache_update_free, d); + } + ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_BUILD, NULL, NULL, NULL); + /* TODO: Check if desktop_dirs_add exists, and rebuild cache if */ + } + else if (!strcmp(file, "icon_data.update")) + { + if (cache_check_change(path)) + { + ev = NEW(Efreet_Event_Cache_Update, 1); + if (!ev) goto error; + + IF_RELEASE(theme_name); + + /* Save all old caches */ + d = NEW(Efreet_Old_Cache, 1); + if (!d) goto error; + d->hash = themes; + d->ef = icon_theme_cache; + l = eina_list_append(l, d); + + d = NEW(Efreet_Old_Cache, 1); + if (!d) goto error; + d->hash = icons; + d->ef = icon_cache; + l = eina_list_append(l, d); + + d = NEW(Efreet_Old_Cache, 1); + if (!d) goto error; + d->hash = fallbacks; + d->ef = fallback_cache; + l = eina_list_append(l, d); + + /* Create new empty caches */ + themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free)); + icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free)); + fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free)); + + icon_theme_cache = NULL; + icon_cache = NULL; + fallback_cache = NULL; + + /* Send event */ + ecore_event_add(EFREET_EVENT_ICON_CACHE_UPDATE, ev, icon_cache_update_free, l); + } + } + return; +error: + IF_FREE(ev); + IF_FREE(d); + EINA_LIST_FREE(l, d) + free(d); +} + +static Eina_Bool +desktop_cache_update_cache_cb(void *data __UNUSED__) +{ + char file[PATH_MAX]; + struct flock fl; + int prio; + + desktop_cache_timer = NULL; + + /* TODO: Retry update cache later */ + if (desktop_cache_exe_lock > 0) return ECORE_CALLBACK_CANCEL; + + snprintf(file, sizeof(file), "%s/efreet/desktop_exec.lock", efreet_cache_home_get()); + + desktop_cache_exe_lock = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (desktop_cache_exe_lock < 0) goto error; + efreet_fsetowner(desktop_cache_exe_lock); + memset(&fl, 0, sizeof(struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + if (fcntl(desktop_cache_exe_lock, F_SETLK, &fl) < 0) goto error; + prio = ecore_exe_run_priority_get(); + ecore_exe_run_priority_set(19); + eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_desktop_cache_create", sizeof(file)); + if (desktop_dirs_add) + { + const char *str; + + eina_strlcat(file, " -d", sizeof(file)); + EINA_LIST_FREE(desktop_dirs_add, str) + { + eina_strlcat(file, " ", sizeof(file)); + eina_strlcat(file, str, sizeof(file)); + eina_stringshare_del(str); + } + } + INF("Run desktop cache creation: %s", file); + desktop_cache_exe = ecore_exe_run(file, NULL); + ecore_exe_run_priority_set(prio); + if (!desktop_cache_exe) goto error; + + return ECORE_CALLBACK_CANCEL; +error: + if (desktop_cache_exe_lock > 0) + { + close(desktop_cache_exe_lock); + desktop_cache_exe_lock = -1; + } + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +icon_cache_update_cache_cb(void *data __UNUSED__) +{ + char file[PATH_MAX]; + struct flock fl; + int prio; + Eina_List **l, *l2; + + icon_cache_timer = NULL; + + /* TODO: Retry update cache later */ + if (icon_cache_exe_lock > 0) return ECORE_CALLBACK_CANCEL; + + snprintf(file, sizeof(file), "%s/efreet/icon_exec.lock", efreet_cache_home_get()); + + icon_cache_exe_lock = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (icon_cache_exe_lock < 0) goto error; + efreet_fsetowner(icon_cache_exe_lock); + memset(&fl, 0, sizeof(struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + if (fcntl(icon_cache_exe_lock, F_SETLK, &fl) < 0) goto error; + prio = ecore_exe_run_priority_get(); + ecore_exe_run_priority_set(19); + eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_icon_cache_create", sizeof(file)); + l = efreet_icon_extra_list_get(); + if (l && eina_list_count(*l) > 0) + { + Eina_List *ll; + char *p; + + eina_strlcat(file, " -d", sizeof(file)); + EINA_LIST_FOREACH(*l, ll, p) + { + eina_strlcat(file, " ", sizeof(file)); + eina_strlcat(file, p, sizeof(file)); + } + } + l2 = efreet_icon_extensions_list_get(); + if (eina_list_count(l2) > 0) + { + Eina_List *ll; + char *p; + + eina_strlcat(file, " -e", sizeof(file)); + EINA_LIST_FOREACH(l2, ll, p) + { + eina_strlcat(file, " ", sizeof(file)); + eina_strlcat(file, p, sizeof(file)); + } + } + icon_cache_exe = ecore_exe_run(file, NULL); + ecore_exe_run_priority_set(prio); + if (!icon_cache_exe) goto error; + + return ECORE_CALLBACK_CANCEL; + +error: + if (icon_cache_exe_lock > 0) + { + close(icon_cache_exe_lock); + icon_cache_exe_lock = -1; + } + return ECORE_CALLBACK_CANCEL; +} + +static void +desktop_cache_update_free(void *data, void *ev) +{ + Efreet_Old_Cache *d; + int dangling = 0; + + d = data; + if (d && (eina_list_data_find(old_desktop_caches, d) == d)) + { + /* + * All users should now had the chance to update their pointers. + * Check whether we still have some dangling and print a warning. + * Programs might close their pointers later. + */ + if (d->hash) + { + Eina_Iterator *it; + Eina_Hash_Tuple *tuple; + + it = eina_hash_iterator_tuple_new(d->hash); + EINA_ITERATOR_FOREACH(it, tuple) + { + if (tuple->data == NON_EXISTING) continue; + WRN("%d:%s still in cache after update event!", + ((Efreet_Desktop *)tuple->data)->ref, (char *)tuple->key); + dangling++; + } + eina_iterator_free(it); + } + if (dangling != 0) + { + WRN("There are still %i desktop files with old\n" + "dangling references to desktop files. This application\n" + "has not handled the EFREET_EVENT_DESKTOP_CACHE_UPDATE\n" + "fully and released its references. Please fix the application\n" + "so it does this.", + dangling); + } + } + free(ev); +} + +static void +icon_cache_update_free(void *data, void *ev) +{ + Efreet_Old_Cache *d; + Eina_List *l; + + l = data; + EINA_LIST_FREE(l, d) + { + if (d->hash) + eina_hash_free(d->hash); + efreet_cache_close(d->ef); + free(d); + } + free(ev); +} + +static void * +hash_array_string_add(void *hash, const char *key, void *data) +{ + if (!hash) + hash = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free)); + if (!hash) + return NULL; + eina_hash_add(hash, key, data); + return hash; +} diff --git a/src/lib/efreet_cache_private.h b/src/lib/efreet_cache_private.h new file mode 100644 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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include + +/* 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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include +#include + +#ifdef _WIN32 +# include +#endif + +#include +#include + +/* 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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include +#include + +/* 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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include +#include +#include +#include + +#include + +/* 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 +#endif + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include + +/* 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 to start with */ + if (xml->tag != efreet_tag_menu) + { + WRN("Efreet_menu: Menu file didn't start with 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, "\n"); + fprintf(f, "\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, "\n"); + if (menu->name) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "%s\n", menu->name); + } + + if (indent == 0) + { + /* Only save these for the root element */ + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + } + + if (menu->desktop) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "%s\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, "\n"); + EINA_LIST_FOREACH(menu->entries, l, entry) + { + if (entry->type == EFREET_MENU_ENTRY_MENU) + { + efreet_menu_save_indent(f, indent + 2); + fprintf(f, "%s\n", entry->id); + has_menu = 1; + } + else if (entry->type == EFREET_MENU_ENTRY_DESKTOP) + { + efreet_menu_save_indent(f, indent + 2); + fprintf(f, "%s\n", entry->id); + has_desktop = 1; + } + else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR) + { + efreet_menu_save_indent(f, indent + 2); + fprintf(f, "\n"); + } + } + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + + if (has_desktop) + { + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\n"); + EINA_LIST_FOREACH(menu->entries, l, entry) + { + if (entry->type == EFREET_MENU_ENTRY_DESKTOP) + { + efreet_menu_save_indent(f, indent + 2); + fprintf(f, "%s\n", entry->id); + } + } + efreet_menu_save_indent(f, indent + 1); + fprintf(f, "\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, "\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 " + "before seeing "); + 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 \ 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 +#endif + +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +#endif + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#ifdef HAVE_ARPA_INET_H +# include +#endif + +#include +#include + +/* 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=..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 +# 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 +#endif + +#include +#include +#include +#include + +#include + +/* 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 +#endif + +#include + +#ifndef _POSIX_HOST_NAME_MAX +#define _POSIX_HOST_NAME_MAX 255 +#endif + +#ifdef HAVE_EVIL +# include +#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 :/// */ + 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 +#endif + +/* TODO: add no_display check, as we might want only displayable items */ + +#undef alloca +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include + +#include + +/* 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 +#endif + +#include +#include +#include +#include +#include + +#include + +/* 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("", xml->tag); + } + else if (xml->text) + printf(">%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/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/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 +#include +#include +#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 +#include +#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/entry b/src/tests/data/entry new file mode 100644 index 0000000..503d8e5 Binary files /dev/null and b/src/tests/data/entry differ diff --git a/src/tests/data/entry.png b/src/tests/data/entry.png new file mode 100644 index 0000000..82e5cbe Binary files /dev/null and b/src/tests/data/entry.png differ diff --git a/src/tests/data/long.ini b/src/tests/data/long.ini new file mode 100644 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 @@ + + + + Preferences + Preferences.directory + + blah + /var/tmp + + + BlahBorp + + + + House + House.directory + + House + Garden + + + + Mouse + House.directory + + House + Garden + + + + House + House.directory + + Cat + + + + 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/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 @@ + + + + Applications + Applications.directory + + + + + applications-merged + + /usr/share/applnk + + + + + + More + + + + Foo + Bar + Foo2 + Bar2 + + + + Preferences + Preferences.directory + preferences.menu + + + + Office + Office.directory + + Office + + + foo.desktop + + + bar.desktop + + + + + + 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_bad_comment.menu b/src/tests/data/test_menu_bad_comment.menu new file mode 100644 index 0000000..5815b89 --- /dev/null +++ b/src/tests/data/test_menu_bad_comment.menu @@ -0,0 +1,14 @@ + + + + Applications + Applications.directory + + + This should be commented + --> + + 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 @@ + + + + Applications/Bar + + + Preferences/Baz + + 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 +#include +#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 +#include +#include +#include + +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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include + +#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 +#include +#include + +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 +#include +#include + +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 +#include + +#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 +#include +#include +#include + +#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 + +#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 + +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 +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#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 +#endif + +#include +#include + +#include + +#include "efreet_suite.h" + +typedef struct _Efreet_Test_Case Efreet_Test_Case; + +struct _Efreet_Test_Case +{ + const char *test_case; + void (*build)(TCase *tc); +}; + +static const Efreet_Test_Case etc[] = { + { "Efreet", efreet_test_efreet }, + { "Efreet Cache", efreet_test_efreet_cache }, + { NULL, NULL } +}; + +static void +_list_tests(void) +{ + const Efreet_Test_Case *itr; + + itr = etc; + fputs("Available Test Cases:\n", stderr); + for (; itr->test_case; itr++) + fprintf(stderr, "\t%s\n", itr->test_case); +} + +static Eina_Bool +_use_test(int argc, const char **argv, const char *test_case) +{ + if (argc < 1) + return 1; + + for (; argc > 0; argc--, argv++) + if (strcmp(test_case, *argv) == 0) + return 1; + return 0; +} + +static Suite * +efreet_suite_build(int argc, const char **argv) +{ + TCase *tc; + Suite *s; + int i; + + s = suite_create("Efreet"); + + for (i = 0; etc[i].test_case; ++i) + { + if (!_use_test(argc, argv, etc[i].test_case)) continue; + tc = tcase_create(etc[i].test_case); + + etc[i].build(tc); + + suite_add_tcase(s, tc); + tcase_set_timeout(tc, 0); + } + + return s; +} + +int +main(int argc, char **argv) +{ + Suite *s; + SRunner *sr; + int i, failed_count; + + for (i = 1; i < argc; i++) + if ((strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0)) + { + fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n", + argv[0]); + _list_tests(); + return 0; + } + else if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "--list") == 0)) + { + _list_tests(); + return 0; + } + + s = efreet_suite_build(argc - 1, (const char **)argv + 1); + sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + failed_count = srunner_ntests_failed(sr); + srunner_free(sr); + + return (failed_count == 0) ? 0 : 255; +} diff --git a/src/tests/efreet_suite.h b/src/tests/efreet_suite.h new file mode 100644 index 0000000..bfb47f9 --- /dev/null +++ b/src/tests/efreet_suite.h @@ -0,0 +1,10 @@ +#ifndef _EFREET_SUITE_H +#define _EFREET_SUITE_H + +#include + +void efreet_test_efreet(TCase *tc); +void efreet_test_efreet_cache(TCase *tc); + + +#endif /* _EFREET_SUITE_H */ diff --git a/src/tests/efreet_test_efreet.c b/src/tests/efreet_test_efreet.c new file mode 100644 index 0000000..d35ec9e --- /dev/null +++ b/src/tests/efreet_test_efreet.c @@ -0,0 +1,25 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#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 +#endif + +#include + +#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 +#include +#include +#include + +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; +}