Initial import of efreet, an implementation of the freedesktop icon and menu
authorningerso <ningerso>
Sat, 3 Feb 2007 13:05:30 +0000 (13:05 +0000)
committerningerso <ningerso@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sat, 3 Feb 2007 13:05:30 +0000 (13:05 +0000)
specs.

SVN revision: 28214

44 files changed:
legacy/efreet/AUTHORS [new file with mode: 0644]
legacy/efreet/COPYING [new file with mode: 0644]
legacy/efreet/Doxyfile [new file with mode: 0644]
legacy/efreet/NOTES [new file with mode: 0644]
legacy/efreet/TODO [new file with mode: 0644]
legacy/efreet/autogen.sh [new file with mode: 0755]
legacy/efreet/configure.in [new file with mode: 0644]
legacy/efreet/efreet-config.in [new file with mode: 0644]
legacy/efreet/m4/ac_attribute.m4 [new file with mode: 0644]
legacy/efreet/m4/ac_define_dir.m4 [new file with mode: 0644]
legacy/efreet/m4/ac_path_generic.m4 [new file with mode: 0644]
legacy/efreet/src/Makefile.am [new file with mode: 0644]
legacy/efreet/src/bin/Makefile.am [new file with mode: 0644]
legacy/efreet/src/bin/compare/Makefile.am [new file with mode: 0644]
legacy/efreet/src/bin/compare/comp.h [new file with mode: 0644]
legacy/efreet/src/bin/compare/compare_results.c [new file with mode: 0644]
legacy/efreet/src/bin/compare/ecore_alloc.c [new file with mode: 0644]
legacy/efreet/src/bin/compare/efreet_alloc.c [new file with mode: 0644]
legacy/efreet/src/bin/compare/efreet_menu_alloc.c [new file with mode: 0644]
legacy/efreet/src/bin/ef_data_dirs.c [new file with mode: 0644]
legacy/efreet/src/bin/ef_desktop.c [new file with mode: 0644]
legacy/efreet/src/bin/ef_icon_theme.c [new file with mode: 0644]
legacy/efreet/src/bin/ef_ini.c [new file with mode: 0644]
legacy/efreet/src/bin/ef_locale.c [new file with mode: 0644]
legacy/efreet/src/bin/ef_menu.c [new file with mode: 0644]
legacy/efreet/src/bin/efreet_spec_test.c [new file with mode: 0644]
legacy/efreet/src/bin/main.c [new file with mode: 0644]
legacy/efreet/src/lib/Efreet.h [new file with mode: 0644]
legacy/efreet/src/lib/Makefile.am [new file with mode: 0644]
legacy/efreet/src/lib/efreet.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_base.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_base.h [new file with mode: 0644]
legacy/efreet/src/lib/efreet_desktop.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_desktop.h [new file with mode: 0644]
legacy/efreet/src/lib/efreet_icon.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_icon.h [new file with mode: 0644]
legacy/efreet/src/lib/efreet_icon_new.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_ini.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_ini.h [new file with mode: 0644]
legacy/efreet/src/lib/efreet_menu.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_menu.h [new file with mode: 0644]
legacy/efreet/src/lib/efreet_private.h [new file with mode: 0644]
legacy/efreet/src/lib/efreet_xml.c [new file with mode: 0644]
legacy/efreet/src/lib/efreet_xml.h [new file with mode: 0644]

diff --git a/legacy/efreet/AUTHORS b/legacy/efreet/AUTHORS
new file mode 100644 (file)
index 0000000..83fd9e4
--- /dev/null
@@ -0,0 +1,4 @@
+dan 'dj2' sinclair
+Brian 'rephorm' Mattern
+Sebastian 'englebass' Dransfeld
+Nathan 'RbdPngn' Ingersoll
diff --git a/legacy/efreet/COPYING b/legacy/efreet/COPYING
new file mode 100644 (file)
index 0000000..376a24a
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (C) 2006 dan sinclair and various contributors (see AUTHORS)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+  
+The above copyright notice and this permission notice shall be included in
+all copies of the Software, its documentation and marketing & publicity 
+materials, and acknowledgment shall be given in the documentation, materials
+and software packages that this Software was used.
+   
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/legacy/efreet/Doxyfile b/legacy/efreet/Doxyfile
new file mode 100644 (file)
index 0000000..6905a9a
--- /dev/null
@@ -0,0 +1,1252 @@
+# Doxyfile 1.4.7
+
+# 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
+#---------------------------------------------------------------------------
+
+# 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         = 0.0.3
+
+# 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       = docs
+
+# 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: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# 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    = YES
+
+# 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        = YES
+
+# 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 is your file systems 
+# 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 the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_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 DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# 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               = 4
+
+# 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
+
+# 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 make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# 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
+
+#---------------------------------------------------------------------------
+# 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            = YES
+
+# 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  = YES
+
+# 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 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      = YES
+
+# 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          = YES
+
+# 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     = YES
+
+# 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       = YES
+
+# 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_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
+
+# 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 define 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 defines 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        = YES
+
+# 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
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# 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
+
+# This WARN_NO_PARAMDOC option can be abled 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           = efreet_doxy_warnings.txt
+
+#---------------------------------------------------------------------------
+# 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                  = src/lib
+
+# 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++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS          = 
+
+# 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              = NO
+
+# 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 filesystem 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 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             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+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
+
+#---------------------------------------------------------------------------
+# 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         = YES
+
+# 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 (the default) 
+# 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 (the default) 
+# 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 documentstion.
+
+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       = YES
+
+#---------------------------------------------------------------------------
+# 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    = 5
+
+# 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_
+
+#---------------------------------------------------------------------------
+# 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            = 
+
+# 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            = 
+
+# 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        = 
+
+# 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 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 compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# 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 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
+
+# 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          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag 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 (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = 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
+
+#---------------------------------------------------------------------------
+# 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.
+
+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, a4wide, 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
+
+#---------------------------------------------------------------------------
+# 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           = NO
+
+# 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              = NO
+
+#---------------------------------------------------------------------------
+# 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        = NO
+
+# 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     = NO
+
+# 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        = YES
+
+# 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             =__UNUSED__=
+
+# 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.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and 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 is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# 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   = NO
+
+# 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               = YES
+
+# 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            = YES
+
+# 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    = YES
+
+# 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               = YES
+
+# 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          = YES
+
+# 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      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags 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 graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# 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        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# 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 MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# 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 a graph may be further truncated if the graph's 
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH 
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), 
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# 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        = NO
+
+# 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
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/legacy/efreet/NOTES b/legacy/efreet/NOTES
new file mode 100644 (file)
index 0000000..485be66
--- /dev/null
@@ -0,0 +1,48 @@
+
+rephorm one thing. if you plan to add .desktop editing, according to the
+spec you need to keep EVERYTHING around (even sections / keys you don't know
+/ care about)
+dj2 k
+rephorm but for most usage (read only) thats a waste of memory
+dj2 hm, i guess the best way to do that is keep everything in a hash
+dj2 and write accessor functions
+dj2 or pull the common stuff out to functions and leve the rest in the hash
+rephorm and maybe have a flag for 'readonly' to kill the hash
+dj2 yea, will have to put some tought into the api for that
+
+rephorm ooh. comments need to be preserved also in edits
+
+
+
+
+rephorm efreet_xml has one bug that i see
+rephorm if you have something like <tag>before<child />after</tag>, you
+can't get to the 'after' text
+dj2 hm, will have to look at that
+rephorm (it stores 'before' as the text on 'tag')
+rephorm it should probably create child nodes with the text
+rephorm so tag would have 3 children, text: before, tag: child and text: after
+
+
+efreet_desktop_string_list_parse() needs to optionally use comma (',') as 
+the separator if the desktop version is < 1.0  (but, what if it isn't set??)
+
+
+desktop_command_get:
+  check for needed types (nNfFuU)
+
+  get lists of needed types (dirs, fullpaths, urls, etc)
+
+  if type in uU:
+    create Pending struct
+    start downloads, pass Pending in as data
+  else:
+    if tyep in UF...
+    exec
+
+download cb:
+  if type in fF
+
+  
+
+
diff --git a/legacy/efreet/TODO b/legacy/efreet/TODO
new file mode 100644 (file)
index 0000000..4173fcb
--- /dev/null
@@ -0,0 +1,20 @@
+TODO
+----
+- Efreet_Menu should setup an Ecore_File_Monitor on the .menu files and the
+  app_dir and reload the menu when needed
+
+- .desktop saving
+
+- We need a better system to distinguish between when a user wants to create a
+  new .desktop file and when he wants to parse an existing file.
+
+- 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
+
+
+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/legacy/efreet/autogen.sh b/legacy/efreet/autogen.sh
new file mode 100755 (executable)
index 0000000..995ff2f
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f aclocal.m4 ltmain.sh
+
+touch README
+
+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
+
+if [ -z "$NOCONFIGURE" ]; then
+       ./configure "$@"
+fi
diff --git a/legacy/efreet/configure.in b/legacy/efreet/configure.in
new file mode 100644 (file)
index 0000000..9bbc067
--- /dev/null
@@ -0,0 +1,80 @@
+AC_INIT(configure.in)
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AM_INIT_AUTOMAKE(efreet, 0.0.3)
+AM_CONFIG_HEADER(config.h)
+
+AC_ISC_POSIX
+AC_PROG_CC
+AM_PROG_CC_STDC
+AC_HEADER_STDC
+AC_C_CONST
+AM_ENABLE_SHARED
+AM_PROG_LIBTOOL
+AC_C___ATTRIBUTE__
+
+dnl Set PACKAGE_DATA_DIR in config.h.
+AC_DEFINE_DIR([PACKAGE_DATA_DIR], [datadir], [Shared Data Directory.])
+
+PCFLAGS=$CFLAGS
+
+AC_PATH_GENERIC(ecore, 0.9.9,
+  [ ],
+    [
+AC_MSG_ERROR([
+ERROR:
+The ecore-config development script was not found in your execute
+path. This may mean one of several things
+1. You may not have installed the ecore-devel (or ecore-dev) packages.
+2. You may have ecore installed somewhere not covered by your path.
+
+If this is the case make sure you have the packages installed, AND
+that the ecore-config script is in your execute path (see your
+shell's manual page on setting the \$PATH environment variable), OR
+alternatively, specify the script to use with --with-ecore-config.
+])
+    ]
+)
+CFLAGS=$ECORE_CFLAGS" "$CFLAGS
+
+requirements="ecore"
+
+AC_ARG_ENABLE(ecore-desktop,
+       [AC_HELP_STRING([--enable-ecore-desktop],[Enable Ecore_Desktop tests])],
+       [enable_ecore_desktop=$enableval], [enable_ecore_desktop="auto"])
+have_ecore_desktop=no
+if test "x$enable_ecore_desktop" != "xno" ; then
+       AC_CHECK_HEADER(Ecore_Desktop.h,
+               [have_ecore_desktop=yes],
+               [], [])
+       if test "x$have_ecore_desktop" = "xyes" ; then
+               AC_DEFINE(ENABLE_ECORE_DESKTOP, 1, [Enable Ecore_Desktop])
+       elif test "x$enable_ecore_desktop" = "xyes" ; then
+               AC_MSG_ERROR(No Ecore_Desktop found disabling comparison tests.)
+       fi
+fi
+AM_CONDITIONAL(HAVE_ECORE_DESKTOP, test "x$have_ecore_desktop" = xyes)
+
+AC_ARG_ENABLE(strict-spec,
+       [AC_HELP_STRING([--enable-strict-spec],[Enable strict spec compliance])],
+       [enable_strict_spec=$enableval], [enable_strict_spec="auto"])
+STRICT_SPEC=0
+if test "x$enable_strict_spec" = "xyes" ; then
+    STRICT_SPEC=1
+fi
+AC_DEFINE_UNQUOTED(STRICT_SPEC, $STRICT_SPEC, [Strict Spec Compliance])
+
+CFLAGS=$PCFLAGS
+
+AC_SUBST(requirements)
+AC_OUTPUT([
+efreet-config
+Makefile
+src/Makefile
+src/lib/Makefile
+src/bin/Makefile
+src/bin/data/Makefile
+src/bin/data/sub/Makefile
+src/bin/compare/Makefile
+], []
+)
diff --git a/legacy/efreet/efreet-config.in b/legacy/efreet/efreet-config.in
new file mode 100644 (file)
index 0000000..e41861a
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+exec_prefix_set=no
+
+usage="\
+Usage: efreet-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--libs] [--cflags]"
+
+if test $# -eq 0; then
+      echo "${usage}" 1>&2
+      exit 1
+fi
+
+while test $# -gt 0; do
+  case "$1" in
+  -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  case $1 in
+    --prefix=*)
+      prefix=$optarg
+      if test $exec_prefix_set = no ; then
+        exec_prefix=$optarg
+      fi
+      ;;
+    --prefix)
+      echo $prefix
+      ;;
+    --exec-prefix=*)
+      exec_prefix=$optarg
+      exec_prefix_set=yes
+      ;;
+    --exec-prefix)
+      echo $exec_prefix
+      ;;
+    --version)
+      echo @VERSION@
+      ;;
+    --cflags)
+      if test @prefix@/include != /usr/include ; then
+        includes="-I@prefix@/include"
+      fi
+      echo $includes @ECORE_CFLAGS@ -I$prefix/include/efreet
+      ;;
+    --libs)
+      libdirs=-L@libdir@
+      echo $libdirs -lefreet @ECORE_LIBS@ 
+      ;;
+    *)
+      echo "${usage}" 1>&2
+      exit 1
+      ;;
+  esac
+  shift
+done
+
+exit 0
diff --git a/legacy/efreet/m4/ac_attribute.m4 b/legacy/efreet/m4/ac_attribute.m4
new file mode 100644 (file)
index 0000000..9e4b6b3
--- /dev/null
@@ -0,0 +1,15 @@
+
+AC_DEFUN([AC_C___ATTRIBUTE__],
+[
+  AC_MSG_CHECKING(for __attribute__)
+  AC_CACHE_VAL(ac_cv___attribute__, [
+  AC_TRY_COMPILE([#include <stdlib.h>
+  int func(int x); int foo(int x __attribute__ ((unused))) { exit(1); }], 
+  [],
+  ac_cv___attribute__=yes, ac_cv___attribute__=no)])
+  if test "$ac_cv___attribute__" = "yes"; then
+    AC_DEFINE(HAVE___ATTRIBUTE__, 1, [Define to 1 if your compiler has __attribute__])
+  fi
+  AC_MSG_RESULT($ac_cv___attribute__)
+])
+
diff --git a/legacy/efreet/m4/ac_define_dir.m4 b/legacy/efreet/m4/ac_define_dir.m4
new file mode 100644 (file)
index 0000000..f3d8734
--- /dev/null
@@ -0,0 +1,47 @@
+##### http://autoconf-archive.cryp.to/ac_define_dir.html
+#
+# SYNOPSIS
+#
+#   AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION])
+#
+# DESCRIPTION
+#
+#   This macro sets VARNAME to the expansion of the DIR variable,
+#   taking care of fixing up ${prefix} and such.
+#
+#   VARNAME is then offered as both an output variable and a C
+#   preprocessor symbol.
+#
+#   Example:
+#
+#      AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.])
+#
+# LAST MODIFICATION
+#
+#   2006-10-13
+#
+# COPYLEFT
+#
+#   Copyright (c) 2006 Stepan Kasal <kasal@ucw.cz>
+#   Copyright (c) 2006 Andreas Schwab <schwab@suse.de>
+#   Copyright (c) 2006 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2006 Alexandre Oliva
+#
+#   Copying and distribution of this file, with or without
+#   modification, are permitted in any medium without royalty provided
+#   the copyright notice and this notice are preserved.
+
+AC_DEFUN([AC_DEFINE_DIR], [
+  prefix_NONE=
+  exec_prefix_NONE=
+  test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix
+  test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix
+dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn
+dnl refers to ${prefix}.  Thus we have to use `eval' twice.
+  eval ac_define_dir="\"[$]$2\""
+  eval ac_define_dir="\"$ac_define_dir\""
+  AC_SUBST($1, "$ac_define_dir")
+  AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3])
+  test "$prefix_NONE" && prefix=NONE
+  test "$exec_prefix_NONE" && exec_prefix=NONE
+])
diff --git a/legacy/efreet/m4/ac_path_generic.m4 b/legacy/efreet/m4/ac_path_generic.m4
new file mode 100644 (file)
index 0000000..b7a2c13
--- /dev/null
@@ -0,0 +1,138 @@
+dnl @synopsis AC_PATH_GENERIC(LIBRARY [, MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl
+dnl Runs a LIBRARY-config script and defines LIBRARY_CFLAGS and LIBRARY_LIBS
+dnl
+dnl The script must support `--cflags' and `--libs' args.
+dnl If MINIMUM-VERSION is specified, the script must also support the
+dnl `--version' arg.
+dnl If the `--with-library-[exec-]prefix' arguments to ./configure are given,
+dnl it must also support `--prefix' and `--exec-prefix'.
+dnl (In other words, it must be like gtk-config.)
+dnl
+dnl For example:
+dnl
+dnl    AC_PATH_GENERIC(Foo, 1.0.0)
+dnl
+dnl would run `foo-config --version' and check that it is at least 1.0.0
+dnl
+dnl If so, the following would then be defined:
+dnl
+dnl    FOO_CFLAGS to `foo-config --cflags`
+dnl    FOO_LIBS   to `foo-config --libs`
+dnl
+dnl At present there is no support for additional "MODULES" (see AM_PATH_GTK)
+dnl (shamelessly stolen from gtk.m4 and then hacked around a fair amount)
+dnl
+dnl @author Angus Lees <gusl@cse.unsw.edu.au>
+
+AC_DEFUN([AC_PATH_GENERIC],
+[dnl
+dnl we're going to need uppercase, lowercase and user-friendly versions of the
+dnl string `LIBRARY'
+pushdef([UP], translit([$1], [a-z], [A-Z]))dnl
+pushdef([DOWN], translit([$1], [A-Z], [a-z]))dnl
+
+dnl
+dnl Get the cflags and libraries from the LIBRARY-config script
+dnl
+AC_ARG_WITH(DOWN-prefix,
+        [  --with-]DOWN[-prefix=PFX       Prefix where $1 is installed (optional)],
+        DOWN[]_config_prefix="$withval", DOWN[]_config_prefix="")
+AC_ARG_WITH(DOWN-exec-prefix,
+        [  --with-]DOWN[-exec-prefix=PFX  Exec prefix where $1 is installed (optional)],
+        DOWN[]_config_exec_prefix="$withval", DOWN[]_config_exec_prefix="")
+
+  if test x$DOWN[]_config_exec_prefix != x ; then
+     DOWN[]_config_args="$DOWN[]_config_args --exec-prefix=$DOWN[]_config_exec_prefix"
+     if test x${UP[]_CONFIG+set} != xset ; then
+       UP[]_CONFIG=$DOWN[]_config_exec_prefix/bin/DOWN-config
+     fi
+  fi
+  if test x$DOWN[]_config_prefix != x ; then
+     DOWN[]_config_args="$DOWN[]_config_args --prefix=$DOWN[]_config_prefix"
+     if test x${UP[]_CONFIG+set} != xset ; then
+       UP[]_CONFIG=$DOWN[]_config_prefix/bin/DOWN-config
+     fi
+  fi
+
+  AC_PATH_PROG(UP[]_CONFIG, DOWN-config, no)
+  ifelse([$2], ,
+     AC_MSG_CHECKING(for $1),
+     AC_MSG_CHECKING(for $1 - version >= $2)
+  )
+  no_[]DOWN=""
+  if test "$UP[]_CONFIG" = "no" ; then
+     no_[]DOWN=yes
+  else
+     UP[]_CFLAGS="`$UP[]_CONFIG $DOWN[]_config_args --cflags`"
+     UP[]_LIBS="`$UP[]_CONFIG $DOWN[]_config_args --libs`"
+     ifelse([$2], , ,[
+        DOWN[]_config_major_version=`$UP[]_CONFIG $DOWN[]_config_args \
+         --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\).*/\1/'`
+        DOWN[]_config_minor_version=`$UP[]_CONFIG $DOWN[]_config_args \
+         --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\).*/\2/'`
+        DOWN[]_config_micro_version=`$UP[]_CONFIG $DOWN[]_config_args \
+         --version | sed 's/[[^0-9]]*\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\).*/\3/'`
+        DOWN[]_wanted_major_version="regexp($2, [\<\([0-9]*\)], [\1])"
+        DOWN[]_wanted_minor_version="regexp($2, [\<\([0-9]*\)\.\([0-9]*\)], [\2])"
+        DOWN[]_wanted_micro_version="regexp($2, [\<\([0-9]*\).\([0-9]*\).\([0-9]*\)], [\3])"
+
+        # Compare wanted version to what config script returned.
+        # If I knew what library was being run, i'd probably also compile
+        # a test program at this point (which also extracted and tested
+        # the version in some library-specific way)
+        if test "$DOWN[]_config_major_version" -lt \
+                        "$DOWN[]_wanted_major_version" \
+          -o \( "$DOWN[]_config_major_version" -eq \
+                        "$DOWN[]_wanted_major_version" \
+            -a "$DOWN[]_config_minor_version" -lt \
+                        "$DOWN[]_wanted_minor_version" \) \
+          -o \( "$DOWN[]_config_major_version" -eq \
+                        "$DOWN[]_wanted_major_version" \
+            -a "$DOWN[]_config_minor_version" -eq \
+                        "$DOWN[]_wanted_minor_version" \
+            -a "$DOWN[]_config_micro_version" -lt \
+                        "$DOWN[]_wanted_micro_version" \) ; then
+          # older version found
+          no_[]DOWN=yes
+          echo -n "*** An old version of $1 "
+          echo -n "($DOWN[]_config_major_version"
+          echo -n ".$DOWN[]_config_minor_version"
+          echo    ".$DOWN[]_config_micro_version) was found."
+          echo -n "*** You need a version of $1 newer than "
+          echo -n "$DOWN[]_wanted_major_version"
+          echo -n ".$DOWN[]_wanted_minor_version"
+          echo    ".$DOWN[]_wanted_micro_version."
+          echo "***"
+          echo "*** If you have already installed a sufficiently new version, this error"
+          echo "*** probably means that the wrong copy of the DOWN-config shell script is"
+          echo "*** being found. The easiest way to fix this is to remove the old version"
+          echo "*** of $1, but you can also set the UP[]_CONFIG environment to point to the"
+          echo "*** correct copy of DOWN-config. (In this case, you will have to"
+          echo "*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf"
+          echo "*** so that the correct libraries are found at run-time)"
+        fi
+     ])
+  fi
+  if test "x$no_[]DOWN" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$3], , :, [$3])
+  else
+     AC_MSG_RESULT(no)
+     if test "$UP[]_CONFIG" = "no" ; then
+       echo "*** The DOWN-config script installed by $1 could not be found"
+       echo "*** If $1 was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the UP[]_CONFIG environment variable to the"
+       echo "*** full path to DOWN-config."
+     fi
+     UP[]_CFLAGS=""
+     UP[]_LIBS=""
+     ifelse([$4], , :, [$4])
+  fi
+  AC_SUBST(UP[]_CFLAGS)
+  AC_SUBST(UP[]_LIBS)
+
+  popdef([UP])
+  popdef([DOWN])
+])
+
diff --git a/legacy/efreet/src/Makefile.am b/legacy/efreet/src/Makefile.am
new file mode 100644 (file)
index 0000000..ad292fa
--- /dev/null
@@ -0,0 +1,4 @@
+
+SUBDIRS = lib bin
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/legacy/efreet/src/bin/Makefile.am b/legacy/efreet/src/bin/Makefile.am
new file mode 100644 (file)
index 0000000..46dbfbd
--- /dev/null
@@ -0,0 +1,25 @@
+SUBDIRS = data compare
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = \
+-I. -I$(top_srcdir)/src/lib @ECORE_CFLAGS@
+
+bin_PROGRAMS = efreet_test efreet_spec_test
+
+efreet_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+efreet_test_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@
+efreet_test_SOURCES = \
+ef_data_dirs.c \
+ef_icon_theme.c \
+ef_ini.c \
+ef_locale.c \
+ef_desktop.c \
+ef_menu.c \
+main.c
+
+
+efreet_spec_test_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+efreet_spec_test_LDADD = $(top_builddir)/src/lib/libefreet.la
+efreet_spec_test_SOURCES = \
+efreet_spec_test.c
+
diff --git a/legacy/efreet/src/bin/compare/Makefile.am b/legacy/efreet/src/bin/compare/Makefile.am
new file mode 100644 (file)
index 0000000..cffa31d
--- /dev/null
@@ -0,0 +1,29 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = \
+-I. -I$(top_srcdir)/src/lib @ECORE_CFLAGS@
+
+if HAVE_ECORE_DESKTOP
+bin_PROGRAMS = efreet_alloc ecore_alloc compare_results efreet_menu_alloc
+else
+bin_PROGRAMS = efreet_alloc efreet_menu_alloc
+endif
+
+efreet_menu_alloc_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+efreet_menu_alloc_SOURCES = efreet_menu_alloc.c comp.h
+efreet_menu_alloc_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@
+
+efreet_alloc_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+efreet_alloc_SOURCES = efreet_alloc.c comp.h
+efreet_alloc_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@
+
+if HAVE_ECORE_DESKTOP
+ecore_alloc_DEPENDENCIES = 
+ecore_alloc_SOURCES = ecore_alloc.c comp.h
+ecore_alloc_LDADD = @ECORE_LIBS@
+
+compare_results_DEPENDENCIES = $(top_builddir)/src/lib/libefreet.la
+compare_results_SOURCES = compare_results.c comp.h
+compare_results_LDADD = $(top_builddir)/src/lib/libefreet.la @ECORE_LIBS@
+endif
+
diff --git a/legacy/efreet/src/bin/compare/comp.h b/legacy/efreet/src/bin/compare/comp.h
new file mode 100644 (file)
index 0000000..c5628bf
--- /dev/null
@@ -0,0 +1,528 @@
+#ifndef COMP_H
+#define COMP_H
+
+#define LOOPS 1000
+#define THEME "Tango"
+#define SIZE "16x16"
+
+#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/legacy/efreet/src/bin/compare/compare_results.c b/legacy/efreet/src/bin/compare/compare_results.c
new file mode 100644 (file)
index 0000000..4710343
--- /dev/null
@@ -0,0 +1,43 @@
+#include <Ecore.h>
+#include <Ecore_Desktop.h>
+#include <Efreet.h>
+#include <stdio.h>
+#include <string.h>
+#include "comp.h"
+
+int
+main(int argc, char ** argv)
+{
+    int i;
+    const char *ef, *ed;
+
+    ecore_init();
+    ecore_desktop_init();
+    efreet_init();
+
+    efreet_icon_extension_add(".svg");
+
+    for (i = 0; icons[i] != NULL; i++)
+    {
+        ef = efreet_icon_path_find(THEME, icons[i], SIZE);
+        ed = ecore_desktop_icon_find(icons[i], SIZE, THEME);
+
+        if (!ef && !ed) continue;
+
+        if (!ef && ed)
+            printf("%s matched ecore (%s) but not efreet\n", icons[i], ed);
+
+        else if (ef && !ed)
+            printf("%s matched efreet (%s) but not ecore\n", icons[i], ef);
+
+        else if (strcmp(ef, ed))
+            printf("%s didn't match ef(%s) vs ed(%s)\n", icons[i], ef, ed);
+    }
+
+    efreet_shutdown();
+    ecore_desktop_shutdown();
+    ecore_shutdown();
+
+    return 0;
+}
+
diff --git a/legacy/efreet/src/bin/compare/ecore_alloc.c b/legacy/efreet/src/bin/compare/ecore_alloc.c
new file mode 100644 (file)
index 0000000..b94731a
--- /dev/null
@@ -0,0 +1,29 @@
+#include <Ecore.h>
+#include <Ecore_Desktop.h>
+#include <stdio.h>
+#include "comp.h"
+
+int
+main(int argc, char ** argv)
+{
+    int i = 0, k;
+    const char *path;
+
+    ecore_init();
+    ecore_desktop_init();
+
+    for (k = 0; k < LOOPS; k++)
+    {
+        for (i = 0; icons[i] != NULL; i++)
+        {
+            path = ecore_desktop_icon_find(icons[i], SIZE, THEME);
+//            printf("%s: %s\n", icons[i], (path ? path : "NOT FOUND"));
+        }
+    }
+
+    ecore_desktop_shutdown();
+    ecore_shutdown();
+
+    return 0;
+}
+
diff --git a/legacy/efreet/src/bin/compare/efreet_alloc.c b/legacy/efreet/src/bin/compare/efreet_alloc.c
new file mode 100644 (file)
index 0000000..ccf4060
--- /dev/null
@@ -0,0 +1,26 @@
+#include <Efreet.h>
+#include <stdio.h>
+#include "comp.h"
+
+int
+main(int argc, char **argv)
+{
+    int i = 0, k;
+    const char *path;
+
+    efreet_init();
+
+    for (k = 0; k < LOOPS; k++)
+    {
+        for (i = 0; icons[i] != NULL; i++)
+        {
+            path = efreet_icon_path_find(THEME, icons[i], SIZE);
+//            printf("%s: %s\n", icons[i], (path ? path : "NOT FOUND"));
+        }
+    }
+
+    efreet_shutdown();
+
+    return 0;
+}
+
diff --git a/legacy/efreet/src/bin/compare/efreet_menu_alloc.c b/legacy/efreet/src/bin/compare/efreet_menu_alloc.c
new file mode 100644 (file)
index 0000000..5a43a0c
--- /dev/null
@@ -0,0 +1,23 @@
+#include <Efreet.h>
+#include <stdio.h>
+#include "comp.h"
+
+int
+main(int argc, char **argv)
+{
+    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/legacy/efreet/src/bin/ef_data_dirs.c b/legacy/efreet/src/bin/ef_data_dirs.c
new file mode 100644 (file)
index 0000000..c24dce7
--- /dev/null
@@ -0,0 +1,297 @@
+#include "Efreet.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int
+ef_cb_efreet_data_home(void)
+{
+    const char *tmp;
+    int ret = 1;
+
+    putenv("XDG_DATA_HOME=/var/tmp");
+
+    tmp = efreet_data_home_get();
+    if (strcmp(tmp, "/var/tmp"))
+    {
+        printf("efreet_data_home_get() returned incorrect "
+                "value on XDG_DATA_HOME=/var/tmp\n");
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_DATA_HOME=");
+    putenv("HOME=/home/tmp");
+
+    tmp = efreet_data_home_get();
+    if (strcmp(tmp, "/home/tmp/.local/share"))
+    {
+        printf("efreet_data_home_get() returned incorrect "
+                "value on blank XDG_DATA_HOME\n");
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_DATA_HOME=");
+    putenv("HOME=");
+
+    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;
+
+    putenv("XDG_CONFIG_HOME=/var/tmp");
+
+    tmp = efreet_config_home_get();
+    if (strcmp(tmp, "/var/tmp"))
+    {
+        printf("efreet_config_home_get() returned incorrect "
+                "value on XDG_CONFIG_HOME=/var/tmp\n");
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_CONFIG_HOME=");
+    putenv("HOME=/home/tmp");
+
+    tmp = efreet_config_home_get();
+    if (strcmp(tmp, "/home/tmp/.config"))
+    {
+        printf("efreet_config_home_get() returned incorrect "
+                "value on blank XDG_CONFIG_HOME\n");
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_CONFIG_HOME=");
+    putenv("HOME=");
+
+    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;
+
+    putenv("XDG_CACHE_HOME=/var/tmp");
+
+    tmp = efreet_cache_home_get();
+    if (strcmp(tmp, "/var/tmp"))
+    {
+        printf("efreet_cache_home_get() returned incorrect "
+                "value on XDG_CACHE_HOME=/var/tmp\n");
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_CACHE_HOME=");
+    putenv("HOME=/home/tmp");
+
+    tmp = efreet_cache_home_get();
+    if (strcmp(tmp, "/home/tmp/.cache"))
+    {
+        printf("efreet_cache_home_get() returned incorrect "
+                "value on blank XDG_CACHE_HOME\n");
+        ret = 0;
+    }
+
+    /* reset efreet here so we can set a new home dir */
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_CACHE_HOME=");
+    putenv("HOME=");
+
+    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)
+{
+    Ecore_List *tmp;
+    int ret = 1, i;
+    char dirs[128], *val;
+    char *vals[] = {"/var/tmp/a", "/tmp/b", "/usr/local/share", "/etc", NULL};
+    char *def_vals[] = {"/usr/local/share", "/usr/share", NULL};
+
+    dirs[0] = '\0';
+    strcat(dirs, "XDG_DATA_DIRS=");
+    for (i = 0; vals[i] != NULL; i++)
+    {
+        if (i > 0) strcat(dirs, ":");
+        strcat(dirs, vals[i]);
+    }
+
+    putenv(dirs);
+
+    i = 0;
+    tmp = efreet_data_dirs_get();
+    ecore_list_goto_first(tmp);
+    while ((val = ecore_list_next(tmp)))
+    {
+        if (vals[i] == NULL)
+        {
+            printf("efreet_data_dirs_get() returned more values then it "
+                    "should have given %s as input\n", dirs);
+            ret = 0;
+            break;
+        }
+
+        if (strcmp(val, vals[i]))
+        {
+            printf("efreet_data_dirs_get() returned incorrect value (%s) when "
+                    "%s set\n", val, dirs);
+            ret = 0;
+        }
+
+        i++;
+    }
+
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_DATA_DIRS=");
+
+    i = 0;
+    tmp = efreet_data_dirs_get();
+    ecore_list_goto_first(tmp);
+    while ((val = ecore_list_next(tmp)))
+    {
+        if (def_vals[i] == NULL)
+        {
+            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\n", val);
+            ret = 0;
+        }
+
+        i++;
+    }
+    return ret;
+}
+
+int
+ef_cb_efreet_config_dirs(void)
+{
+    Ecore_List *tmp;
+    int ret = 1, i;
+    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';
+
+    strcat(dirs, "XDG_CONFIG_DIRS=");
+    for (i = 0; vals[i] != NULL; i++)
+    {
+        if (i > 0) strcat(dirs, ":");
+        strcat(dirs, vals[i]);
+    }
+
+    putenv(dirs);
+
+    i = 0;
+    tmp = efreet_config_dirs_get();
+    ecore_list_goto_first(tmp);
+    while ((val = ecore_list_next(tmp)))
+    {
+        if (vals[i] == NULL)
+        {
+            printf("efreet_config_dirs_get() returned more values then it "
+                    "should have given %s as input\n", dirs);
+            ret = 0;
+            break;
+        }
+
+        if (strcmp(val, vals[i]))
+        {
+            printf("efreet_config_dirs_get() returned incorrect value (%s) when "
+                    "%s set\n", val, dirs);
+            ret = 0;
+        }
+
+        i++;
+    }
+
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("XDG_CONFIG_DIRS=");
+
+    i = 0;
+    tmp = efreet_config_dirs_get();
+    ecore_list_goto_first(tmp);
+    while ((val = ecore_list_next(tmp)))
+    {
+        if (def_vals[i] == NULL)
+        {
+            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/legacy/efreet/src/bin/ef_desktop.c b/legacy/efreet/src/bin/ef_desktop.c
new file mode 100644 (file)
index 0000000..33e783d
--- /dev/null
@@ -0,0 +1,346 @@
+#include "Efreet.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include "../lib/efreet_private.h"
+
+static void _cb_command(void *data, Efreet_Desktop *desktop, char *exec, int remaining);
+
+
+int
+ef_cb_desktop_parse(void)
+{
+    Efreet_Desktop *desktop;
+    int ret = 1;
+
+    desktop = efreet_desktop_get(PACKAGE_DATA_DIR"/efreet/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;
+
+        ecore_list_goto_first(desktop->categories);
+        while ((cat = ecore_list_next(desktop->categories)))
+        {
+            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;
+
+    return ret;
+}
+
+#if 0
+int
+ef_cb_desktop_file_id(void)
+{
+    Efreet_Desktop *desktop;
+    int ret = 1;
+
+    desktop = efreet_desktop_get(PACKAGE_DATA_DIR"/efreet/test/test.desktop");
+    if (desktop)
+    {
+        const char *id;
+        int i = 0;
+
+        struct {
+            char *dir;
+            int legacy;
+            char *prefix;
+            char *expected;
+        } tests[] = {
+            {PACKAGE_DATA_DIR"/efreet/test/", 0, NULL, "test.desktop"},
+            {PACKAGE_DATA_DIR"/efreet/", 0, NULL, "test-test.desktop"},
+            {PACKAGE_DATA_DIR"/efreet/", 1, NULL, "test.desktop"},
+            {PACKAGE_DATA_DIR"/efreet/", 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) ecore_string_release(id);
+        }
+    }
+    else
+        ret = 0;
+
+    return ret;
+}
+#endif
+
+int
+ef_cb_desktop_save(void)
+{
+    Efreet_Desktop *desktop;
+
+    printf("\n");
+    desktop = efreet_desktop_get(PACKAGE_DATA_DIR"/efreet/test/test.desktop");
+    printf("save data: %d\n", efreet_desktop_save(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 = ecore_list_new();
+    ecore_list_set_free_cb(desktop->categories, ECORE_FREE_CB(free));
+    ecore_list_append(desktop->categories, strdup("Test"));
+    ecore_list_append(desktop->categories, strdup("Enlightenment"));
+    printf("save test: %d\n", efreet_desktop_save(desktop));
+    unlink("/tmp/test.desktop");
+#if 0
+    /* After saving a .desktop, it should be in the cache. This should then
+     * be destroyed with it. */
+    ecore_list_destroy(desktop->categories);
+    desktop->categories = NULL;
+#endif
+
+    return 1;
+}
+
+typedef struct
+{
+  Ecore_List *expected;
+  int error;
+  char type;
+} Test_Info;
+
+int
+ef_cb_desktop_command_get(void)
+{
+    Efreet_Desktop *desktop;
+    Ecore_List *files, *expected;
+    char olddir[PATH_MAX];
+    Test_Info *info;
+    int ret;
+
+    getcwd(olddir, PATH_MAX);
+    chdir("/");
+
+    printf("\n");
+    desktop = efreet_desktop_empty_new("test.desktop");
+
+    desktop->name = strdup("App Name");
+    desktop->icon = strdup("icon.png");
+
+    files = ecore_list_new();
+    ecore_list_append(files, "/tmp/absolute_path");
+    ecore_list_append(files, "relative_path");
+    ecore_list_append(files, "file:///tmp/absolute_uri");
+    ecore_list_append(files, "file:relative_uri");
+
+    info = NEW(Test_Info, 1);
+    expected = ecore_list_new();
+    info->expected = expected;
+    info->error = 0;
+
+    /* test single full path */
+    info->type = 'f';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %f");
+    ecore_list_append(expected, "app '/tmp/absolute_path'");
+    ecore_list_append(expected, "app '/relative_path'");
+    ecore_list_append(expected, "app '/tmp/absolute_uri'");
+    ecore_list_append(expected, "app '/relative_uri'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test single uri */
+    info->type = 'u';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %u");
+    ecore_list_append(expected, "app 'file:///tmp/absolute_path'");
+    ecore_list_append(expected, "app 'file:///relative_path'");
+    ecore_list_append(expected, "app 'file:///tmp/absolute_uri'");
+    ecore_list_append(expected, "app 'file:///relative_uri'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test single dir */
+    info->type = 'd';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %d");
+    ecore_list_append(expected, "app '/tmp'");
+    ecore_list_append(expected, "app '/'");
+    ecore_list_append(expected, "app '/tmp'");
+    ecore_list_append(expected, "app '/'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+
+    /* test single names */
+    info->type = 'n';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %n");
+    ecore_list_append(expected, "app 'absolute_path'");
+    ecore_list_append(expected, "app 'relative_path'");
+    ecore_list_append(expected, "app 'absolute_uri'");
+    ecore_list_append(expected, "app 'relative_uri'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test multiple fullpaths */
+    info->type = 'F';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %F");
+    ecore_list_append(expected, "app '/tmp/absolute_path' '/relative_path' '/tmp/absolute_uri' '/relative_uri'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test multiple URIs */
+    info->type = 'U';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %U");
+    ecore_list_append(expected, "app 'file:///tmp/absolute_path' 'file:///relative_path' 'file:///tmp/absolute_uri' 'file:///relative_uri'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test multiple dirs */
+    info->type = 'D';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %D");
+    ecore_list_append(expected, "app '/tmp' '/' '/tmp' '/'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test multiple names */
+    info->type = 'N';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %N");
+    ecore_list_append(expected, "app 'absolute_path' 'relative_path' 'absolute_uri' 'relative_uri'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, files, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test icon appending */
+    info->type = 'i';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %i");
+    ecore_list_append(expected, "app --icon 'icon.png'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, NULL, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test app name */
+    info->type = 'c';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %c");
+    ecore_list_append(expected, "app 'App Name'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, NULL, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* test desktop path */
+    info->type = 'k';
+    IF_FREE(desktop->exec);
+    desktop->exec = strdup("app %k");
+    ecore_list_append(expected, "app 'test.desktop'");
+
+    ecore_list_goto_first(expected);
+    efreet_desktop_command_get(desktop, NULL, _cb_command, info);
+    ecore_list_clear(expected);
+
+    /* clean up */
+    efreet_desktop_free(desktop);
+    ecore_list_destroy(files);
+    ecore_list_destroy(expected);
+
+    ret = info->error > 0 ? 0 : 1;
+    free(info);
+    chdir(olddir);
+
+    return ret;
+}
+
+static void
+_cb_command(void *data, Efreet_Desktop *desktop, char *exec, int remaining)
+{
+  Test_Info *info = data;
+  char *expected;
+
+  expected = ecore_list_next(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);
+}
diff --git a/legacy/efreet/src/bin/ef_icon_theme.c b/legacy/efreet/src/bin/ef_icon_theme.c
new file mode 100644 (file)
index 0000000..4ae7542
--- /dev/null
@@ -0,0 +1,602 @@
+#include "Efreet.h"
+#include "efreet_private.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <Ecore_File.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define SIZE "16x16"
+#define THEME "Tango"
+
+static void ef_icon_theme_themes_find(const char *search_dir, 
+                                        Ecore_Hash *themes);
+static void ef_icons_find(Efreet_Icon_Theme *theme, Ecore_List *themes, 
+                                                    Ecore_Hash *icons);
+static void ef_read_dir(const char *dir, Ecore_Hash *icons);
+
+int
+ef_cb_efreet_icon_theme(void)
+{
+    int ret = 1;
+    const char *tmp;
+
+    putenv("HOME=/var/tmp");
+
+    tmp = efreet_icon_dir_get();
+    if (strcmp(tmp, "/var/tmp/.icons"))
+    {
+        printf("efreet_icon_dir_get() returned incorrect "
+                "value on HOME=/var/tmp\n");
+        ret = 0;
+    }
+
+    efreet_shutdown();
+    efreet_init();
+
+    putenv("HOME=");
+
+    tmp = efreet_icon_dir_get();
+    if (strcmp(tmp, "/tmp/.icons"))
+    {
+        printf("efreet_icon_dir_get() returned incorrect "
+                "value on HOME=\n");
+        ret = 0;
+    }
+
+    return ret;
+}
+
+int
+ef_cb_efreet_icon_theme_list(void)
+{
+    int ret = 1;
+    Ecore_List *themes;
+    Ecore_Hash *dirs;
+    Efreet_Icon_Theme *theme;
+    Ecore_List *icon_dirs;
+    const char *dir;
+    char buf[PATH_MAX];
+
+    dirs = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(dirs, free);
+
+    icon_dirs = efreet_data_dirs_get();
+    ecore_list_goto_first(icon_dirs);
+
+    ef_icon_theme_themes_find(efreet_icon_dir_get(), dirs);
+    while ((dir = ecore_list_next(icon_dirs))) 
+    {
+        snprintf(buf, sizeof(buf), "%s/icons", dir);
+        ef_icon_theme_themes_find(buf, dirs);
+    }
+    ef_icon_theme_themes_find("/usr/share/pixmaps", dirs);
+   
+    themes = efreet_icon_theme_list_get();
+    ecore_list_goto_first(themes);
+    while ((theme = ecore_list_next(themes)))
+    {
+        if (ecore_hash_get(dirs, theme->name.internal))
+            ecore_hash_remove(dirs, theme->name.internal);
+        else
+        {
+            printf("efreet_icon_theme_list_get() returned %s which we didn't "
+                    "see when scanning the directories.\n", theme->name.internal);
+            ret = 0;
+        }
+    }
+    ecore_list_destroy(themes);
+
+    themes = ecore_hash_keys(dirs);
+    if (ecore_list_nodes(themes) > 0)
+    {
+        char *dir;
+
+        printf("efreet_icon_theme_list_get() missed: ");
+        ecore_list_goto_first(themes);
+        while ((dir = ecore_list_next(themes)))
+            printf("%s ", dir);
+        printf("\n");
+
+        ret = 0;
+    }
+    ecore_list_destroy(themes);
+    ecore_hash_destroy(dirs);
+
+    return ret;
+}
+
+static void
+ef_icon_theme_themes_find(const char *search_dir, Ecore_Hash *themes)
+{
+    Ecore_List *dirs;
+    char *dir;
+
+    if (!search_dir || !themes) return;
+
+    dirs = ecore_file_ls(search_dir);
+    if (!dirs) return;
+
+    while ((dir = ecore_list_remove_first(dirs)))
+    {
+        char p[PATH_MAX];
+
+        /* if we've already added the theme we're done */
+        if (ecore_hash_get(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;
+            efreet_ini_free(ini);
+
+            if (!skip)
+            {
+                d = strdup(dir);
+                ecore_hash_set(themes, d, d);
+            }
+        }
+        free(dir);
+    }
+    ecore_list_destroy(dirs);
+}
+
+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",
+    "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",
+    "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
+};
+
+int
+ef_cb_efreet_icon_match(void)
+{
+    int i, ret = 1;
+    Ecore_Hash *icon_hash;
+    Efreet_Icon_Theme *theme;
+    Ecore_List *themes;
+
+    themes = efreet_icon_theme_list_get();
+    ecore_list_goto_first(themes);
+    while ((theme = ecore_list_next(themes)))
+    {
+        if (!strcmp(theme->name.internal, THEME))
+            break;
+    }
+
+    if (!theme)
+    {
+        printf("Theme not installed, SKIPPED.\n");
+        ecore_list_destroy(themes);
+        return 1;
+    }
+
+    icon_hash = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(icon_hash, free);
+    ecore_hash_set_free_value(icon_hash, free);
+
+    ef_icons_find(theme, themes, icon_hash); 
+    ecore_list_destroy(themes);
+
+    for (i = 0; icons[i] != NULL; i++)
+    {
+        const char *path;
+        char *t, *s;
+
+        path = efreet_icon_path_find(THEME, icons[i], SIZE);
+
+        if (!path)
+        {
+            if (ecore_hash_get(icon_hash, icons[i]))
+            {
+                printf("NOT FOUND %s\n", icons[i]);
+                ret = 0;
+            }
+            continue;
+        }
+
+        t = strdup(path);
+        s = strrchr(t, '.');
+        if (s) *s = '\0';
+        s = strrchr(t, '/');
+        if (s) s++;
+
+        if (s && strcmp(s, icons[i]))
+        {
+            printf("Name mismatch name (%s) vs ef (%s)\n", icons[i], s);
+            ret = 0;
+        }
+        free(t);
+    }
+    ecore_hash_destroy(icon_hash);
+
+    return ret;
+}
+
+static void
+ef_icons_find(Efreet_Icon_Theme *theme, Ecore_List *themes, Ecore_Hash *icons)
+{
+    char path[PATH_MAX];
+
+    if (!theme || !icons) return;
+
+    if (theme->paths.count == 1)
+    {
+        Efreet_Icon_Theme_Directory *dir;
+
+        ecore_list_goto_first(theme->directories);
+        while ((dir = ecore_list_next(theme->directories)))
+        {
+            if (theme->paths.count > 1)
+            {
+                Ecore_List *list;
+                char *tmp;
+
+                list = theme->paths.path;
+                ecore_list_goto_first(list);
+                while ((tmp = ecore_list_next(list)))
+                {
+                    snprintf(path, sizeof(path), "%s/%s/", tmp, dir->name);
+                    ef_read_dir(path, icons);
+                }
+            }
+            else if (theme->paths.count == 1)
+            {
+                snprintf(path, sizeof(path), "%s/%s/", (char *)theme->paths.path, dir->name);
+                ef_read_dir(path, icons);
+            }
+        }
+    }
+    else if (theme->paths.count > 1)
+    {
+        const char *theme_path;
+
+        ecore_list_goto_first(theme->paths.path);
+        while ((theme_path = ecore_list_next(theme->paths.path)))
+        {
+            Efreet_Icon_Theme_Directory *dir;
+
+            ecore_list_goto_first(theme->directories);
+            while ((dir = ecore_list_next(theme->directories)))
+            {
+                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;
+
+        ecore_list_goto_first(theme->inherits);
+        while ((parent = ecore_list_next(theme->inherits)))
+        {
+            ecore_list_goto_first(themes);
+            while ((parent_theme = ecore_list_next(themes)))
+            {
+                if (!strcmp(parent_theme->name.internal, parent))
+                    ef_icons_find(parent_theme, themes, icons);
+            }
+        }
+    }
+    else
+    {
+        Efreet_Icon_Theme *parent_theme;
+
+        ecore_list_goto_first(themes);
+        while ((parent_theme = ecore_list_next(themes)))
+        {
+            if (!strcmp(parent_theme->name.internal, "hicolor"))
+                ef_icons_find(parent_theme, themes, icons);
+        }
+    }
+
+    ef_read_dir("/usr/share/pixmaps", icons);
+}
+
+static void
+ef_read_dir(const char *dir, Ecore_Hash *icons)
+{
+    Ecore_List *files;
+    char *file;
+
+    if (!dir || !icons) return;
+
+    files = ecore_file_ls(dir);
+    if (!files) return;
+
+    while ((file = ecore_list_remove_first(files)))
+    {
+        char *p;
+
+        p = strrchr(file, '.');
+        if (!p) 
+        {
+            FREE(file);
+            continue;
+        }
+
+        if (!strcmp(p, ".png") || !strcmp(p, ".xpm"))
+        {
+            *p = '\0';
+
+            p = strrchr(file, '/');
+            if (p) p++;
+            if (p) ecore_hash_set(icons, strdup(p), strdup(p));
+        }
+
+        FREE(file);
+    }
+    ecore_list_destroy(files);
+}
+
diff --git a/legacy/efreet/src/bin/ef_ini.c b/legacy/efreet/src/bin/ef_ini.c
new file mode 100644 (file)
index 0000000..7736f93
--- /dev/null
@@ -0,0 +1,157 @@
+#include "Efreet.h"
+#include "efreet_private.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+int
+ef_cb_ini_parse(void)
+{
+    int ret = 1;
+    Efreet_Ini *ini;
+
+    putenv("LC_ALL=en_US");
+
+    ini = efreet_ini_new(PACKAGE_DATA_DIR"/efreet/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(PACKAGE_DATA_DIR"/efreet/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;
+}
diff --git a/legacy/efreet/src/bin/ef_locale.c b/legacy/efreet/src/bin/ef_locale.c
new file mode 100644 (file)
index 0000000..058e884
--- /dev/null
@@ -0,0 +1,83 @@
+#include <Efreet.h>
+#include "efreet_private.h"
+
+int
+ef_cb_locale(void)
+{
+    int ret = 1, i;
+    struct
+    {
+        char *lc_message;
+        char *lang;
+        char *country;
+        char *modifier;
+    } langs[] = {
+        /* these are ordered such that when we move from LANG to LC_MESSAGES 
+         * the LANG env will still be effect. Same with moving from
+         * LC_MESSAGES to LANG */
+        {"LANG=", NULL, NULL, NULL},
+        {"LANG=en", "en", NULL, NULL},
+        {"LANG=en@Latn", "en", NULL, "Latn"},
+        {"LANG=en_US", "en", "US", NULL},
+        {"LANG=en_US@Latn", "en", "US", "Latn"},
+        {"LANG=en_US.blah@Latn", "en", "US", "Latn"},
+        {"LC_MESSAGES=", "en", "US", "Latn"}, /* This will fallback to LANG */
+        {"LC_MESSAGES=fr", "fr", NULL, NULL},
+        {"LC_MESSAGES=fr@Blah", "fr", NULL, "Blah"},
+        {"LC_MESSAGES=fr_FR", "fr", "FR", NULL},
+        {"LC_MESSAGES=fr_FR@Blah", "fr", "FR", "Blah"},
+        {"LC_MESSAGES=fr_FR.Foo@Blah", "fr", "FR", "Blah"},
+        {"LC_ALL=", "fr", "FR", "Blah"}, /* this will fallback to LC_MESSAGES */
+        {"LC_ALL=sr", "sr", NULL, NULL},
+        {"LC_ALL=sr@Ret", "sr", NULL, "Ret"},
+        {"LC_ALL=sr_YU", "sr", "YU", NULL},
+        {"LC_ALL=sr_YU@Ret", "sr", "YU", "Ret"},
+        {"LC_ALL=sr_YU.ssh@Ret", "sr", "YU", "Ret"},
+        {NULL, NULL, NULL, NULL}
+    };
+
+    /* reset everything to blank */
+    putenv("LC_ALL=");
+    putenv("LC_MESSAGES=");
+    putenv("LANG=");
+
+    for (i = 0; langs[i].lc_message != NULL; 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/legacy/efreet/src/bin/ef_menu.c b/legacy/efreet/src/bin/ef_menu.c
new file mode 100644 (file)
index 0000000..0d0e1d6
--- /dev/null
@@ -0,0 +1,75 @@
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static void
+ef_menu_desktop_exec(Efreet_Menu *menu)
+{
+#if 0
+    if (menu->entries)
+    {
+        Efreet_Desktop *desktop;
+
+        ecore_list_goto_first(menu->entries);
+        while ((desktop = ecore_list_next(menu->entries)))
+            efreet_desktop_exec(desktop, NULL);
+    }
+    if (menu->sub_menus)
+    {
+        Efreet_Menu *sub_menu;
+
+        ecore_list_goto_first(menu->sub_menus);
+        while ((sub_menu = ecore_list_next(menu->sub_menus)))
+            ef_menu_desktop_exec(sub_menu);
+    }
+#endif
+}
+
+int
+ef_cb_menu_get(void)
+{
+    Efreet_Menu *menu;
+
+    menu = efreet_menu_get();
+//    menu = efreet_menu_parse(PACKAGE_DATA_DIR"/efreet/test/test.menu");
+    if (!menu)
+    {
+        printf("efreet_menu_get() returned NULL\n");
+        return 0;
+    }
+#if 0
+    if (strcmp(menu->name.internal, "Applications"))
+    {
+        printf("menu name didn't match\n");
+        return 0;
+    }
+
+    if (!menu->moves || ecore_list_nodes(menu->moves) != 2)
+    {
+        printf("Missing moves\n");
+        return 0;
+    }
+
+    if (menu->current_move)
+    {
+        printf("Current move still set\n");
+        return 0;
+    }
+
+    if (menu->filters)
+    {
+        printf("Have filters when we shouldn't\n");
+        return 0;
+    }
+    ef_menu_desktop_exec(menu);
+#endif
+    printf("\n");
+    efreet_menu_dump(menu, "");
+#if 0
+    unlink("/tmp/test.menu");
+    efreet_menu_save(menu, "/tmp/test.menu");
+#endif
+    efreet_menu_free(menu);
+
+    return 1;
+}
+
diff --git a/legacy/efreet/src/bin/efreet_spec_test.c b/legacy/efreet/src/bin/efreet_spec_test.c
new file mode 100644 (file)
index 0000000..862d4d1
--- /dev/null
@@ -0,0 +1,58 @@
+#include <Efreet.h>
+#include <stdio.h>
+
+#define PATH_MAX 4096
+
+static void dump(Efreet_Menu *menu, const char *path);
+
+int
+main(int argc, char **argv)
+{
+    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;
+
+    if (!menu || !menu->entries) return;
+
+    ecore_list_goto_first(menu->entries);
+    while ((entry = ecore_list_next(menu->entries)))
+    {
+        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, PATH_MAX, "%s%s/", path, entry->name);
+            dump(entry, new_path);
+        }
+    }
+}
+
diff --git a/legacy/efreet/src/bin/main.c b/legacy/efreet/src/bin/main.c
new file mode 100644 (file)
index 0000000..fc51286
--- /dev/null
@@ -0,0 +1,142 @@
+#include "Efreet.h"
+#include <Ecore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int ef_cb_efreet_data_home(void);
+int ef_cb_efreet_config_home(void);
+int ef_cb_efreet_cache_home(void);
+int ef_cb_efreet_data_dirs(void);
+int ef_cb_efreet_config_dirs(void);
+int ef_cb_efreet_icon_theme(void);
+int ef_cb_efreet_icon_theme_list(void);
+int ef_cb_efreet_icon_match(void);
+int ef_cb_ini_parse(void);
+int ef_cb_locale(void);
+int ef_cb_desktop_parse(void);
+#if 0
+int ef_cb_desktop_file_id(void);
+#endif
+int ef_cb_menu_get(void);
+int ef_cb_ini_long_line(void);
+int ef_cb_desktop_save(void);
+int ef_cb_desktop_command_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},
+    {"Locale Parsing", ef_cb_locale},
+    {"Desktop Parsing", ef_cb_desktop_parse},
+#if 0
+    {"Desktop File ID", ef_cb_desktop_file_id},
+#endif
+    {"Menu Parsing", ef_cb_menu_get},
+    {"Desktop Save", ef_cb_desktop_save},
+    {"Desktop Command", ef_cb_desktop_command_get},
+    {NULL, NULL}
+};
+
+extern char **environ;
+static Ecore_List *environment = NULL;
+
+void
+environment_store(void)
+{
+    char **e;
+
+    if (environment)
+        ecore_list_clear(environment);
+    else
+    {
+        environment = ecore_list_new();
+        ecore_list_set_free_cb(environment, ECORE_FREE_CB(free));
+    }
+
+    for (e = environ; *e; e++)
+        ecore_list_append(environment, strdup(*e));
+}
+
+void
+environment_restore(void)
+{
+    char *e;
+    if (!environment) return;
+
+    *environ = NULL;
+    ecore_list_goto_first(environment);
+    while ((e = ecore_list_next(environment)))
+        putenv(e);
+}
+
+int
+main(int argc, char ** argv)
+{
+    int i, passed = 0, num_tests = 0;
+    Ecore_List *run = NULL;
+    double total;
+
+    total = ecore_time_get();
+    if (argc > 1)
+    {
+        run = ecore_list_new();
+        for (i = 1; i < argc; i++)
+            ecore_list_append(run, argv[i]);
+    }
+
+    environment_store();
+    for (i = 0; tests[i].name != NULL; i++)
+    {
+        int ret;
+        double start;
+
+        /* we've been given specific tests and it isn't in the list */
+        if (run && !ecore_list_find(run, ECORE_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");
+    if (environment) ecore_list_destroy(environment);
+    printf("Passed %d of %d tests.\n", passed, num_tests);
+
+    if (run) ecore_list_destroy(run);
+
+    printf("Total run: %.3f seconds\n", ecore_time_get() - total);
+    return 0;
+}
+
diff --git a/legacy/efreet/src/lib/Efreet.h b/legacy/efreet/src/lib/Efreet.h
new file mode 100644 (file)
index 0000000..e668406
--- /dev/null
@@ -0,0 +1,44 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "efreet_base.h"
+#include "efreet_icon.h"
+#include "efreet_desktop.h"
+#include "efreet_menu.h"
+
+int efreet_init(void);
+int efreet_shutdown(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/legacy/efreet/src/lib/Makefile.am b/legacy/efreet/src/lib/Makefile.am
new file mode 100644 (file)
index 0000000..483710e
--- /dev/null
@@ -0,0 +1,37 @@
+
+MAINTAINERCLEANFILES = Makefile.in
+
+INCLUDES = \
+-I$(top_builddir) \
+-I$(top_srcdir) \
+-g -O0 -W -Wall \
+@ECORE_CFLAGS@
+
+lib_LTLIBRARIES = libefreet.la
+
+EFREETHEADERS = \
+Efreet.h \
+efreet_base.h \
+efreet_desktop.h \
+efreet_icon.h \
+efreet_menu.h
+
+EFREETSOURCES = \
+efreet.c \
+efreet_base.c \
+efreet_icon.c \
+efreet_xml.c \
+efreet_ini.c \
+efreet_desktop.c \
+efreet_menu.c \
+$(EFREETHEADERS)
+
+libefreet_la_SOURCES = \
+       $(EFREETSOURCES)
+
+installed_headersdir = $(prefix)/include/efreet
+installed_headers_DATA = $(EFREETHEADERS)
+
+libefreet_la_LIBADD = @ECORE_LIBS@
+libefreet_la_LDFLAGS = -version-info 1:0:0
+
diff --git a/legacy/efreet/src/lib/efreet.c b/legacy/efreet/src/lib/efreet.c
new file mode 100644 (file)
index 0000000..0b8f956
--- /dev/null
@@ -0,0 +1,191 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static int init = 0;
+static int efreet_parsed_locale = 0;
+static char *efreet_lang = NULL;
+static char *efreet_lang_country = NULL;
+static char *efreet_lang_modifier = NULL;
+
+static void efreet_parse_locale(void);
+static int efreet_parse_locale_setting(const char *env);
+
+/**
+ * @return Returns > 0 if the initialization was successful, 0 otherwise
+ * @brief Initializes the Efreet system
+ */
+int
+efreet_init(void)
+{
+    if (init++) return init;
+    if (!efreet_base_init()) return --init;
+    if (!efreet_xml_init()) return --init;
+    if (!efreet_icon_init()) return --init;
+    if (!efreet_ini_init()) return --init;
+    if (!efreet_desktop_init()) return --init;
+    if (!efreet_menu_init()) return --init;
+    return init;
+}
+
+/**
+ * @return Returns the number of times the init function as been called
+ * minus the corresponding init call.
+ * @brief Shuts down Efreet if a balanced number of init/shutdown calls have
+ * been made 
+ */
+int
+efreet_shutdown(void)
+{
+    if (--init) return init;
+    efreet_menu_shutdown();
+    efreet_desktop_shutdown();
+    efreet_ini_shutdown();
+    efreet_icon_shutdown();
+    efreet_xml_shutdown();
+    efreet_base_shutdown();
+
+    IF_FREE(efreet_lang);
+    IF_FREE(efreet_lang_country);
+    IF_FREE(efreet_lang_modifier);
+    efreet_parsed_locale = 0;  /* reset this in case they init efreet again */
+
+    return init;
+}
+
+/**
+ * @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("LC_ALL"))
+        return;
+
+    if (efreet_parse_locale_setting("LC_MESSAGES"))
+        return;
+
+    efreet_parse_locale_setting("LANG");
+}
+
+/**
+ * @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;
+
+    setting = getenv(env);
+    if (!setting) return 0;
+    setting = strdup(setting);
+
+    /* pull the modifier off the end */
+    p = strrchr(setting, '@');
+    if (p)
+    {
+        *p = '\0';
+        efreet_lang_modifier = strdup(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 = strdup(p + 1);
+        found = 1;
+    }
+
+    if (setting && (*setting != '\0'))
+    {
+        efreet_lang = strdup(setting);
+        found = 1;
+    }
+
+    FREE(setting);
+
+    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 += ecore_strlcpy(buffer + n, strs[i], size - n);
+    }
+    return n;
+}
+
+
diff --git a/legacy/efreet/src/lib/efreet_base.c b/legacy/efreet/src/lib/efreet_base.c
new file mode 100644 (file)
index 0000000..6789fc5
--- /dev/null
@@ -0,0 +1,212 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+
+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 Ecore_List *xdg_data_dirs = NULL;
+static Ecore_List *xdg_config_dirs = NULL;
+
+static const char *efreet_dir_get(const char *key, const char *fallback);
+static Ecore_List *efreet_dirs_get(const char *key, 
+                                        const char *fallback);
+
+/**
+ * @internal
+ * @return Returns 1 on success or 0 on failure
+ * @brief Initializes the efreet base settings
+ */
+int
+efreet_base_init(void)
+{
+    if (!ecore_string_init()) return 0;
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Cleans up the efree base settings system
+ */
+void
+efreet_base_shutdown(void)
+{
+    IF_RELEASE(efreet_home_dir);
+    IF_RELEASE(xdg_data_home);
+    IF_RELEASE(xdg_config_home);
+    IF_RELEASE(xdg_cache_home);
+
+    IF_FREE_LIST(xdg_data_dirs);
+    IF_FREE_LIST(xdg_config_dirs);
+
+    ecore_string_shutdown();
+}
+
+/**
+ * @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");
+    if (!efreet_home_dir || efreet_home_dir[0] == '\0')
+        efreet_home_dir = "/tmp";
+
+    efreet_home_dir = ecore_string_instance(efreet_home_dir);
+
+    return efreet_home_dir;
+}
+
+/**
+ * @return Returns the XDG Data Home directory
+ * @brief Retrieves the XDG Data Home directory
+ */
+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;
+}
+
+/**
+ * @return Returns the Ecore_List of preference ordered extra data directories
+ * @brief Returns the Ecore_List of prefernece oredred 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.
+ */
+Ecore_List *
+efreet_data_dirs_get(void)
+{
+    if (xdg_data_dirs) return xdg_data_dirs;
+    xdg_data_dirs = efreet_dirs_get("XDG_DATA_DIRS", 
+                            "/usr/local/share:/usr/share");
+    return xdg_data_dirs;
+}
+
+/**
+ * @return Returns the XDG Config Home directory
+ * @brief Retrieves the XDG Config Home directory
+ */
+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;
+}
+
+/**
+ * @return Returns the Ecore_List of preference ordered extra config directories
+ * @brief Returns the Ecore_List of prefernece oredred 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.
+ */
+Ecore_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;
+}
+
+/**
+ * @return Returns the XDG Cache Home directory
+ * @brief Retrieves the XDG Cache Home directory
+ */
+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;
+}
+
+/**
+ * @internal
+ * @param key: The environemnt key to lookup
+ * @param fallback: The fallback value to use
+ * @return Returns the directory related to the given key or the fallback
+ * @brief This trys 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 = malloc(sizeof(char) * len);
+        snprintf(dir, len, "%s%s", user, fallback);
+
+        t = ecore_string_instance(dir);
+        FREE(dir);
+    }
+    else t = ecore_string_instance(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 Ecore_List *
+efreet_dirs_get(const char *key, const char *fallback)
+{
+    Ecore_List *dirs;
+    const char *path;
+    char *tmp, *s, *p;
+
+    path = getenv(key);
+    if (!path || (path[0] == '\0')) path = fallback;
+
+    dirs = ecore_list_new();
+    ecore_list_set_free_cb(dirs, ECORE_FREE_CB(ecore_string_release));
+    if (!path) return dirs;
+
+    tmp = strdup(path);
+    s = tmp;
+    p = strchr(s, ':');
+    while (p)
+    {
+        *p = '\0';
+        if (!ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), s))
+            ecore_list_append(dirs, (void *)ecore_string_instance(s));
+
+        s = ++p;
+        p = strchr(s, ':');
+    }
+    if (!ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), s))
+      ecore_list_append(dirs, (void *)ecore_string_instance(s));
+    FREE(tmp);
+
+    return dirs;
+}
+
diff --git a/legacy/efreet/src/lib/efreet_base.h b/legacy/efreet/src/lib/efreet_base.h
new file mode 100644 (file)
index 0000000..ec8c567
--- /dev/null
@@ -0,0 +1,31 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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
+ *
+ * @{
+ */
+
+#include <Ecore.h>
+#include <Ecore_Data.h>
+
+const char *efreet_data_home_get(void);
+Ecore_List *efreet_data_dirs_get(void);
+
+const char *efreet_config_home_get(void);
+Ecore_List *efreet_config_dirs_get(void);
+
+const char *efreet_cache_home_get(void);
+
+/**
+ * @}
+ */
+
+#endif
+
diff --git a/legacy/efreet/src/lib/efreet_desktop.c b/legacy/efreet/src/lib/efreet_desktop.c
new file mode 100644 (file)
index 0000000..2a5d1a7
--- /dev/null
@@ -0,0 +1,1469 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+#include <Ecore_File.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#define DESKTOP_VERSION 1.0
+
+/**
+ * The current desktop environment (e.g. "Enlightenment" or "Gnome")
+ */
+static const char *desktop_environment = NULL;
+
+/**
+ * A cache of all loaded desktops, hashed by file name. 
+ * Values are Efreet_Desktop structures
+ */
+static Ecore_Hash *efreet_desktop_cache = NULL;
+
+/**
+ * A unique id for each tmp file created while building a command
+ */
+static int efreet_desktop_command_file_id = 0;
+
+static int init = 0;
+
+static Efreet_Desktop *efreet_desktop_new(const char *file);
+static Efreet_Desktop_Type efreet_desktop_type_parse(const char *type_str);
+static Ecore_List *efreet_desktop_string_list_parse(const char *string);
+static char *efreet_desktop_string_list_join(Ecore_List *list);
+static int 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 int 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 void efreet_desktop_x_fields_parse(Ecore_Hash_Node *node, 
+                                            Efreet_Desktop *desktop);
+static int efreet_desktop_environment_check(Efreet_Ini *ini);
+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);
+static void  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_icon(char *dest, int *size, int *len,
+                                                Efreet_Desktop *desktop);
+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_multiple(char *dest, int *size, int *len, 
+                                                    Efreet_Desktop_Command *command, 
+                                                    char type);
+
+static char *efreet_desktop_command_path_absolute(const char *path);
+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 void efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop, 
+                                            char *exec, int remaining);
+
+/**
+ * @internal
+ * @return Returns > 0 on success or 0 on failure
+ * @brief Initialize the Desktop parser subsystem
+ */
+int
+efreet_desktop_init(void)
+{
+    if (init++) return init;
+    if (!ecore_string_init()) return --init;
+    if (!ecore_file_init()) return --init;
+
+    efreet_desktop_cache = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(efreet_desktop_cache, ECORE_FREE_CB(free));
+    ecore_hash_set_free_value(efreet_desktop_cache, 
+                                ECORE_FREE_CB(efreet_desktop_free));
+
+    return init;
+}
+
+/**
+ * @internal
+ * @returns the number of initializations left for this system
+ * @brief Attempts to shut down the subsystem if nothing else is using it
+ */
+int
+efreet_desktop_shutdown(void)
+{
+    if (--init) return init;
+    ecore_file_shutdown();
+    ecore_string_shutdown();
+    if (desktop_environment) ecore_string_release(desktop_environment);
+    desktop_environment = NULL;
+
+    IF_FREE_HASH(efreet_desktop_cache);
+    return init;
+}
+
+/**
+ * @internal
+ * @param desktop: The desktop to check
+ * @return Returns 1 if the cache is still valid, 0 otherwise
+ * @brief This will check if the desktop cache is still valid. 
+ */
+static int
+efreet_desktop_cache_check(Efreet_Desktop *desktop)
+{
+    struct stat buf;
+
+    if (!desktop) return 0;
+
+    /* have we modified this file since we last read it in? */
+    if (stat(desktop->orig_path, &buf) || (buf.st_mtime > desktop->load_time))
+        return 0;
+
+    return 1;
+}
+
+/**
+ * @param file: The file to get the Efreet_Desktop from
+ * @return Returns a reference to a cached Efreet_Desktop on success, NULL 
+ * on failure. This reference should not be freed.
+ * @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.
+ */
+Efreet_Desktop *
+efreet_desktop_get(const char *file)
+{
+    Efreet_Desktop *desktop;
+
+    if (efreet_desktop_cache) 
+    {
+        desktop = ecore_hash_get(efreet_desktop_cache, file);
+        if (desktop)
+        {
+            if (efreet_desktop_cache_check(desktop))
+                return desktop;
+
+            ecore_hash_remove(efreet_desktop_cache, file);
+            efreet_desktop_free(desktop);
+        }
+    }
+
+    desktop = efreet_desktop_new(file);
+    if (!desktop) return NULL;
+    if (!desktop->type)
+    {
+        efreet_desktop_free(desktop);
+        return NULL;
+    }
+
+    ecore_hash_set(efreet_desktop_cache, strdup(file), desktop);
+    return 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
+ */
+Efreet_Desktop *
+efreet_desktop_empty_new(const char *file)
+{
+    Efreet_Desktop *desktop;
+
+    desktop = NEW(Efreet_Desktop, 1);
+    if (!desktop) return NULL;
+
+    desktop->orig_path = strdup(file);
+    desktop->load_time = ecore_time_get();
+    return desktop;
+}
+
+/**
+ * @internal
+ * @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
+ */
+static Efreet_Desktop *
+efreet_desktop_new(const char *file)
+{
+    Efreet_Desktop *desktop;
+    Efreet_Ini *ini;
+    int error = 0;
+    int ok;
+
+    desktop = NEW(Efreet_Desktop, 1);
+    if (!desktop) return NULL;
+
+    desktop->orig_path = strdup(file);
+    desktop->load_time = ecore_time_get();
+
+    ini = efreet_ini_new(file);
+    if (!ini->data) 
+    {
+        efreet_ini_free(ini);
+        return desktop;
+    }
+
+    ok = efreet_ini_section_set(ini, "Desktop Entry");
+    if (!ok) ok = efreet_ini_section_set(ini, "KDE Desktop Entry");
+    if (!ok) 
+    {
+        printf("efreet_desktop_new error: no Desktop Entry section\n");
+        error = 1;
+    }
+
+    if (!error)
+    {
+        desktop->type = efreet_desktop_type_parse(
+                                efreet_ini_string_get(ini, "Type"));
+        desktop->version = efreet_ini_double_get(ini, "Version");
+
+        switch (desktop->type)
+        {
+            case EFREET_DESKTOP_TYPE_APPLICATION:
+                if (!efreet_desktop_application_fields_parse(desktop, ini)) 
+                    error = 1;
+                break;
+            case EFREET_DESKTOP_TYPE_LINK:
+                if (!efreet_desktop_link_fields_parse(desktop, ini)) 
+                    error = 1;
+                break;
+            case EFREET_DESKTOP_TYPE_DIRECTORY:
+                /* there are no directory specific fields */
+                break;
+            case EFREET_DESKTOP_TYPE_UNKNOWN:
+            default:
+                error = 1;
+                break;
+        }
+    }
+
+    if (!error && !efreet_desktop_environment_check(ini)) error = 1;
+    if (!error && !efreet_desktop_generic_fields_parse(desktop, ini)) error = 1;
+    if (!error)
+        ecore_hash_for_each_node(ini->section,
+                ECORE_FOR_EACH(efreet_desktop_x_fields_parse), desktop);
+
+    efreet_ini_free(ini);
+
+    if (!error) 
+      return desktop;
+
+    efreet_desktop_free(desktop);
+    return NULL;
+}
+
+/**
+ * @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
+ */
+int
+efreet_desktop_save(Efreet_Desktop *desktop)
+{
+    Efreet_Ini *ini;
+    int ok = 1;
+
+    ini = efreet_ini_new(desktop->orig_path);
+    efreet_ini_section_add(ini, "Desktop Entry");
+    efreet_ini_section_set(ini, "Desktop Entry");
+
+    switch (desktop->type)
+    {
+        case EFREET_DESKTOP_TYPE_APPLICATION:
+            efreet_ini_string_set(ini, "Type", "Application");
+            efreet_desktop_application_fields_save(desktop, ini);
+            break;
+        case EFREET_DESKTOP_TYPE_LINK:
+            efreet_ini_string_set(ini, "Type", "Link");
+            efreet_desktop_link_fields_save(desktop, ini);
+            break;
+        case EFREET_DESKTOP_TYPE_DIRECTORY:
+            efreet_ini_string_set(ini, "Type", "Directory");
+            break;
+        case EFREET_DESKTOP_TYPE_UNKNOWN:
+        default:
+            ok = 0;
+            break;
+    }
+
+    if (ok)
+    {
+        char *val;
+
+        if (desktop->only_show_in)
+        {
+            val = efreet_desktop_string_list_join(desktop->only_show_in);
+            efreet_ini_string_set(ini, "OnlyShowIn", val);
+            FREE(val);
+        }
+        if (desktop->not_show_in)
+        {
+            val = efreet_desktop_string_list_join(desktop->not_show_in);
+            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_double_set(ini, "Version", DESKTOP_VERSION);
+
+        if (!efreet_ini_save(ini, desktop->orig_path)) ok = 0;
+        else
+        {
+            if (desktop != ecore_hash_get(efreet_desktop_cache, desktop->orig_path))
+                ecore_hash_set(efreet_desktop_cache, 
+                                strdup(desktop->orig_path), desktop);
+        }
+    }
+    efreet_ini_free(ini);
+    return ok;
+}
+
+/**
+ * @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
+ */
+int
+efreet_desktop_save_as(Efreet_Desktop *desktop, const char *file)
+{
+    if (desktop == ecore_hash_get(efreet_desktop_cache, desktop->orig_path))
+        ecore_hash_remove(efreet_desktop_cache, desktop->orig_path);
+    FREE(desktop->orig_path);
+    desktop->orig_path = strdup(file);
+    return efreet_desktop_save(desktop);
+}
+
+/**
+ * @internal
+ * @param desktop: The Efreet_Desktop to work with
+ * @return Returns no value
+ * @brief Frees the Efreet_Desktop structure and all of it's data
+ */
+void
+efreet_desktop_free(Efreet_Desktop *desktop)
+{
+    if (!desktop) return;
+
+    IF_FREE(desktop->orig_path);
+
+    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);
+    IF_FREE_LIST(desktop->not_show_in);
+    IF_FREE_LIST(desktop->categories);
+    IF_FREE_LIST(desktop->mime_types);
+
+    IF_FREE_HASH(desktop->x);
+
+    FREE(desktop);
+}
+
+/**
+ * @param desktop: The desktop file to work with
+ * @param files: The files to be substituted into the exec line
+ * @aparam 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.
+ */
+void
+efreet_desktop_exec(Efreet_Desktop *desktop, Ecore_List *files, void *data)
+{
+    efreet_desktop_command_get(desktop, files, efreet_desktop_exec_cb, data);
+}
+
+static void
+efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop __UNUSED__, 
+                                char *exec, int remaining __UNUSED__)
+{
+    ecore_exe_run(exec, data);
+    free(exec);
+}
+
+/**
+ * @param desktop: The desktop to work with
+ * @return Returns 1 if the desktop is set to NoDisplay, 0 otherwise
+ * @brief Retrives the NoDisplay parameter from the desktop file
+ */
+int
+efreet_desktop_no_display_get(Efreet_Desktop *desktop)
+{
+    if (!desktop) return 0;
+    return desktop->no_display;
+}
+
+/**
+ * @internal
+ * @param environment: the environment name
+ * @brief sets the global desktop environment name
+ */
+void
+efreet_desktop_environment_set(const char *environment)
+{
+    if (desktop_environment) ecore_string_release(desktop_environment);
+    if (environment) ecore_string_instance(environment);
+    else desktop_environment = NULL;
+}
+
+/**
+ * @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
+ */
+unsigned int
+efreet_desktop_category_count_get(Efreet_Desktop *desktop)
+{
+    if (!desktop || !desktop->categories) return 0;
+    return ecore_list_nodes(desktop->categories);
+}
+
+/**
+ * @param desktop: the desktop
+ * @param category: the category name
+ * @brief add a category to a desktop
+ */
+void
+efreet_desktop_category_add(Efreet_Desktop *desktop, const char *category)
+{
+    if (!desktop) return;
+
+    if (!desktop->categories)
+    {
+        desktop->categories = ecore_list_new();
+        ecore_list_set_free_cb(desktop->categories, 
+                                ECORE_FREE_CB(ecore_string_release));
+    }
+    else
+        if (ecore_list_find(desktop->categories, 
+                                ECORE_COMPARE_CB(strcmp), category)) return;
+
+    ecore_list_append(desktop->categories, 
+                        (void *)ecore_string_instance(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
+ */
+int
+efreet_desktop_category_del(Efreet_Desktop *desktop, const char *category)
+{
+    int found = 0;
+    if (!desktop || !desktop->categories) return 0;
+
+    if (ecore_list_find(desktop->categories, 
+                            ECORE_COMPARE_CB(strcmp), category)) 
+    {
+        found = 1;
+        ecore_list_remove(desktop->categories);
+    }
+
+    return found;
+}
+/**
+ * @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
+efreet_desktop_type_parse(const char *type_str)
+{
+    if (!type_str) return EFREET_DESKTOP_TYPE_UNKNOWN;
+
+    if (!strcmp("Application", type_str)) 
+        return EFREET_DESKTOP_TYPE_APPLICATION;
+    if (!strcmp("Link", type_str)) 
+        return EFREET_DESKTOP_TYPE_LINK;
+    if (!strcmp("Directory", type_str)) 
+        return EFREET_DESKTOP_TYPE_DIRECTORY;
+
+    return EFREET_DESKTOP_TYPE_UNKNOWN;
+}
+
+/**
+ * @internal
+ * @param string: the raw string list
+ * @return an Ecore_List of ecore string's
+ * @brief Parse ';' separate list of strings according to the desktop spec
+ */
+static Ecore_List *
+efreet_desktop_string_list_parse(const char *string)
+{
+    Ecore_List *list;
+    char *tmp;
+    char *s, *p;
+
+    if (!string) return NULL;
+
+    list = ecore_list_new();
+    if (!list) return NULL;
+
+    ecore_list_set_free_cb(list, ECORE_FREE_CB(ecore_string_release));
+
+    tmp = strdup(string);
+    s = tmp;
+
+    while ((p = strchr(s, ';')))
+    {
+        if (p > tmp && *(p-1) == '\\') continue;
+        *p = '\0';
+        ecore_list_append(list, (void *)ecore_string_instance(s));
+        s = p + 1;
+    }
+    /* If this is true, the .desktop file does not follow the standard */
+    if (*s)
+    {
+#if STRICT_SPEC
+        printf("[Efreet]: Found a string list without ';' "
+                "at the end: %s\n", string);
+#endif
+        ecore_list_append(list, (void *)ecore_string_instance(s));
+    }
+
+    free(tmp);
+
+    return list;
+}
+
+/**
+ * @internal
+ * @param list: Ecore_List with strings
+ * @return a raw string list
+ * @brief Create a ';' separate list of strings according to the desktop spec
+ */
+static char *
+efreet_desktop_string_list_join(Ecore_List *list)
+{
+    const char *tmp;
+    char *string;
+    size_t size, pos, len;
+
+    if (ecore_list_is_empty(list)) return strdup("");
+
+    size = 1024;
+    string = malloc(size);
+    pos = 0;
+
+    ecore_list_goto_first(list);
+    while ((tmp = ecore_list_next(list)))
+    {
+        len = strlen(tmp);
+        /* +1 for ';' */
+        if ((len + pos + 1) >= size)
+        {
+            size += 1024;
+            string = realloc(string, size);
+        }
+        strcpy(string + pos, tmp);
+        pos += len;
+        strcpy(string + pos, ";");
+        pos += 1;
+    }
+    return string;
+}
+
+/**
+ * @internal
+ * @param desktop: the Efreet_Desktop to store parsed fields in
+ * @param ini: the Efreet_Ini to parse fields from
+ * @return 1 if parsed succesfully, 0 otherwise
+ * @brief Parse application specific desktop fields
+ */
+static int
+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);
+
+    desktop->categories = efreet_desktop_string_list_parse(
+                                efreet_ini_string_get(ini, "Categories"));
+    desktop->mime_types = efreet_desktop_string_list_parse(
+                                efreet_ini_string_get(ini, "MimeType"));
+
+    desktop->terminal = efreet_ini_boolean_get(ini, "Terminal");
+    desktop->startup_notify = efreet_ini_boolean_get(ini, "StartupNotify");
+
+    return 1;
+}
+
+/**
+ * @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);
+        efreet_ini_string_set(ini, "Categories", val);
+        FREE(val);
+    }
+
+    if (desktop->mime_types)
+    {
+        val = efreet_desktop_string_list_join(desktop->mime_types);
+        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 1 if parsed succesfully, 0 otherwise
+ * @brief Parse link specific desktop fields
+ */
+static int
+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 1;
+}
+
+/**
+ * @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 succesfully, 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;
+
+    val = efreet_ini_localestring_get(ini, "Name");
+    if (val) desktop->name = strdup(val);
+    else 
+    {
+        printf("efreet_desktop_generic_fields_parse error: no Name\n");
+        return 0;
+    }
+
+    val = efreet_ini_localestring_get(ini, "GenericName");
+    if (val) desktop->generic_name = strdup(val);
+
+    val = efreet_ini_localestring_get(ini, "Comment");
+    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");
+
+    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);
+}
+
+/**
+ * @internal
+ * @param node: The node to work with
+ * @param desktop: The desktop file to work with
+ * @return Returns no value
+ * @brief Parses out an X- deys from @a node and stores in @a desktop
+ */
+static void
+efreet_desktop_x_fields_parse(Ecore_Hash_Node *node, Efreet_Desktop *desktop)
+{
+    if (strncmp(node->key, "X-", 2)) return;
+
+    if (!desktop->x)
+    {
+        desktop->x = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+        ecore_hash_set_free_key(desktop->x, 
+                            ECORE_FREE_CB(ecore_string_release));
+        ecore_hash_set_free_value(desktop->x, 
+                            ECORE_FREE_CB(ecore_string_release));
+    }
+    ecore_hash_set(desktop->x, (void *)ecore_string_instance(node->key),
+            (void *)ecore_string_instance(node->value));
+}
+
+
+/**
+ * @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_Ini *ini)
+{
+    Ecore_List *list;
+    const char *val;
+
+    list = efreet_desktop_string_list_parse(efreet_ini_string_get(ini, "OnlyShowIn"));
+    if (list)
+    {
+        int found = 0;
+
+        if (desktop_environment) 
+        {
+            ecore_list_goto_first(list);
+            while ((val = ecore_list_next(list)))
+            {
+                if (!strcmp(val, desktop_environment)) 
+                {
+                    found = 1;
+                    break;
+                }
+            }
+        }
+
+        ecore_list_destroy(list);
+        return found;
+    }
+
+    if (desktop_environment) 
+    {
+        int found = 0;
+
+        list = efreet_desktop_string_list_parse(efreet_ini_string_get(ini, "NotShowIn"));
+        if (list)
+        {
+            ecore_list_goto_first(list);
+            while ((val = ecore_list_next(list)))
+            {
+                if (!strcmp(val, desktop_environment)) 
+                {
+                    found = 1;
+                    break;
+                }
+            }
+            ecore_list_destroy(list);
+        }
+
+        return !found;
+    }
+
+    return 1;
+}
+
+
+/**
+ * @param desktop: the desktop entry
+ * @param files: an ecore 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 1 on success or 0 on failure
+ * @brief Get a command to use to execute a desktop entry.
+ */
+int
+efreet_desktop_command_get(Efreet_Desktop *desktop, Ecore_List *files, 
+                            Efreet_Desktop_Command_Cb func, void *data)
+{
+    return efreet_desktop_command_progress_get(desktop, files, func, NULL, data);
+}
+
+
+/**
+ * @param desktop: the desktop entry
+ * @param files: an ecore 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_progress: 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.
+ */
+int
+efreet_desktop_command_progress_get(Efreet_Desktop *desktop, Ecore_List *files, 
+                                    Efreet_Desktop_Command_Cb cb_command, 
+                                    Efreet_Desktop_Progress_Cb cb_progress,  
+                                    void *data)
+{
+    char *p;
+    Efreet_Desktop_Command *command;
+    char *file;
+
+    if (!desktop || !cb_command || !desktop->exec) return 0;
+
+    command = NEW(Efreet_Desktop_Command, 1);
+    if (!command) return 0;
+
+    command->cb_command = cb_command;
+    command->cb_progress = cb_progress;
+    command->data = data;
+    command->files = ecore_list_new();
+    command->desktop = desktop;
+
+    ecore_list_set_free_cb(command->files, 
+                            ECORE_FREE_CB(efreet_desktop_command_file_free));
+
+    /* first, determine which fields are present in the Exec string */
+    p = strchr(desktop->exec, '%');
+    while (p)
+    {
+        p++;
+        switch(*p)
+        {
+            case 'f':
+            case 'F':
+                command->flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
+                break;
+            case 'u':
+            case 'U':
+                command->flags |= EFREET_DESKTOP_EXEC_FLAG_URI;
+                break;
+            case 'd':
+            case 'D':
+                command->flags |= EFREET_DESKTOP_EXEC_FLAG_DIR;
+                break;
+            case 'n':
+            case 'N':
+                command->flags |= EFREET_DESKTOP_EXEC_FLAG_FILE;
+                break;
+            case '%':
+                p++;
+                break;
+            default:
+                break;
+        }
+
+        p = strchr(p, '%');
+    }
+
+    /* get the required info for each file passed in */
+    if (files)
+    {
+        ecore_list_goto_first(files);
+        while ((file = ecore_list_next(files)))
+        {
+            Efreet_Desktop_Command_File *dcf;
+
+            dcf = efreet_desktop_command_file_process(command, file);
+            if (!dcf) continue;
+            ecore_list_append(command->files, dcf);
+            command->num_pending += dcf->pending;
+        }
+    }
+
+    if (command->num_pending == 0) efreet_desktop_command_build(command);
+    
+    return 1;
+}
+
+
+/**
+ * @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 Nothing is returned
+ */
+static void
+efreet_desktop_command_build(Efreet_Desktop_Command *command)
+{
+    Efreet_Desktop_Command_File *file = NULL;
+    int first = 1;
+    int num = 0;
+    Ecore_List *execs;
+    char *exec;
+
+    execs = ecore_list_new();
+
+    ecore_list_goto_first(command->files);
+
+    /* 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 */
+    while ((file = ecore_list_next(command->files)) || first)
+    {
+        const char *p;
+        int len = 0;
+        int size = PATH_MAX;
+        int file_added = 0;
+
+        first = 0;
+
+        exec = malloc(size);
+        p = command->desktop->exec;
+        len = 0;
+
+        while (*p)
+        {
+            if (len >= size - 1) 
+            {
+                size += 1024;
+                exec = realloc(exec, size);
+            }
+
+            /* 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);
+                            file_added = 1;
+                        }
+                        break;
+                    case 'F': 
+                    case 'U': 
+                    case 'D': 
+                    case 'N': 
+                        if (file) 
+                        {
+                            exec = efreet_desktop_command_append_multiple(exec, &size, 
+                                                                    &len, command, *p);
+                            file_added = 1;
+                        }
+                        break;
+                    case 'i': 
+                        exec = efreet_desktop_command_append_icon(exec, &size, &len, 
+                                                                    command->desktop);
+                        break;
+                    case 'c': 
+                        exec = efreet_desktop_command_append_quoted(exec, &size, &len,
+                                                                command->desktop->name);
+                        break;
+                    case 'k': 
+                        exec = efreet_desktop_command_append_quoted(exec, &size, &len, 
+                                                            command->desktop->orig_path);
+                        break;
+                    case 'v': 
+                    case 'm': 
+                        printf("[Efreet]: Deprecated conversion char: '%c' in file '%s'\n", 
+                                                            *p, command->desktop->orig_path);
+                        break;
+                    case '%':
+                        exec[len++] = *p;
+                        break;
+                    default:
+#if STRICT_SPEC
+                        printf("[Efreet]: Unknown conversion character: '%c'\n", *p);
+#endif
+                        break;
+                }
+            }
+            else exec[len++] = *p;
+            p++;
+        }
+
+        exec[len++] = '\0';
+
+        ecore_list_append(execs, exec);
+        num++;
+
+        /* 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;
+    }
+
+    ecore_list_goto_first(execs);
+    while((exec = ecore_list_next(execs)))
+    {
+        command->cb_command(command->data, command->desktop, exec, --num);
+    }
+
+    efreet_desktop_command_free(command);
+    ecore_list_destroy(execs);
+}
+
+static void
+efreet_desktop_command_free(Efreet_Desktop_Command *command)
+{
+    if (!command) return;
+
+    IF_FREE_LIST(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, "'");
+
+    /* single quotes in src need to be escaped */
+    if (strchr(src, '\''))
+    {
+        char *p;
+        p = src;
+        while (*p)
+        {
+            if (*p == '\'')
+                dest = efreet_string_append_char(dest, size, len, '\\');
+                
+            dest = efreet_string_append_char(dest, size, len, *p);
+            p++;
+        }
+    }
+    else
+        dest = efreet_string_append(dest, size, len, src);
+
+    dest = efreet_string_append(dest, size, len, "'");
+
+    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;
+    int first = 1;
+
+    if (!command->files) return dest;
+
+    ecore_list_goto_first(command->files);
+    while ((file = ecore_list_next(command->files)))
+    {
+        if (first)
+            first = 0;
+        else
+            dest = efreet_string_append_char(dest, size, len, ' ');
+
+        dest = efreet_desktop_command_append_single(dest, size, len, 
+                                                    file, tolower(type));
+    }
+
+    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:
+            printf("Invalid type passed to efreet_desktop_command_append_single:"
+                                                                " '%c'\n", type);
+            return dest;
+    }
+
+    if (!str) return dest;
+
+    dest = efreet_desktop_command_append_quoted(dest, size, len, str);
+
+    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 ");
+    dest = efreet_desktop_command_append_quoted(dest, size, len, desktop->icon);
+
+    return dest;
+}
+
+
+/**
+ * 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 = ecore_strlcpy(dest + *len, src, *size - *len);
+
+    while (l > *size - *len) 
+    {
+        /* we successfully appended this much */
+        off += *size - *len - 1;
+        *len = *size - 1;
+        *size += 1024;
+        dest = realloc(dest, *size);
+        *(dest + *len) = '\0';
+
+        l = ecore_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)
+    {
+        *size += 1024;
+        dest = realloc(dest, *size);
+    }
+
+    dest[(*len)++] = c;
+
+    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;
+/*
+    printf("FLAGS: %d, %d, %d, %d\n", 
+        command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH ? 1 : 0,
+        command->flags & EFREET_DESKTOP_EXEC_FLAG_URI ? 1 : 0,
+        command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR ? 1 : 0,
+        command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE ? 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_get_file(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, PATH_MAX, "/tmp/%d-%d-%s", getpid(), 
+                            efreet_desktop_command_file_id++, base);
+            printf("nonlocal fullpath: %s\n", buf);
+            f->fullpath = strdup(buf);
+            f->pending = 1;
+
+            ecore_file_download(uri, f->fullpath, efreet_desktop_cb_download_complete, 
+                                            efreet_desktop_cb_download_progress, f);
+        }
+
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI) 
+            f->uri = strdup(uri);
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR) 
+            f->dir = strdup("/tmp");
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE) 
+            f->file = strdup(base);
+    }
+    else
+    {
+        char *abs = efreet_desktop_command_path_absolute(file);
+        /* process local uri/path */
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
+            f->fullpath = strdup(abs);
+
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
+        {
+            char buf[PATH_MAX];
+            snprintf(buf, sizeof(buf), "file://%s", abs);
+            f->uri = strdup(buf);
+        }
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR) 
+            f->dir = ecore_file_get_dir(abs);
+        if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE) 
+            f->file = strdup(ecore_file_get_file(file));
+
+        free(abs);
+    }
+#if 0
+    printf("  fullpath: %s\n", f->fullpath);
+    printf("  uri: %s\n", f->uri);
+    printf("  dir: %s\n", f->dir);
+    printf("  file: %s\n", f->file);
+#endif
+    return f;
+}
+
+/**
+ * @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];
+        tmp = strdup(uri + 7);
+        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);
+            }
+        }
+        free(tmp);
+    }
+
+    /* 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)
+        efreet_desktop_command_build(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] != '/')
+    {
+        buf = malloc(size);
+        if (!getcwd(buf, size)) return NULL;
+        len = strlen(buf);
+
+        if (buf[len-1] != '/') buf = efreet_string_append(buf, &size, &len, "/");
+        buf = efreet_string_append(buf, &size, &len, path);
+
+        return buf;
+    }
+
+    /* just dup an alreaady absolute buffer */
+    return strdup(path);
+}
diff --git a/legacy/efreet/src/lib/efreet_desktop.h b/legacy/efreet/src/lib/efreet_desktop.h
new file mode 100644 (file)
index 0000000..2f1a263
--- /dev/null
@@ -0,0 +1,120 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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 
+ *
+ * @{
+ */
+
+/**
+ * Possible types of .desktop files. Unknown files are ignored.
+ */
+enum Efreet_Desktop_Type
+{
+    EFREET_DESKTOP_TYPE_UNKNOWN,
+    EFREET_DESKTOP_TYPE_APPLICATION,
+    EFREET_DESKTOP_TYPE_LINK,
+    EFREET_DESKTOP_TYPE_DIRECTORY
+};
+
+/**
+ * Efreet_Desktop_Type
+ */
+typedef enum Efreet_Desktop_Type Efreet_Desktop_Type;
+
+/**
+ * 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);
+
+/**
+ * Efreet_Desktop
+ * @brief a parsed representation of a .desktop file
+ */
+struct Efreet_Desktop
+{
+    Efreet_Desktop_Type type; /**< type of desktop file */
+
+    double version;         /**< version of spec file conforms to */
+
+    char *orig_path;        /**< original path to .desktop file */
+    double load_time;       /**< when the .desktop was loaded from 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 */
+
+    Ecore_List *only_show_in;   /**< list of environments that should 
+                                                    display the icon */
+    Ecore_List *not_show_in;    /**< list of environments that shoudn't 
+                                                    display the icon */
+    Ecore_List *categories;     /**< Categories in which item should be shown */
+    Ecore_List *mime_types;     /**< The mime types supppored by this app */
+
+    unsigned char no_display:1;        /**< Don't display this application in menus */
+    unsigned char hidden:1;            /**< User delete the item */
+    unsigned char terminal:1;          /**< Does the program run in a terminal */
+    unsigned char startup_notify:1;    /**< The starup notify settings of the app */
+
+    Ecore_Hash *x; /**< Keep track of all user extensions, keys that begin with X- */
+};
+
+Efreet_Desktop   *efreet_desktop_get(const char *file);
+Efreet_Desktop   *efreet_desktop_empty_new(const char *file);
+void              efreet_desktop_free(Efreet_Desktop *desktop);
+
+int               efreet_desktop_save(Efreet_Desktop *desktop);
+int               efreet_desktop_save_as(Efreet_Desktop *desktop, 
+                                                const char *file);
+
+void              efreet_desktop_exec(Efreet_Desktop *desktop, 
+                                      Ecore_List *files, void *data);
+
+int               efreet_desktop_no_display_get(Efreet_Desktop *desktop);
+
+void              efreet_desktop_environment_set(const char *environment);
+int               efreet_desktop_command_progress_get(Efreet_Desktop *desktop,
+                                         Ecore_List *files,
+                                         Efreet_Desktop_Command_Cb cb_command,
+                                         Efreet_Desktop_Progress_Cb cb_prog,
+                                         void *data);
+int               efreet_desktop_command_get(Efreet_Desktop *desktop,
+                                         Ecore_List *files,
+                                         Efreet_Desktop_Command_Cb func,
+                                         void *data);
+
+unsigned int      efreet_desktop_category_count_get(Efreet_Desktop *desktop);
+void              efreet_desktop_category_add(Efreet_Desktop *desktop,
+                                              const char *category);
+int               efreet_desktop_category_del(Efreet_Desktop *desktop,
+                                              const char *category);
+
+/** 
+ * @}
+ */
+
+#endif
+
diff --git a/legacy/efreet/src/lib/efreet_icon.c b/legacy/efreet/src/lib/efreet_icon.c
new file mode 100644 (file)
index 0000000..4a7df5f
--- /dev/null
@@ -0,0 +1,1365 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+#include <sys/stat.h>
+#include <limits.h>
+
+#define NO_MATCH_KEY ((char *)0xdeadbeef)
+
+static char *efreet_icon_user_dir = NULL;
+static Ecore_Hash *efreet_icon_dirs_cached = NULL;
+static Ecore_Hash *efreet_icon_themes = NULL; 
+Ecore_List *efreet_icon_extensions = NULL;
+
+static Efreet_Icon *efreet_icon_find_helper(Efreet_Icon_Theme *theme, 
+                                                const char *cache_key,
+                                                const char *icon,
+                                                const char *size);
+static Efreet_Icon *efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, 
+                                                    const char *icon_name, 
+                                                    const char *size);
+static Efreet_Icon *efreet_icon_fallback_icon(const char *icon_name);
+static Efreet_Icon *efreet_icon_fallback_dir_scan(const char *dir, 
+                                                    const char *icon_name);
+
+static Efreet_Icon *efreet_icon_lookup_directory(
+                                            Efreet_Icon_Theme *theme,
+                                            Efreet_Icon_Theme_Directory *dir,
+                                            const char *icon_name, 
+                                            unsigned int size);
+static int efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
+                                                    unsigned int size);
+static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, 
+                                                  unsigned int size);
+
+static Efreet_Icon *efreet_icon_new(const char *path);
+static void efreet_icon_free(Efreet_Icon *icon);
+static void efreet_icon_point_free(Efreet_Icon_Point *point);
+static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
+
+static Efreet_Icon *efreet_icon_cache_check(Efreet_Icon_Theme *theme, 
+                                                    const char *name, 
+                                                    unsigned int size);
+static void *efreet_icon_cache_get(Efreet_Icon_Theme *theme, const char *key);
+static void efreet_icon_cache_set(Efreet_Icon_Theme *theme, 
+                                    const char *key, void *value);
+static void efreet_icon_directory_cache(Efreet_Icon_Theme *theme,
+                                    Efreet_Icon_Theme_Directory *dir, 
+                                    const char *path,
+                                    unsigned int size);
+
+static Efreet_Icon_Theme *efreet_icon_theme_new(void);
+static void efreet_icon_theme_free(Efreet_Icon_Theme *theme);
+static void efreet_icon_theme_dir_scan_all(const char *theme_name);
+static void efreet_icon_theme_dir_scan(const char *dir,
+                                        const char *theme_name);
+static void efreet_icon_theme_dir_validity_check(void);
+static void efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, 
+                                                const char *path);
+static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, 
+                                                const char *path);
+
+static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini, 
+                                                                const char *name);
+static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir);
+
+static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme);
+static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, 
+                                                        const char *dir);
+
+/**
+ * @internal
+ * @return Returns 1 on success or 0 on failure
+ * @brief Initializes the icon system
+ */
+int 
+efreet_icon_init(void)
+{
+    if (!efreet_icon_themes)
+    {
+        const char *default_exts[] = {".png", ".xpm", NULL};
+        int i;
+
+        if (!ecore_init()) return 0;
+
+        /* setup the default extension list */
+        efreet_icon_extensions = ecore_list_new();
+        ecore_list_set_free_cb(efreet_icon_extensions, free);
+
+        for (i = 0; default_exts[i] != NULL; i++)
+            ecore_list_append(efreet_icon_extensions, strdup(default_exts[i]));
+
+        efreet_icon_themes = ecore_hash_new(NULL, NULL);
+        ecore_hash_set_free_value(efreet_icon_themes, 
+                            ECORE_FREE_CB(efreet_icon_theme_free));
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Shuts down the icon system
+ */
+void
+efreet_icon_shutdown(void)
+{
+    IF_FREE(efreet_icon_user_dir);
+
+    IF_FREE_LIST(efreet_icon_extensions);
+    IF_FREE_HASH(efreet_icon_themes);
+    IF_FREE_HASH(efreet_icon_dirs_cached);
+
+    ecore_shutdown();
+}
+
+/**
+ * @return Returns the user icon directory
+ * @brief Returns the user icon directory
+ */
+const char *
+efreet_icon_dir_get(void)
+{
+    const char *user;
+    int len;
+
+    if (efreet_icon_user_dir) return efreet_icon_user_dir;
+
+    user = efreet_home_dir_get();
+    len = strlen(user) + strlen("/.icons") + 1;
+    efreet_icon_user_dir = malloc(sizeof(char) * len);
+    snprintf(efreet_icon_user_dir, len, "%s/.icons", user);
+
+    return efreet_icon_user_dir;
+}
+
+/**
+ * @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
+ */
+void
+efreet_icon_extension_add(const char *ext)
+{
+    ecore_list_append(efreet_icon_extensions, strdup(ext));
+}
+
+/**
+ * @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.
+ */
+Ecore_List *
+efreet_icon_theme_list_get(void)
+{
+    Ecore_List *list, *theme_list;
+    char *dir;
+
+    /* reset the theme hash */
+    ecore_hash_destroy(efreet_icon_themes);
+    efreet_icon_themes = ecore_hash_new(NULL, NULL);
+    ecore_hash_set_free_value(efreet_icon_themes, 
+                              ECORE_FREE_CB(efreet_icon_theme_free));
+
+    efreet_icon_theme_dir_scan_all(NULL);
+    efreet_icon_theme_dir_validity_check();
+
+    /* create the list for the user */
+    list = ecore_list_new();
+    theme_list = ecore_hash_keys(efreet_icon_themes);
+    ecore_list_goto_first(theme_list);
+    while ((dir = ecore_list_next(theme_list)))
+    {
+        Efreet_Icon_Theme *theme;
+
+        theme = ecore_hash_get(efreet_icon_themes, dir);
+        if (theme->hidden || theme->fake) continue;
+
+        ecore_list_append(list, theme);
+    }
+    ecore_list_destroy(theme_list);
+
+    return list;
+}
+
+/**
+ * @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
+ */
+Efreet_Icon_Theme *
+efreet_icon_theme_find(const char *theme_name)
+{
+    const char *key;
+    Efreet_Icon_Theme *theme;
+
+    key = ecore_string_instance(theme_name);
+    theme = ecore_hash_get(efreet_icon_themes, key);
+    if (!theme)
+    {
+        efreet_icon_theme_dir_scan_all(theme_name);
+        theme = ecore_hash_get(efreet_icon_themes, key);
+    }
+    ecore_string_release(key);
+
+    return theme;
+}
+
+/**
+ * @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.
+ */
+Efreet_Icon *
+efreet_icon_find(const char *theme_name, const char *icon, const char *size)
+{
+    const char *share_key;
+    char cache_key[PATH_MAX]; 
+    Efreet_Icon *value = NULL;
+    Efreet_Icon_Theme *theme;
+    const char *key_list[] = { icon, "@", size, NULL };
+
+    theme = efreet_icon_theme_find(theme_name);
+    if (!theme)
+    {
+        theme = efreet_icon_theme_new();
+        theme->fake = 1;
+        theme->name.internal = ecore_string_instance(theme_name);
+        ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, theme);
+    }
+
+    efreet_array_cat(cache_key, sizeof(cache_key), key_list);
+
+    share_key = ecore_string_instance(cache_key);
+    value = efreet_icon_find_helper(theme, share_key, icon, size);
+
+    /* we didn't find the icon in the theme or in the inherited directories
+     * then just look for a non theme icon */
+    if (!value) value = efreet_icon_fallback_icon(icon);
+
+    efreet_icon_cache_set(theme, share_key, value);
+
+    if (value == (void *)NO_MATCH_KEY)
+        value = NULL;
+
+    return value;
+}
+
+/**
+ * @param theme: 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.
+ */
+const char *
+efreet_icon_path_find(const char *theme, const char *icon, const char *size)
+{
+    Efreet_Icon *ico;
+
+    ico = efreet_icon_find(theme, icon, size);
+
+    return (ico ? ico->path : NULL);
+}
+
+/**
+ * @internal
+ * @param theme: The theme to search in
+ * @param cache_key: The cache key to use  (icon\@sizexsize ecore_string)
+ * @param icon: The icon to search for
+ * @param size: The size to search for
+ * @return Returns the icon matching the given information or NULL if no
+ * icon found
+ * @brief Scans the theme and any inheriting themes for the given icon
+ */
+static Efreet_Icon *
+efreet_icon_find_helper(Efreet_Icon_Theme *theme, const char *cache_key,
+                                                const char *icon, 
+                                                const char *size)
+{
+    Efreet_Icon *value;
+
+    efreet_icon_theme_cache_check(theme);
+
+    /* see if this is in the cache already */
+    value = efreet_icon_cache_get(theme, cache_key);
+    if (value) return value;
+
+    /* go no further if this theme is fake */
+    if (theme->fake || !theme->valid) return NULL;
+
+    value = efreet_icon_lookup_icon(theme, icon, size);    
+
+    /* we didin't find the image check the inherited themes */
+    if (!value)
+    {
+        char *parent;
+
+        if (theme->inherits)
+        {
+            ecore_list_goto_first(theme->inherits);
+            while ((parent = ecore_list_next(theme->inherits)))
+            {
+                Efreet_Icon_Theme *parent_theme;
+
+                parent_theme = efreet_icon_theme_find(parent);
+                if (!parent_theme) continue;
+
+                value = efreet_icon_find_helper(parent_theme, cache_key, 
+                                                            icon, 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_find_helper(parent_theme, cache_key, 
+                                                            icon, size);
+        }
+    }
+
+    return value;
+}
+
+/**
+ * @internal
+ * @param theme: The icon theme to look in
+ * @param icon_name: The icon name to look for
+ * @param size: The icon size to look for
+ * @return Returns the Efreet_Icon for the theme/icon/size combo or NULL if
+ * none found
+ * @brief Looks for the @a icon in the @a theme for the @a size given.
+ */
+static Efreet_Icon *
+efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name, 
+                                                    const char *size)
+{
+    Efreet_Icon *icon = NULL, *tmp = NULL;
+    Efreet_Icon_Theme_Directory *dir;
+    int minimal_size = INT_MAX;
+    unsigned int real_size;
+
+    if (!theme || (theme->paths.count == 0) || !icon_name || !size) 
+        return NULL;
+
+    real_size = atoi(size);
+
+    /* search for allowed size == requested size */
+    ecore_list_goto_first(theme->directories);
+    while ((dir = ecore_list_next(theme->directories)))
+    {
+        if (!efreet_icon_directory_size_match(dir, real_size)) continue;
+        icon = efreet_icon_lookup_directory(theme, dir, 
+                                            icon_name, real_size);
+        if (icon) return icon;
+    }
+
+    /* search for any icon that matches */
+    ecore_list_goto_first(theme->directories);
+    while ((dir = ecore_list_next(theme->directories)))
+    {
+        int distance;
+
+        distance = efreet_icon_directory_size_distance(dir, real_size);
+        if (distance >= minimal_size) continue;
+
+        tmp = efreet_icon_lookup_directory(theme, dir, 
+                                           icon_name, 
+                                           real_size);
+        if (tmp) 
+        {
+            icon = tmp;
+            minimal_size = distance;
+        }
+    }
+     
+    return icon;
+}
+
+
+/**
+ * @internal
+ * @param theme: The theme to use
+ * @param dir: The theme directory to look in
+ * @param icon_name: The icon name to look for
+ * @param size: The icon size to look for
+ * @return Returns the icon cloest matching the given information or NULL if
+ * none found
+ * @brief Tries to find the file closest matching the given icon
+ */
+static Efreet_Icon *
+efreet_icon_lookup_directory(Efreet_Icon_Theme *theme,
+                             Efreet_Icon_Theme_Directory *dir,
+                             const char *icon_name, unsigned int size)
+{
+    if (theme->paths.count == 1)
+        efreet_icon_directory_cache(theme, dir, theme->paths.path, size);
+
+    else
+    {
+        const char *path;
+
+        ecore_list_goto_first(theme->paths.path);
+        while ((path = ecore_list_next(theme->paths.path)))
+            efreet_icon_directory_cache(theme, dir, path, size);
+    }
+
+    return efreet_icon_cache_check(theme, icon_name, size);
+}
+
+/**
+ * @internal
+ * @param dir: The theme directory to work with
+ * @param size: The size to check
+ * @return Returns true if the size matches for the given directory, 0
+ * otherwise
+ * @brief Checks if the size matches for the given directory or not
+ */
+static int
+efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, 
+                                unsigned int size)
+{
+    if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
+        return (dir->size.normal == size);
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
+        return ((dir->size.min < size) && (size < dir->size.max));
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
+        return (((dir->size.normal - dir->size.threshold) < size)
+                && (size < (dir->size.normal + dir->size.threshold)));
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param dir: The directory to work with
+ * @param size: The size to check for
+ * @return Returns the distance this size is away from the desired size
+ * @brief Returns the distance the given size is away from the desired size
+ */
+static int
+efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
+                                    unsigned int size)
+{
+    if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
+        return (abs(dir->size.normal - size));
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
+    {
+        if (size < dir->size.min)
+            return dir->size.min - size;
+        if (dir->size.max < size)
+            return size - dir->size.max;
+
+        return 0;
+    }
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
+    {
+        if (size < (dir->size.normal - dir->size.threshold))
+            return (dir->size.min - size);
+        if ((dir->size.normal + dir->size.threshold) < size)
+            return (size - dir->size.max);
+
+        return 0;
+    }
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param icon_name: The icon name to look for
+ * @return Returns the Efreet_Icon for the given name or NULL if none found
+ * @brief Looks for the un-themed icon in the base directories
+ */
+static Efreet_Icon *
+efreet_icon_fallback_icon(const char *icon_name)
+{
+    Efreet_Icon *icon;
+
+    if (!icon_name) return NULL;
+
+    icon = efreet_icon_fallback_dir_scan(efreet_icon_dir_get(), icon_name);
+    if (!icon)
+    {
+        Ecore_List *xdg_dirs;
+        const char *dir;
+        char path[PATH_MAX];
+
+        xdg_dirs = efreet_data_dirs_get();
+        ecore_list_goto_first(xdg_dirs);
+        while ((dir = ecore_list_next(xdg_dirs)))
+        {
+            snprintf(path, PATH_MAX, "%s/icons", dir);
+            icon = efreet_icon_fallback_dir_scan(path, icon_name);
+            if (icon) return icon;
+        }
+
+        icon = efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name);
+    }
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param dir: The directory to scan
+ * @param icon_name: The icon to look for
+ * @return Returns the icon for the given name or NULL on failure
+ * @brief Scans the given @a dir for the given @a icon_name returning the
+ * Efreet_icon if found, NULL otherwise.
+ */
+static Efreet_Icon *
+efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name)
+{
+    Efreet_Icon *icon = NULL;
+    char path[PATH_MAX], *ext;
+
+    if (!dir || !icon_name) return NULL;
+
+    ecore_list_goto_first(efreet_icon_extensions);
+    while ((ext = ecore_list_next(efreet_icon_extensions)))
+    {
+        const char *icon_path[] = { dir, "/", icon_name, ext, NULL };
+        efreet_array_cat(path, sizeof(path), icon_path);
+
+        if (ecore_file_exists(path))
+        {
+            icon = efreet_icon_new(path);
+            if (icon) break;
+        }
+    }
+    /* This is to catch non-conforming .desktop files */
+    if (!icon)
+    {
+        const char *icon_path[] = { dir, "/", icon_name, NULL };
+        efreet_array_cat(path, sizeof(path), icon_path);
+
+        if (ecore_file_exists(path))
+        {
+            icon = efreet_icon_new(path);
+#if STRICT_SPEC
+            if (icon)
+                printf("[Efreet]: Found an icon that already has an extension: %s\n", path);
+#endif
+        }
+    }
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to work with
+ * @param dir: The theme directory to work with
+ * @param path: The partial path to use
+ * @param size: The size to look for
+ * @return Returns no value
+ * @brief Caches the icons in the given theme directory path at the given
+ * size
+ */
+static void
+efreet_icon_directory_cache(Efreet_Icon_Theme *theme,
+                Efreet_Icon_Theme_Directory *dir, const char *path,
+                unsigned int size)
+{
+    char file_path[PATH_MAX], size_str[10];
+    const char *dir_key, *path_strs[] = { path, "/", dir->name, NULL };
+    DIR *dirp;
+    struct dirent *file;
+
+    /* make sure the cache exists, create if needed */
+    if (!efreet_icon_dirs_cached)
+    {
+        efreet_icon_dirs_cached = ecore_hash_new(NULL, NULL);
+        ecore_hash_set_free_key(efreet_icon_dirs_cached, 
+                                ECORE_FREE_CB(ecore_string_release));
+    }
+
+    efreet_array_cat(file_path, sizeof(file_path), path_strs);
+
+    /* if we've already cached this directory don't do it again */
+    dir_key = ecore_string_instance(file_path);
+    if (ecore_hash_get(efreet_icon_dirs_cached, dir_key))
+        return;
+    ecore_hash_set(efreet_icon_dirs_cached, (void *)dir_key, (void *)1);
+
+    snprintf(size_str, sizeof(size_str), "%d", size);
+    dirp = opendir(file_path);
+    if (dirp)
+    {
+        while ((file = readdir(dirp)))
+        {
+            char key_str[PATH_MAX], *ext, *name;
+            const char *key, *name_strs[] = {NULL, "@", size_str, "x", size_str, NULL};
+            Efreet_Icon *value;
+
+            name = strdup(file->d_name);
+            name_strs[0] = name;
+
+            /* Drop the extension to cache icon name */
+            ext = strrchr(name, '.');
+            if (ext) 
+            {
+                /* we need to skip .icon files as their used for
+                 * informational purposes only */
+                if (!strcmp(ext, ".icon")) 
+                {
+                    FREE(name);
+                    continue;
+                }
+                *ext = '\0';
+            }
+
+            efreet_array_cat(key_str, sizeof(key_str), name_strs);
+            key = ecore_string_instance(key_str);
+            FREE(name);
+
+            /* Check for an existing cached icon */
+            value = ecore_hash_get(theme->icon_cache, key);
+            if (value == (void *)NO_MATCH_KEY) value = NULL;
+
+            if (!value)
+            {
+                const char *icon_path[] = { path, "/", dir->name, "/", file->d_name, NULL };
+
+                /* No icon present, build a full path and generate icon */
+                efreet_array_cat(file_path, sizeof(file_path), icon_path);
+                value = efreet_icon_new(file_path);
+            }
+
+            /* Add icon to cache */
+            if (value) 
+            {
+                value->ref_count ++;
+                ecore_hash_set(theme->icon_cache, (void *)key, value);
+            }
+        }
+        closedir(dirp);
+    }
+}
+
+/**
+ * @param theme: The theme to work with
+ * @param name: The icon name to look for
+ * @param size: The icon size to look for
+ * @return Returns the icon in the @a themes cache for @a name and @a size
+ * or NULL if none found
+ * @brief Looks up the @a name'd icon at @a size in the @a themes cache
+ */
+static Efreet_Icon *
+efreet_icon_cache_check(Efreet_Icon_Theme *theme, const char *name, unsigned int size)
+{
+    Efreet_Icon *icon = NULL;
+    char size_ext[PATH_MAX];
+
+    snprintf(size_ext, sizeof(size_ext), "%dx%d", size, size);
+    {
+        char icon_path[PATH_MAX];
+        const char *share_key[] = { name, "@", size_ext, NULL };
+        const char *cache_key;
+
+        efreet_array_cat(icon_path, sizeof(icon_path), share_key);
+        cache_key = ecore_string_instance(icon_path);
+        icon = efreet_icon_cache_get(theme, cache_key);
+    }
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to get the icon from
+ * @param key: The key to lookup in the cache. The key must be an
+ * ecore_string.
+ * @return Returns the icon for the given key or NULL if none found.
+ * @brief Retrives the icon for the given @a key or NULL if no icon for that key.
+ */
+static void *
+efreet_icon_cache_get(Efreet_Icon_Theme *theme, const char *key)
+{
+    return ecore_hash_get(theme->icon_cache, key);
+}
+
+/**
+ * @param theme: The theme to work with
+ * @param key: The key to cache, this key must be an ecore_string
+ * @param value: The value to hash for the given key
+ * @return Returns no value
+ * @brief Adds the given @a value into the @a themes hash for @a key
+ */
+static void
+efreet_icon_cache_set(Efreet_Icon_Theme *theme, const char *key, void *value)
+{
+    /* add back to the cache */
+    /* XXX this is a bit inefficient as I'll end up adding back to the cache
+     * even if I found the item in the cache. Not a big deal at the moment
+     * tho. */
+    if (!value) 
+        ecore_hash_set(theme->icon_cache, (void *)key, NO_MATCH_KEY);
+    else 
+    {
+        if (value != NO_MATCH_KEY) ((Efreet_Icon *)value)->ref_count ++;
+
+        ecore_hash_set(theme->icon_cache, (void *)key, value);
+    }
+}
+
+/**
+ * @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);
+    icon->path = strdup(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) icon->name = strdup(path);
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param icon: The Efreet_Icon to cleanup
+ * @return Returns no value.
+ * @brief Free's the given icon and all its internal data.
+ */
+static void
+efreet_icon_free(Efreet_Icon *icon)
+{
+    if (!icon || (icon == (void *)NO_MATCH_KEY)) return;
+
+    icon->ref_count --;
+    if (icon->ref_count > 0) return;
+
+    IF_FREE(icon->path);
+    IF_FREE(icon->name);
+    IF_FREE_LIST(icon->attach_points);
+
+    FREE(icon);
+}
+
+/**
+ * @internal
+ * @param point: The Efreet_Icon_Point to free
+ * @return Returns no value
+ * @brief Frees the given structure
+ */
+static void
+efreet_icon_point_free(Efreet_Icon_Point *point)
+{
+    if (!point) return;
+
+    FREE(point);
+}
+
+/**
+ * @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->data)
+    {
+        efreet_ini_free(ini);
+        return;
+    }
+
+    efreet_ini_section_set(ini, "Icon Data");
+    tmp = efreet_ini_localestring_get(ini, "DisplayName");
+    if (tmp) icon->name = strdup(tmp);
+
+    tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
+    if (tmp)
+    {
+        int points[4];
+        char *t, *s, *p;
+        int i;
+
+        t = strdup(tmp);
+        s = t;
+        for (i = 0; i < 4; i++)
+        {
+            p = strchr(s, ',');
+
+            if (!p) 
+            {
+                points[i] = 0;
+                continue;
+            }
+
+            *p = '\0';
+            points[i] = atoi(p);
+
+            s = ++p;
+        }
+
+        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];
+
+        FREE(t);
+    }
+
+    tmp = efreet_ini_string_get(ini, "AttachPoints");
+    if (tmp)
+    {
+        char *t, *s, *p;
+        int last = 0;
+
+        icon->attach_points = ecore_list_new();
+        ecore_list_set_free_cb(icon->attach_points, 
+                            ECORE_FREE_CB(efreet_icon_point_free));
+
+        t = strdup(tmp);
+        s = t;
+        p = t;
+        while (!last)
+        {
+            Efreet_Icon_Point *point;
+
+            p = strchr(s, ',');
+            if (!p) break;
+
+            point = NEW(Efreet_Icon_Point, 1);
+
+            *p = '\0';
+            point->x = atoi(s);
+
+            s = ++p;
+            p = strchr(s, '|');
+
+            if (!p) last = 1;
+            else *p = '\0';
+
+            point->y = atoi(s);
+            ecore_list_append(icon->attach_points, point);
+
+            if (!last) s = ++p;
+        }
+        FREE(t);
+    }
+
+    efreet_ini_free(ini);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Icon_Theme on success or NULL on failure
+ * @brief Creates a new Efreet_Icon_Theme structure
+ */
+static Efreet_Icon_Theme *
+efreet_icon_theme_new(void)
+{
+    Efreet_Icon_Theme *theme;
+
+    theme = NEW(Efreet_Icon_Theme, 1);
+
+    return theme;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to free
+ * @return Returns no value
+ * @brief Frees up the @a theme structure.
+ */
+static void
+efreet_icon_theme_free(Efreet_Icon_Theme *theme)
+{
+    if (!theme) return;
+
+    IF_RELEASE(theme->name.internal);
+    IF_RELEASE(theme->name.name);
+
+    IF_FREE(theme->comment);
+    IF_FREE(theme->example_icon);
+
+    if (theme->paths.count == 1)
+        IF_FREE(theme->paths.path)
+    else
+        IF_FREE_LIST(theme->paths.path)
+
+    IF_FREE_LIST(theme->inherits);
+    IF_FREE_LIST(theme->directories);
+
+    IF_FREE_HASH(theme->icon_cache);
+
+    FREE(theme);
+}
+
+/**
+ * @internal
+ * @param theme: The theme to work with
+ * @param path: The path to add
+ * @return Returns no value
+ * @brief This will correctly add the given path to the list of theme paths.
+ * @Note Assumes you've already verified that @a path is a valid directory.
+ */
+static void
+efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path)
+{
+    if (!theme || !path) return;
+
+    if (theme->paths.count == 0)
+        theme->paths.path = strdup(path);
+
+    else if (theme->paths.count > 1)
+        ecore_list_append(theme->paths.path, strdup(path));
+
+    else
+    {
+        char *old;
+
+        old = theme->paths.path;
+        theme->paths.path = ecore_list_new();
+        ecore_list_set_free_cb(theme->paths.path, free);
+
+        ecore_list_append(theme->paths.path, old);
+        ecore_list_append(theme->paths.path, strdup(path));
+    }
+    theme->paths.count ++;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief This validates that our cache is still valid.
+ *
+ * This is checked by the following algorithm:
+ *   - if we've check less then 5 seconds ago we're good
+ *   - if the mtime on the dir is less then our last check time we're good
+ *   - otherwise, invalidate the caches
+ */
+static void
+efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme)
+{
+    double new_check;
+
+    new_check = ecore_time_get();
+    /* we're within 5 seconds of the last time we checked the cache */
+    if ((new_check - 5) <= theme->last_cache_check) return;
+
+    if (theme->fake)
+        efreet_icon_theme_dir_scan_all(theme->name.internal);
+
+    else if (theme->paths.count == 1)
+        efreet_icon_theme_cache_check_dir(theme, theme->paths.path);
+
+    else if (theme->paths.count > 1)
+    {
+        char *path;
+
+        ecore_list_goto_first(theme->paths.path);
+        while ((path = ecore_list_next(theme->paths.path)))
+        {
+            if (!efreet_icon_theme_cache_check_dir(theme, path))
+                break;
+        }
+    }
+
+    /* When the cache is invalided it will delete the cache. Make sure we
+     * have a cache before we finish */
+    if (!theme->icon_cache)
+    {
+        theme->icon_cache = ecore_hash_new(NULL, NULL);
+        ecore_hash_set_free_key(theme->icon_cache, 
+                                    ECORE_FREE_CB(ecore_string_release));
+        ecore_hash_set_free_value(theme->icon_cache, 
+                                    ECORE_FREE_CB(efreet_icon_free));
+    }
+
+    theme->last_cache_check = new_check;
+}
+
+/**
+ * @internal
+ * @param theme: The icon theme to check
+ * @param dir: The directory to check
+ * @return Returns 1 if the cache is still valid, 0 otherwise
+ * @brief This will check if the theme cache is still valid. If it isn't the
+ * cache will be invalided and 0 returned.
+ */
+static int
+efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir)
+{
+    struct stat buf;
+
+    /* have we modified this directory since our last cache check? */
+    if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check))
+    {
+        if (efreet_icon_dirs_cached)
+            ecore_hash_remove(efreet_icon_dirs_cached, dir);
+        IF_FREE_HASH(theme->icon_cache);
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param theme_name: The theme to scan for
+ * @return Returns no value
+ * @brief Scans the theme directories. If @a theme_name is NULL it will load
+ * up all theme data. If @a theme_name is not NULL it will look for that
+ * specific theme data
+ */
+static void
+efreet_icon_theme_dir_scan_all(const char *theme_name)
+{
+    Ecore_List *xdg_dirs;
+    char path[PATH_MAX], *dir;
+
+    efreet_icon_theme_dir_scan(efreet_icon_dir_get(), theme_name);
+    efreet_icon_theme_dir_scan(efreet_data_home_get(), theme_name);
+
+    xdg_dirs = efreet_data_dirs_get();
+    ecore_list_goto_first(xdg_dirs);
+    while ((dir = ecore_list_next(xdg_dirs)))
+    {
+        snprintf(path, sizeof(path), "%s/icons", dir);
+        efreet_icon_theme_dir_scan(path, theme_name);
+    }
+
+    efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name);
+}
+
+/**
+ * @internal
+ * @param search_dir: The directory to scan
+ * @param theme_name: Scan for this specific theme, set to NULL to find all
+ * themes.
+ * @return Returns no value
+ * @brief Scans the given directory and adds non-hidden icon themes to the
+ * given list. If the theme isnt' in our cache then load the index.theme and
+ * add to the cache.
+ */
+static void
+efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name)
+{
+    DIR *dirs;
+    struct dirent *dir;
+
+    if (!search_dir) return;
+
+    dirs = opendir(search_dir);
+    if (!dirs) return;
+
+    while ((dir = readdir(dirs)))
+    {
+        Efreet_Icon_Theme *theme;
+        char path[PATH_MAX];
+        const char *key;
+
+        if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
+
+        /* only care if this is a directory or the theme name matches the
+         * given name */
+        snprintf(path, sizeof(path), "%s/%s", search_dir, dir->d_name);
+        if (((theme_name != NULL) && (strcmp(theme_name, dir->d_name)))
+                || !ecore_file_is_dir(path)) 
+            continue;
+
+        key = ecore_string_instance(dir->d_name);
+        theme = ecore_hash_get(efreet_icon_themes, key);
+
+        if (!theme)
+        {
+            theme = efreet_icon_theme_new();
+            theme->name.internal = key;
+            ecore_hash_set(efreet_icon_themes, 
+                        (void *)theme->name.internal, theme);
+        }
+        else
+        {
+            if (theme->fake)
+                theme->fake = 0;
+            ecore_string_release(key);
+        }
+
+        efreet_icon_theme_path_add(theme, path);
+
+        /* 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 */
+        strncat(path, "/index.theme", sizeof(path));
+        if (ecore_file_exists(path))
+            efreet_icon_theme_index_read(theme, path);
+    }
+    closedir(dirs);
+
+    /* if we were given a theme name we want to make sure that that given
+     * theme is valid before finishing, unless it's a fake theme */
+    if (theme_name)
+    {
+        Efreet_Icon_Theme *theme;
+    
+        theme = ecore_hash_get(efreet_icon_themes, theme_name);
+        if (theme && !theme->valid && !theme->fake)
+        {
+            ecore_hash_remove(efreet_icon_themes, theme_name);
+            efreet_icon_theme_free(theme);
+        }
+    }
+}
+
+/**
+ * @internal
+ * @param theme: The theme to set the values into
+ * @param path: The path to the index.theme file for this theme
+ * @return Returns no value
+ * @brief This will load up the theme with the data in the index.theme file
+ */
+static void
+efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path)
+{
+    Efreet_Ini *ini;
+    const char *tmp;
+
+    if (!theme || !path) return;
+
+    ini = efreet_ini_new(path);
+    if (!ini->data)
+    {
+        efreet_ini_free(ini);
+        return;
+    }
+
+    efreet_ini_section_set(ini, "Icon Theme");
+    tmp = efreet_ini_localestring_get(ini, "Name");
+    if (tmp) theme->name.name = ecore_string_instance(tmp);
+
+    tmp = efreet_ini_localestring_get(ini, "Comment");
+    if (tmp) theme->comment = strdup(tmp);
+
+    tmp = efreet_ini_string_get(ini, "Example"); 
+    if (tmp) theme->example_icon = strdup(tmp);
+
+    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;
+
+        theme->inherits = ecore_list_new();
+        ecore_list_set_free_cb(theme->inherits, free);
+
+        t = strdup(tmp);
+        s = t;
+        p = strchr(s, ',');
+
+        while (p)
+        {
+            *p = '\0';
+
+            ecore_list_append(theme->inherits, strdup(s));
+            s = ++p;
+            p = strchr(s, ',');
+        }
+        ecore_list_append(theme->inherits, strdup(s));
+
+        FREE(t);
+    }
+
+    /* 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;
+        int last = 0;
+
+        theme->directories = ecore_list_new();
+        ecore_list_set_free_cb(theme->directories, 
+                            ECORE_FREE_CB(efreet_icon_theme_directory_free));
+
+        t = strdup(tmp);
+        s = t;
+        p = s;
+
+        while (!last)
+        {
+            p = strchr(s, ',');
+
+            if (!p) last = 1;
+            else *p = '\0';
+
+            ecore_list_append(theme->directories, 
+                            efreet_icon_theme_directory_new(ini, s));
+
+            if (!last) s = ++p;
+        }
+
+        FREE(t);
+    }
+
+    efreet_ini_free(ini);
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Because the theme icon directories can be spread over multiple
+ * base directories we may need to create the icon theme strucutre before
+ * finding the index.theme file. It may also be that we never find an
+ * index.theme file as this isn't a valid theme. This function makes sure
+ * that everything we've got in our hash has a valid key to it.
+ */
+static void
+efreet_icon_theme_dir_validity_check(void)
+{
+    Ecore_List *keys;
+    const char *name;
+
+    keys = ecore_hash_keys(efreet_icon_themes);
+    ecore_list_goto_first(keys);
+    while ((name = ecore_list_next(keys)))
+    {
+        Efreet_Icon_Theme *theme;
+
+        theme = ecore_hash_get(efreet_icon_themes, name);
+        if (!theme->valid && !theme->fake)
+        {
+            ecore_hash_remove(efreet_icon_themes, name);
+            efreet_icon_theme_free(theme);
+        }
+    }
+    ecore_list_destroy(keys);
+}
+
+/**
+ * @internal
+ * @param ini: The ini file with information on this directory
+ * @param name: The name of the directory
+ * @return Returns a new Efreet_Icon_Theme_Directory based on the
+ * information in @a ini.
+ * @brief Creates and initialises an icon theme directory from the given ini
+ * information
+ */
+static Efreet_Icon_Theme_Directory *
+efreet_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);
+    dir->name = strdup(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;
+    }
+    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;
+
+        else if (!strcasecmp(tmp, "Threshold"))
+            dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
+    }
+
+    dir->size.normal = efreet_ini_int_get(ini, "Size");
+
+    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;
+
+    val = efreet_ini_int_get(ini, "Threshold");
+    if (val < 0) dir->size.threshold = 2;
+    else dir->size.threshold = val;
+
+    return dir;
+}
+
+/**
+ * @internal
+ * @param dir: The Efreet_Icon_Theme_Directory to free
+ * @return Returns no value
+ * @brief Frees the given directory @a dir
+ */
+static void
+efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir)
+{
+    if (!dir) return;
+
+    IF_FREE(dir->name);
+    FREE(dir);
+}
+
diff --git a/legacy/efreet/src/lib/efreet_icon.h b/legacy/efreet/src/lib/efreet_icon.h
new file mode 100644 (file)
index 0000000..9f47d64
--- /dev/null
@@ -0,0 +1,178 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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 
+ *
+ * @{
+ */
+
+/**
+ * The possible contexts for an icon directory
+ */
+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
+ */
+typedef enum Efreet_Icon_Theme_Context Efreet_Icon_Theme_Context;
+
+/**
+ * The possible size types for an icon directory 
+ */
+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
+ */
+typedef enum Efreet_Icon_Size_Type 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 */
+
+    char *comment;        /**< String describing the theme */
+    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. If count is 1 then path
+     * will be a char * pointing to the directory. If count > 1 then path
+     * will be an Ecore_List of char *'s pointing to the directories */
+    struct
+    {
+        void *path;       /**< The paths */
+        int count;        /**< The number of path's */
+    } paths;              /**< The paths to this theme */
+
+    Ecore_List *inherits;       /**< Icon themes we inherit from */
+    Ecore_List *directories;    /**< List of subdirectories for this theme */
+
+    double last_cache_check;    /**< Last time the cache was checked */
+    Ecore_Hash *icon_cache;     /**< Cache of the icon data */
+
+    unsigned char hidden:1;     /**< Should this theme be hidden from users */
+    unsigned char valid:1;      /**< Have we seen an index for this theme */
+    unsigned char fake:1;       /**< This isnt' a real theme but the user has 
+                                        tried to query from it. We create the 
+                                        fake one to give us the theme cache. */
+};
+
+/**
+ * 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
+{
+    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
+{
+    char *path;       /**< Full path to the icon */
+    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 */
+
+    Ecore_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; /**< Was the embedded
+                                                        rectangle set */
+};
+
+/**
+ * Efreet_Point
+ */
+typedef struct Efreet_Icon_Point Efreet_Icon_Point;
+
+/**
+ * Efreet_Point 
+ * @brief Stores an x, y point.
+ */
+struct Efreet_Icon_Point
+{
+    int x;          /**< x coord */
+    int y;          /**< y coord */
+};
+
+const char        *efreet_icon_dir_get(void);
+void               efreet_icon_extension_add(const char *ext);
+
+Ecore_List        *efreet_icon_theme_list_get(void);
+Efreet_Icon_Theme *efreet_icon_theme_find(const char *theme_name);
+Efreet_Icon       *efreet_icon_find(const char *theme_name, const char *icon, 
+                                                        const char *size);
+
+const char        *efreet_icon_path_find(const char *theme, const char *icon,
+                                                        const char *size);
+
+/** 
+ * @}
+ */
+
+#endif
+
diff --git a/legacy/efreet/src/lib/efreet_icon_new.c b/legacy/efreet/src/lib/efreet_icon_new.c
new file mode 100644 (file)
index 0000000..65f51b3
--- /dev/null
@@ -0,0 +1,1464 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+#include <sys/stat.h>
+#include <limits.h>
+
+#define NO_MATCH_KEY ((char *)0xdeadbeef)
+
+static char *efreet_icon_user_dir = NULL;
+static Ecore_Hash *efreet_icon_themes = NULL;  /**< Maps theme name to data */
+Ecore_List *efreet_icon_extensions = NULL; /**< The list of file extensions 
+                                                    we'll check */
+
+static Efreet_Icon *efreet_icon_new(const char *path);
+static void efreet_icon_free(Efreet_Icon *icon);
+static void efreet_icon_populate(Efreet_Icon *icon, const char *file);
+
+Efreet_Icon *efreet_icon_find_helper(Efreet_Icon_Theme *theme, 
+                                                const char *cache_key,
+                                                const char *icon,
+                                                const char *size);
+static Efreet_Icon *efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, 
+                                                    const char *icon_name, 
+                                                    const char *size);
+static Efreet_Icon *efreet_icon_fallback_icon(const char *icon_name);
+static Efreet_Icon *efreet_icon_fallback_dir_scan(const char *dir, 
+                                                    const char *icon_name);
+
+static Efreet_Icon *efreet_icon_lookup_directory_size_distance(
+                                            Efreet_Icon_Theme *theme,
+                                            Efreet_Icon_Theme_Directory *dir,
+                                            const char *icon_name, 
+                                            unsigned int size,
+                                            int *minimal_size);
+static Efreet_Icon *efreet_icon_lookup_directory_size_match(
+                                            Efreet_Icon_Theme *theme, 
+                                            Efreet_Icon_Theme_Directory *dir, 
+                                            const char *icon_name, 
+                                            unsigned int size);
+static int efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
+                                                    unsigned int size);
+static int efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, 
+                                                  unsigned int size);
+#if 0
+static Efreet_Icon *efreet_icon_lookup_directory_size_match_scan(const char *path,
+                                                Efreet_Icon_Theme_Directory *dir, 
+                                                const char *icon_name, 
+                                                unsigned int size);
+static Efreet_Icon *efreet_icon_lookup_directory_size_distance_scan(const char *path,
+                                                Efreet_Icon_Theme_Directory *dir, 
+                                                const char *icon_name, 
+                                                unsigned int size, 
+                                                unsigned int *minimal_size);
+#endif
+
+static Efreet_Icon_Theme *efreet_icon_theme_new(void);
+static void efreet_icon_theme_free(Efreet_Icon_Theme *theme);
+static Efreet_Icon_Theme *efreet_icon_theme_find(const char *theme_name);
+static void efreet_icon_theme_dir_scan_all(const char *theme_name);
+static void efreet_icon_theme_dir_scan(const char *dir,
+                                        const char *theme_name);
+static void efreet_icon_theme_dir_validity_check(void);
+static void efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, 
+                                                const char *path);
+static void efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, 
+                                                const char *path);
+
+static Efreet_Icon_Theme_Directory *efreet_icon_theme_directory_new(Efreet_Ini *ini, 
+                                                                const char *name);
+static void efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir);
+
+static void efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme);
+static int efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, 
+                                                        const char *dir);
+
+static void efreet_icon_point_free(Efreet_Icon_Point *point);
+
+/**
+ * @internal
+ * @return Returns 1 on success or 0 on failure
+ * @brief Initializes the icon system
+ */
+int 
+efreet_icon_init(void)
+{
+    if (!efreet_icon_themes)
+    {
+        const char *default_exts[] = {".png", ".xpm", NULL};
+        int i;
+
+        if (!ecore_init()) return 0;
+
+        /* setup the default extension list */
+        efreet_icon_extensions = ecore_list_new();
+        ecore_list_set_free_cb(efreet_icon_extensions, free);
+
+        for (i = 0; default_exts[i] != NULL; i++)
+            ecore_list_append(efreet_icon_extensions, strdup(default_exts[i]));
+
+        efreet_icon_themes = ecore_hash_new(NULL, NULL);
+        ecore_hash_set_free_key(efreet_icon_themes,
+                            ECORE_FREE_CB(ecore_string_release));
+        ecore_hash_set_free_value(efreet_icon_themes, 
+                            ECORE_FREE_CB(efreet_icon_theme_free));
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Shuts down the icon system
+ */
+void
+efreet_icon_shutdown(void)
+{
+    IF_FREE(efreet_icon_user_dir);
+
+    IF_FREE_LIST(efreet_icon_extensions);
+    IF_FREE_HASH(efreet_icon_themes);
+
+    ecore_shutdown();
+}
+
+/**
+ * @return Returns the user icon directory
+ * @brief Returns the user icon directory
+ */
+const char *
+efreet_icon_dir_get(void)
+{
+    const char *user;
+    int len;
+
+    if (efreet_icon_user_dir) return efreet_icon_user_dir;
+
+    user = efreet_home_dir_get();
+    len = strlen(user) + strlen("/.icons") + 1;
+    efreet_icon_user_dir = malloc(sizeof(char) * len);
+    snprintf(efreet_icon_user_dir, len, "%s/.icons", user);
+
+    return efreet_icon_user_dir;
+}
+
+/**
+ * @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
+ */
+void
+efreet_icon_extension_add(const char *ext)
+{
+    ecore_list_append(efreet_icon_extensions, strdup(ext));
+}
+
+/**
+ * @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.
+ */
+Ecore_List *
+efreet_icon_theme_list_get(void)
+{
+    Ecore_List *list, *theme_list;
+    char *dir;
+
+    /* reset the theme hash */
+    ecore_hash_destroy(efreet_icon_themes);
+    efreet_icon_themes = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_value(efreet_icon_themes, 
+                              ECORE_FREE_CB(efreet_icon_theme_free));
+
+    efreet_icon_theme_dir_scan_all(NULL);
+    efreet_icon_theme_dir_validity_check();
+
+    /* create the list for the user */
+    list = ecore_list_new();
+    theme_list = ecore_hash_keys(efreet_icon_themes);
+    ecore_list_goto_first(theme_list);
+    while ((dir = ecore_list_next(theme_list)))
+    {
+        Efreet_Icon_Theme *theme;
+
+        theme = ecore_hash_get(efreet_icon_themes, dir);
+        if (theme->hidden || theme->fake) continue;
+
+        ecore_list_append(list, theme);
+    }
+    ecore_list_destroy(theme_list);
+
+    return list;
+}
+
+static 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 += efreet_strlcpy(buffer + n, strs[i], size - n);
+    return n;
+}
+
+static void *
+efreet_icon_cache_get(Efreet_Icon_Theme *theme, const char *key)
+{
+    return ecore_hash_get(theme->icon_cache, key);
+}
+
+static void
+efreet_icon_cache_set(Efreet_Icon_Theme *theme, const char *key, void *value)
+{
+    /* add back to the cache */
+    /* XXX this is a bit inefficient as I'll end up adding back to the cache
+     * even if I found the item in the cache. Not a big deal at the moment
+     * tho. */
+    if (!value) 
+        ecore_hash_set(theme->icon_cache, (void *)key, NO_MATCH_KEY);
+    else 
+        ecore_hash_set(theme->icon_cache, (void *)key, value);
+}
+
+/**
+ * @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.
+ */
+Efreet_Icon *
+efreet_icon_find(const char *theme_name, const char *icon, const char *size)
+{
+    const char *share_key;
+    char cache_key[PATH_MAX]; 
+    Efreet_Icon *value = NULL;
+    Efreet_Icon_Theme *theme;
+    const char *key_list[] = { icon, "@", size, NULL };
+
+    theme = efreet_icon_theme_find(theme_name);
+    if (!theme)
+    {
+        theme = efreet_icon_theme_new();
+        theme->fake = 1;
+        theme->name.internal = ecore_string_instance(theme_name);
+        ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal, theme);
+    }
+
+    efreet_array_cat(cache_key, sizeof(cache_key), key_list);
+
+    share_key = ecore_string_instance(cache_key);
+    value = efreet_icon_find_helper(theme, share_key, icon, size);
+
+    /* we didn't find the icon in the theme or in the inherited directories
+     * then just look for a non theme icon */
+    if (!value) value = efreet_icon_fallback_icon(icon);
+
+    efreet_icon_cache_set(theme, share_key, value);
+
+    if (value == (void *)NO_MATCH_KEY)
+        value = NULL;
+
+    return value;
+}
+
+/**
+ * @param theme_name: The theme to search in
+ * @param cache_key: The cache key to use
+ * @param icon: The icon to search for
+ * @param size: The size to search for
+ * @return Returns the icon matching the given information or NULL if no
+ * icon found
+ * @brief Scans the theme and any inheriting themes for the given icon
+ */
+Efreet_Icon *
+efreet_icon_find_helper(Efreet_Icon_Theme *theme, const char *cache_key,
+                                                const char *icon, 
+                                                const char *size)
+{
+    Efreet_Icon *value;
+
+    efreet_icon_theme_cache_check(theme);
+
+    /* see if this is in the cache already */
+    value = efreet_icon_cache_get(theme, cache_key);
+    if (value) return value;
+
+    /* go no further if this theme is fake */
+    if (theme->fake || !theme->valid) return NULL;
+
+    value = efreet_icon_lookup_icon(theme, icon, size);    
+
+    /* we didin't find the image check the inherited themes */
+    if (!value)
+    {
+        char *parent;
+
+        if (theme->inherits)
+        {
+            ecore_list_goto_first(theme->inherits);
+            while ((parent = ecore_list_next(theme->inherits)))
+            {
+                Efreet_Icon_Theme *parent_theme;
+
+                parent_theme = efreet_icon_theme_find(parent);
+                if (!parent_theme) continue;
+
+                value = efreet_icon_find_helper(parent_theme, cache_key, 
+                                                            icon, 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_find_helper(parent_theme, cache_key, 
+                                                            icon, size);
+        }
+    }
+
+    return value;
+}
+
+/**
+ * @param theme: 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.
+ */
+const char *
+efreet_icon_path_find(const char *theme, const char *icon, const char *size)
+{
+    Efreet_Icon *ico;
+
+    ico = efreet_icon_find(theme, icon, size);
+
+    return (ico ? ico->path : NULL);
+}
+
+/**
+ * @internal
+ * @param theme: The icon theme to look in
+ * @param icon: The icon name to look for
+ * @param size: The icon size to look for
+ * @return Returns the Efreet_Icon for the theme/icon/size combo or NULL if
+ * none found
+ * @brief Looks for the @a icon in the @a theme for the @a size given.
+ */
+static Efreet_Icon *
+efreet_icon_lookup_icon(Efreet_Icon_Theme *theme, const char *icon_name, 
+                                                    const char *size)
+{
+    Efreet_Icon *icon = NULL, *tmp = NULL;
+    Efreet_Icon_Theme_Directory *dir;
+    int minimal_size = INT_MAX;
+    unsigned int real_size;
+
+    if (!theme || !icon_name || !size) return NULL;
+
+    real_size = atoi(size);
+
+    /* search for allowed size == requested size */
+    ecore_list_goto_first(theme->directories);
+    while ((dir = ecore_list_next(theme->directories)))
+    {
+        icon = efreet_icon_lookup_directory_size_match(theme, dir, 
+                                                icon_name, real_size);
+        if (icon) return icon;
+    }
+
+    /* search for any icon that matches */
+    ecore_list_goto_first(theme->directories);
+    while ((dir = ecore_list_next(theme->directories)))
+    {
+        tmp = efreet_icon_lookup_directory_size_distance(theme, dir, 
+                                                        icon_name, 
+                                                        real_size, 
+                                                        &minimal_size);
+        if (tmp) icon = tmp;
+    }
+     
+    return icon;
+}
+
+/**
+ */
+static void
+efreet_icon_directory_cache(Efreet_Icon_Theme *theme,
+                Efreet_Icon_Theme_Directory *dir, const char *path,
+                unsigned int size)
+{
+    char *icon_name;
+    char file_path[PATH_MAX];
+    Ecore_List *icon_list;
+    const char *path_strs[] = { path, "/", dir->name, NULL };
+
+    efreet_array_cat(file_path, sizeof(file_path), path_strs);
+
+    icon_list = ecore_file_ls(file_path);
+    if (icon_list)
+    {
+        while ((icon_name = ecore_list_remove_first(icon_list)))
+        {
+            char *ext;
+            const char *key;
+            Efreet_Icon *value;
+
+            /* Drop the extension to cache icon name */
+            ext = strrchr(icon_name, '.');
+            if (ext) *ext = '\0';
+
+            snprintf(file_path, sizeof(file_path), "%s@%dx%d", icon_name, size,
+                            size);
+
+            /* Cache is direct compare, so get a string instance */
+            key = ecore_string_instance(file_path);
+
+            /* Restore extension for full path */
+            if (ext) *ext = '.';
+
+            /* Check for an existing cached icon */
+            value = ecore_hash_get(theme->icon_cache, key);
+            if (value == (void *)NO_MATCH_KEY) value = NULL;
+            if (!value)
+            {
+                const char *icon_path[] = { path, "/", dir->name, "/", icon_name };
+                /* No icon present, build a full path and generate icon */
+                efreet_array_cat(file_path, sizeof(file_path), icon_path);
+                value = efreet_icon_new(file_path);
+            }
+
+            if (value)
+            {
+                const char *cur_ext, *check_ext;
+
+                /* Find the currently cached path extension */
+                cur_ext = strrchr(value->path, '.');
+                if (!cur_ext) cur_ext = "";
+
+                ecore_list_goto_first(efreet_icon_extensions);
+                while ((check_ext = ecore_list_next(efreet_icon_extensions)))
+                {
+                    /* Finished if the current extension matched first */
+                    if (!strcmp(cur_ext, check_ext))
+                        break;
+                    else if (!strcmp(ext, check_ext))
+                    {
+                        /* Higher priority extension found, replace old */
+                        efreet_icon_free(value);
+                        value = efreet_icon_new(file_path);
+                        break;
+                    }
+                }
+                value = NULL;
+                ecore_string_release(key);
+            }
+
+            /* Add icon to cache */
+            if (value)
+            {
+                printf("Cached %s: %s\n", key, file_path);
+                ecore_hash_set(theme->icon_cache, (void *)key, value);
+            }
+            FREE(icon_name);
+        }
+        IF_FREE_LIST(icon_list);
+    }
+}
+
+/**
+ * @internal
+ * @param theme: The theme to work with
+ * @param dir: The theme directory to work with
+ * @param icon_name: The icon name to search for
+ * @param size; The icon size to search for
+ * @return Returns the Efreet_Icon for the given information or NULL if none
+ * found
+ * @brief Returns the icon for the given information
+ */
+static Efreet_Icon *
+efreet_icon_lookup_directory_size_match(Efreet_Icon_Theme *theme, 
+                                    Efreet_Icon_Theme_Directory *dir, 
+                                    const char *icon_name, unsigned int size)
+{
+    const char *path;
+    Efreet_Icon *icon = NULL;
+
+    printf("path size %d: %s/%s\n", size, theme->name.internal, dir->name);
+
+    if (theme->paths.count == 0) return NULL;
+
+    if (theme->paths.count == 1)
+    {
+           path = theme->paths.path;
+       if (efreet_icon_directory_size_match(dir, size))
+       {
+           efreet_icon_directory_cache(theme, dir, path, size);
+       }
+    }
+    else
+    {
+        ecore_list_goto_first(theme->paths.path);
+        while ((path = ecore_list_next(theme->paths.path)))
+        {
+               if (efreet_icon_directory_size_match(dir, size))
+               {
+                       break;
+               }
+        }
+
+           /* Pre-load the cache for this directory at the size specified */
+           if (dir) efreet_icon_directory_cache(theme, dir, path, size);
+    }
+
+    if (path)
+    {
+           char size_ext[PATH_MAX];
+
+           if (snprintf(size_ext, sizeof(size_ext), "%dx%d", size, size))
+           {
+               char icon_path[PATH_MAX];
+               const char *share_key[] = { icon_name, "@", size_ext, NULL };
+
+               efreet_array_cat(icon_path, sizeof(icon_path), share_key);
+               icon = efreet_icon_cache_get(theme, icon_path);
+           }
+    }
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to use
+ * @param dir: The theme directory to look in
+ * @param icon_name: The icon name to look for
+ * @param size: The icon size to look for
+ * @param minimal_size: The current minimal size seen
+ * @return Returns the icon cloest matching the given information or NULL if
+ * none found
+ * @brief Tries to find the file closest matching the given icon
+ */
+static Efreet_Icon *
+efreet_icon_lookup_directory_size_distance(Efreet_Icon_Theme *theme,
+                                    Efreet_Icon_Theme_Directory *dir,
+                                    const char *icon_name, unsigned int size,
+                                    int *minimal_size)
+{
+    const char *path;
+    Efreet_Icon *icon = NULL;
+
+    if (theme->paths.count == 0) return NULL;
+
+    if (theme->paths.count == 1)
+    {
+        path = theme->paths.path;
+        if (efreet_icon_directory_size_distance(dir, size) >= *minimal_size)
+        {
+            efreet_icon_directory_cache(theme, dir, path, size);
+        }
+    }
+    else
+    {
+
+        ecore_list_goto_first(theme->paths.path);
+        while ((path = ecore_list_next(theme->paths.path)))
+        {
+            int distance;
+            distance = efreet_icon_directory_size_distance(dir, size);
+            if (distance >= *minimal_size)
+            {
+                *minimal_size = distance;
+            }
+        }
+
+        /* Pre-load the cache for this directory at the size specified */
+        if (dir) efreet_icon_directory_cache(theme, dir, path, size);
+    }
+
+    if (path)
+    {
+        char size_ext[PATH_MAX];
+
+        if (snprintf(size_ext, sizeof(size_ext), "%dx%d", size, size))
+        {
+            char icon_path[PATH_MAX];
+            const char *share_key[] = { icon_name, "@", size_ext, NULL };
+
+            efreet_array_cat(icon_path, sizeof(icon_path), share_key);
+            icon = efreet_icon_cache_get(theme, icon_path);
+        }
+    }
+
+    return icon;
+}
+
+#if 0
+/**
+ * @internal
+ * @param path: The path to search
+ * @param theme: The theme we're searching in
+ * @param dir: The theme directory we're searching in
+ * @param icon_name: The icon name we're search for
+ * @param size; The icon size we're searching for
+ * @return Returns the icon matching the given information or NULL if none
+ * found
+ * @brief Tries to match an icon to the given information
+ */
+static Efreet_Icon *
+efreet_icon_lookup_directory_size_match_scan(const char *path,
+                                                Efreet_Icon_Theme_Directory *dir, 
+                                                const char *icon_name, 
+                                                unsigned int size)
+{
+    Efreet_Icon *icon = NULL;
+    const char *ext;
+    char file_path[PATH_MAX];
+
+    /* size doesn't match, we're done */
+    if (!efreet_icon_directory_size_match(dir, size)) return NULL;
+
+    ecore_list_goto_first(efreet_icon_extensions);
+    while ((ext = ecore_list_next(efreet_icon_extensions)))
+    {
+        const char *icon_path[] = { path, "/", dir->name, "/", icon_name, ext, NULL };
+        efreet_array_cat(file_path, sizeof(file_path), icon_path);
+        if (ecore_file_exists(file_path))
+            return efreet_icon_new(file_path);
+    }
+    return icon;
+}
+
+/**
+ * @internal
+ * @param path: The path to search
+ * @param theme: The theme we're searching in
+ * @param dir: The theme directory we're searching in
+ * @param icon_name: The icon name we're search for
+ * @param size; The icon size we're searching for
+ * @param minimal_size: The minimal size we're working in
+ * @return Returns the icon matching the given information or NULL if none
+ * found
+ * @brief Tries to match an icon to the given information
+ */
+static Efreet_Icon *
+efreet_icon_lookup_directory_size_distance_scan(const char *path,
+                                                Efreet_Icon_Theme_Directory *dir,
+                                                const char *icon_name, 
+                                                unsigned int size, 
+                                                unsigned int *minimal_size)
+{
+    Efreet_Icon *icon = NULL;
+    const char *ext;
+    char file_path[PATH_MAX];
+    unsigned int distance;
+
+    /* only care if we're within the required distance */
+    distance = efreet_icon_directory_size_distance(dir, size);
+    if (distance >= *minimal_size) return NULL;
+
+    ecore_list_goto_first(efreet_icon_extensions);
+    while ((ext = ecore_list_next(efreet_icon_extensions)))
+    {
+        const char *icon_path[] = { path, "/", dir->name, "/", icon_name, ext, NULL };
+        efreet_array_cat(file_path, sizeof(file_path), icon_path);
+        if (ecore_file_exists(file_path))
+        {
+            *minimal_size = distance;
+            icon = efreet_icon_new(file_path);
+            break;
+        }
+    }
+
+    return icon;
+}
+#endif
+
+/**
+ * @internal
+ * @param theme: The theme to work with
+ * @param dir: The theme directory to work with
+ * @param size: The size to check
+ * @return Returns true if the size matches for the given directory, 0
+ * otherwise
+ * @brief Checks if the size matches for the given directory or not
+ */
+static int
+efreet_icon_directory_size_match(Efreet_Icon_Theme_Directory *dir, 
+                                unsigned int size)
+{
+    if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
+        return (dir->size.normal == size);
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
+        return ((dir->size.min < size) && (size < dir->size.max));
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
+        return (((dir->size.normal - dir->size.threshold) < size)
+                && (size < (dir->size.normal + dir->size.threshold)));
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to work with
+ * @param dir: The directory to work with
+ * @param size: The size to check for
+ * @return Returns the distance this size is away from the desired size
+ * @brief Returns the distance the given size is away from the desired size
+ */
+static int
+efreet_icon_directory_size_distance(Efreet_Icon_Theme_Directory *dir,
+                                    unsigned int size)
+{
+    if (dir->type == EFREET_ICON_SIZE_TYPE_FIXED)
+        return (abs(dir->size.normal - size));
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_SCALABLE)
+    {
+        if (size < dir->size.min)
+            return dir->size.min - size;
+        if (dir->size.max < size)
+            return size - dir->size.max;
+
+        return 0;
+    }
+
+    if (dir->type == EFREET_ICON_SIZE_TYPE_THRESHOLD)
+    {
+        if (size < (dir->size.normal - dir->size.threshold))
+            return (dir->size.min - size);
+        if ((dir->size.normal + dir->size.threshold) < size)
+            return (size - dir->size.max);
+
+        return 0;
+    }
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @param icon_name: The icon name to look for
+ * @return Returns the Efreet_Icon for the given name or NULL if none found
+ * @brief Looks for the un-themed icon in the base directories
+ */
+static Efreet_Icon *
+efreet_icon_fallback_icon(const char *icon_name)
+{
+    Efreet_Icon *icon;
+
+    if (!icon_name) return NULL;
+
+    icon = efreet_icon_fallback_dir_scan(efreet_icon_dir_get(), icon_name);
+    if (!icon)
+    {
+        Ecore_List *xdg_dirs;
+        const char *dir;
+
+        xdg_dirs = efreet_data_dirs_get();
+        ecore_list_goto_first(xdg_dirs);
+        while ((dir = ecore_list_next(xdg_dirs)))
+        {
+            icon = efreet_icon_fallback_dir_scan(dir, icon_name);
+            if (icon) return icon;
+        }
+
+        efreet_icon_fallback_dir_scan("/usr/share/pixmaps", icon_name);
+    }
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param dir: The directory to scan
+ * @param icon_name: The icon to look for
+ * @return Returns the icon for the given name or NULL on failure
+ * @brief Scans the given @a dir for the given @a icon_name returning the
+ * Efreet_icon if found, NULL otherwise.
+ */
+static Efreet_Icon *
+efreet_icon_fallback_dir_scan(const char *dir, const char *icon_name)
+{
+    char path[PATH_MAX], *ext;
+
+    if (!dir || !icon_name) return NULL;
+
+    ecore_list_goto_first(efreet_icon_extensions);
+    while ((ext = ecore_list_next(efreet_icon_extensions)))
+    {
+        const char *icon_path[] = { dir, "/", icon_name, ext, NULL };
+        efreet_array_cat(path, sizeof(path), icon_path);
+
+        if (ecore_file_exists(path))
+            return efreet_icon_new(path);
+    }
+
+    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);
+    icon->path = strdup(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) icon->name = strdup(path);
+
+    return icon;
+}
+
+/**
+ * @internal
+ * @param icon: The Efreet_Icon to cleanup
+ * @return Returns no value.
+ * @brief Free's the given icon and all its internal data.
+ */
+static void
+efreet_icon_free(Efreet_Icon *icon)
+{
+    if (!icon || (icon == (void *)NO_MATCH_KEY)) return;
+
+    IF_FREE(icon->path);
+    IF_FREE(icon->name);
+    IF_FREE_LIST(icon->attach_points);
+
+    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;
+
+    efreet_ini_section_set(ini, "Icon Data");
+    tmp = efreet_ini_localestring_get(ini, "DisplayName");
+    if (tmp) icon->name = strdup(tmp);
+
+    tmp = efreet_ini_string_get(ini, "EmbeddedTextRectangle");
+    if (tmp)
+    {
+        int points[4];
+        char *t, *s, *p;
+        int i;
+
+        t = strdup(tmp);
+        s = t;
+        for (i = 0; i < 4; i++)
+        {
+            p = strchr(s, ',');
+
+            if (!p) 
+            {
+                points[i] = 0;
+                continue;
+            }
+
+            *p = '\0';
+            points[i] = atoi(p);
+
+            s = ++p;
+        }
+
+        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];
+
+        FREE(t);
+    }
+
+    tmp = efreet_ini_string_get(ini, "AttachPoints");
+    if (tmp)
+    {
+        char *t, *s, *p;
+        int last = 0;
+
+        icon->attach_points = ecore_list_new();
+        ecore_list_set_free_cb(icon->attach_points, 
+                            ECORE_FREE_CB(efreet_icon_point_free));
+
+        t = strdup(tmp);
+        s = t;
+        p = t;
+        while (!last)
+        {
+            Efreet_Icon_Point *point;
+
+            p = strchr(s, ',');
+            if (!p) break;
+
+            point = NEW(Efreet_Icon_Point, 1);
+
+            *p = '\0';
+            point->x = atoi(s);
+
+            s = ++p;
+            p = strchr(s, '|');
+
+            if (!p) last = 1;
+            else *p = '\0';
+
+            point->y = atoi(s);
+            ecore_list_append(icon->attach_points, point);
+
+            if (!last) s = ++p;
+        }
+        FREE(t);
+    }
+
+    efreet_ini_free(ini);
+}
+
+/**
+ * @internal
+ * @return Returns a new Efreet_Icon_Theme on success or NULL on failure
+ * @brief Creates a new Efreet_Icon_Theme structure
+ */
+static Efreet_Icon_Theme *
+efreet_icon_theme_new(void)
+{
+    Efreet_Icon_Theme *theme;
+
+    theme = NEW(Efreet_Icon_Theme, 1);
+
+    return theme;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to free
+ * @return Returns no value
+ * @brief Frees up the @a theme structure.
+ */
+static void
+efreet_icon_theme_free(Efreet_Icon_Theme *theme)
+{
+    if (!theme) return;
+
+    IF_RELEASE(theme->name.internal);
+    IF_RELEASE(theme->name.name);
+
+    IF_FREE(theme->comment);
+    IF_FREE(theme->example_icon);
+
+    if (theme->paths.count == 1)
+        IF_FREE(theme->paths.path)
+    else
+        IF_FREE_LIST(theme->paths.path)
+
+    IF_FREE_LIST(theme->inherits);
+    IF_FREE_LIST(theme->directories);
+
+    IF_FREE_HASH(theme->icon_cache);
+
+    FREE(theme);
+}
+
+/**
+ * @internal
+ * @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
+ */
+static Efreet_Icon_Theme *
+efreet_icon_theme_find(const char *theme_name)
+{
+    const char *key;
+    Efreet_Icon_Theme *theme;
+
+    key = ecore_string_instance(theme_name);
+    theme = ecore_hash_get(efreet_icon_themes, key);
+    if (!theme)
+    {
+        efreet_icon_theme_dir_scan_all(theme_name);
+        theme = ecore_hash_get(efreet_icon_themes, key);
+    }
+    ecore_string_release(key);
+
+    return theme;
+}
+
+/**
+ * @internal
+ * @param theme: The theme to work with
+ * @param path: The path to add
+ * @return Returns no value
+ * @brief This will correctly add the given path to the list of theme paths.
+ * @Note Assumes you've already verified that @a path is a valid directory.
+ */
+static void
+efreet_icon_theme_path_add(Efreet_Icon_Theme *theme, const char *path)
+{
+    if (!theme || !path) return;
+
+    if (theme->paths.count == 0)
+        theme->paths.path = strdup(path);
+
+    else if (theme->paths.count > 1)
+        ecore_list_append(theme->paths.path, strdup(path));
+
+    else
+    {
+        char *old;
+
+        old = theme->paths.path;
+        theme->paths.path = ecore_list_new();
+        ecore_list_set_free_cb(theme->paths.path, free);
+
+        ecore_list_append(theme->paths.path, old);
+        ecore_list_append(theme->paths.path, strdup(path));
+    }
+    theme->paths.count ++;
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief This validates that our cache is still valid.
+ *
+ * This is checked by the following algorithm:
+ *   - if we've check less then 5 seconds ago we're good
+ *   - if the mtime on the dir is less then our last check time we're good
+ *   - otherwise, invalidate the caches
+ */
+static void
+efreet_icon_theme_cache_check(Efreet_Icon_Theme *theme)
+{
+    double new_check;
+
+    new_check = ecore_time_get();
+    /* we're within 5 seconds of the last time we checked the cache */
+    if ((new_check - 5) <= theme->last_cache_check) return;
+
+    if (theme->fake)
+        efreet_icon_theme_dir_scan_all(theme->name.internal);
+
+    else if (theme->paths.count == 1)
+        efreet_icon_theme_cache_check_dir(theme, theme->paths.path);
+
+    else if (theme->paths.count > 1)
+    {
+        char *path;
+
+        ecore_list_goto_first(theme->paths.path);
+        while ((path = ecore_list_next(theme->paths.path)))
+        {
+            if (!efreet_icon_theme_cache_check_dir(theme, path))
+                break;
+        }
+    }
+
+    /* When the cache is invalided it will delete the cache. Make sure we
+     * have a cache before we finish */
+    if (!theme->icon_cache)
+    {
+        theme->icon_cache = ecore_hash_new(NULL, NULL);
+        ecore_hash_set_free_key(theme->icon_cache, 
+                                    ECORE_FREE_CB(ecore_string_release));
+        ecore_hash_set_free_value(theme->icon_cache, 
+                                    ECORE_FREE_CB(efreet_icon_free));
+    }
+
+    theme->last_cache_check = new_check;
+}
+
+/**
+ * @internal
+ * @param theme: The icon theme to check
+ * @param dir: The directory to check
+ * @return Returns 1 if the cache is still valid, 0 otherwise
+ * @brief This will check if the theme cache is still valid. If it isn't the
+ * cache will be invalided and 0 returned.
+ */
+static int
+efreet_icon_theme_cache_check_dir(Efreet_Icon_Theme *theme, const char *dir)
+{
+    struct stat buf;
+
+    /* have we modified this directory since our last cache check? */
+    if (stat(dir, &buf) || (buf.st_mtime > theme->last_cache_check))
+    {
+        IF_FREE_HASH(theme->icon_cache);
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @param theme_name: The theme to scan for
+ * @return Returns no value
+ * @brief Scans the theme directories. If @a theme_name is NULL it will load
+ * up all theme data. If @a theme_name is not NULL it will look for that
+ * specific theme data
+ */
+static void
+efreet_icon_theme_dir_scan_all(const char *theme_name)
+{
+    Ecore_List *xdg_dirs;
+    char path[PATH_MAX], *dir;
+
+    efreet_icon_theme_dir_scan(efreet_icon_dir_get(), theme_name);
+    efreet_icon_theme_dir_scan(efreet_data_home_get(), theme_name);
+
+    xdg_dirs = efreet_data_dirs_get();
+    ecore_list_goto_first(xdg_dirs);
+    while ((dir = ecore_list_next(xdg_dirs)))
+    {
+        snprintf(path, sizeof(path), "%s/icons", (char *)dir);
+        efreet_icon_theme_dir_scan(path, theme_name);
+    }
+
+    efreet_icon_theme_dir_scan("/usr/share/pixmaps", theme_name);
+}
+
+/**
+ * @internal
+ * @param search_dir: The directory to scan
+ * @param theme_name: Scan for this specific theme, set to NULL to find all
+ * themes.
+ * @return Returns no value
+ * @brief Scans the given directory and adds non-hidden icon themes to the
+ * given list. If the theme isnt' in our cache then load the index.theme and
+ * add to the cache.
+ */
+static void
+efreet_icon_theme_dir_scan(const char *search_dir, const char *theme_name)
+{
+    Ecore_List *dirs;
+    char *dir;
+
+    if (!search_dir) return;
+
+    dirs = ecore_file_ls(search_dir);
+    if (!dirs) return;
+
+    while ((dir = ecore_list_remove_first(dirs)))
+    {
+        Efreet_Icon_Theme *theme;
+        char path[PATH_MAX];
+
+        /* only care if this is a directory or the theme name matches the
+         * given name */
+        snprintf(path, sizeof(path), "%s/%s", search_dir, dir);
+        if (((theme_name != NULL) && (strcmp(theme_name, dir)))
+                || !ecore_file_is_dir(path)) 
+        {
+            FREE(dir);
+            continue;
+        }
+
+        theme = ecore_hash_get(efreet_icon_themes, dir);
+        if (!theme)
+        {
+            theme = efreet_icon_theme_new();
+            theme->name.internal = ecore_string_instance(dir);
+            ecore_hash_set(efreet_icon_themes, (void *)theme->name.internal,
+                            theme);
+        }
+        else if (theme->fake)
+            theme->fake = 0;
+
+        FREE(dir);
+
+        efreet_icon_theme_path_add(theme, path);
+
+        /* 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 */
+        strncat(path, "/index.theme", sizeof(path));
+        if (ecore_file_exists(path))
+            efreet_icon_theme_index_read(theme, path);
+    }
+    ecore_list_destroy(dirs);
+
+    /* if we were given a theme name we want to make sure that that given
+     * theme is valid before finishing, unless it's a fake theme */
+    if (theme_name)
+    {
+        Efreet_Icon_Theme *theme;
+    
+        theme = ecore_hash_get(efreet_icon_themes, theme_name);
+        if (theme && !theme->valid && !theme->fake)
+        {
+            ecore_hash_remove(efreet_icon_themes, theme_name);
+            efreet_icon_theme_free(theme);
+        }
+    }
+}
+
+/**
+ * @internal
+ * @param theme: The theme to set the values into
+ * @param path: The path to the index.theme file for this theme
+ * @return Returns no value
+ * @brief This will load up the theme with the data in the index.theme file
+ */
+static void
+efreet_icon_theme_index_read(Efreet_Icon_Theme *theme, const char *path)
+{
+    Efreet_Ini *ini;
+    const char *tmp;
+
+    if (!theme || !path) return;
+
+    ini = efreet_ini_new(path);
+    if (!ini) return;
+
+    efreet_ini_section_set(ini, "Icon Theme");
+    tmp = efreet_ini_localestring_get(ini, "Name");
+    if (tmp) theme->name.name = ecore_string_instance(tmp);
+
+    tmp = efreet_ini_localestring_get(ini, "Comment");
+    if (tmp) theme->comment = strdup(tmp);
+
+    tmp = efreet_ini_string_get(ini, "Example"); 
+    if (tmp) theme->example_icon = strdup(tmp);
+
+    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;
+
+        theme->inherits = ecore_list_new();
+        ecore_list_set_free_cb(theme->inherits, free);
+
+        t = strdup(tmp);
+        s = t;
+        p = strchr(s, ',');
+
+        while (p)
+        {
+            *p = '\0';
+
+            ecore_list_append(theme->inherits, strdup(s));
+            s = ++p;
+            p = strchr(s, ',');
+        }
+        ecore_list_append(theme->inherits, strdup(s));
+
+        FREE(t);
+    }
+
+    /* 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;
+        int last = 0;
+
+        theme->directories = ecore_list_new();
+        ecore_list_set_free_cb(theme->directories, 
+                            ECORE_FREE_CB(efreet_icon_theme_directory_free));
+
+        t = strdup(tmp);
+        s = t;
+        p = s;
+
+        while (!last)
+        {
+            p = strchr(s, ',');
+
+            if (!p) last = 1;
+            else *p = '\0';
+
+            ecore_list_append(theme->directories, 
+                            efreet_icon_theme_directory_new(ini, s));
+
+            if (!last) s = ++p;
+        }
+
+        FREE(t);
+    }
+
+    efreet_ini_free(ini);
+}
+
+/**
+ * @internal
+ * @return Returns no value
+ * @brief Because the theme icon directories can be spread over multiple
+ * base directories we may need to create the icon theme strucutre before
+ * finding the index.theme file. It may also be that we never find an
+ * index.theme file as this isn't a valid theme. This function makes sure
+ * that everything we've got in our hash has a valid key to it.
+ */
+static void
+efreet_icon_theme_dir_validity_check(void)
+{
+    Ecore_List *keys;
+    const char *name;
+
+    keys = ecore_hash_keys(efreet_icon_themes);
+    ecore_list_goto_first(keys);
+    while ((name = ecore_list_next(keys)))
+    {
+        Efreet_Icon_Theme *theme;
+
+        theme = ecore_hash_get(efreet_icon_themes, name);
+        if (!theme->valid && !theme->fake)
+        {
+            ecore_hash_remove(efreet_icon_themes, name);
+            efreet_icon_theme_free(theme);
+        }
+    }
+    ecore_list_destroy(keys);
+}
+
+/**
+ * @internal
+ * @param ini: The ini file with information on this directory
+ * @param name: The name of the directory
+ * @return Returns a new Efreet_Icon_Theme_Directory based on the
+ * information in @a ini.
+ * @brief Creates and initialises an icon theme directory from the given ini
+ * information
+ */
+static Efreet_Icon_Theme_Directory *
+efreet_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);
+    dir->name = strdup(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;
+    }
+    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;
+
+        else if (!strcasecmp(tmp, "Threshold"))
+            dir->type = EFREET_ICON_SIZE_TYPE_THRESHOLD;
+    }
+
+    dir->size.normal = efreet_ini_int_get(ini, "Size");
+
+    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;
+
+    val = efreet_ini_int_get(ini, "Threshold");
+    if (val < 0) dir->size.threshold = 2;
+    else dir->size.threshold = val;
+
+    return dir;
+}
+
+/**
+ * @internal
+ * @param dir: The Efreet_Icon_Theme_Directory to free
+ * @return Returns no value
+ * @brief Frees the given directory @a dir
+ */
+static void
+efreet_icon_theme_directory_free(Efreet_Icon_Theme_Directory *dir)
+{
+    if (!dir) return;
+
+    IF_FREE(dir->name);
+    FREE(dir);
+}
+
+/**
+ * @internal
+ * @param point: The Efreet_Icon_Point to free
+ * @return Returns no value
+ * @brief Frees the given structure
+ */
+static void
+efreet_icon_point_free(Efreet_Icon_Point *point)
+{
+    if (!point) return;
+
+    FREE(point);
+}
+
+
diff --git a/legacy/efreet/src/lib/efreet_ini.c b/legacy/efreet/src/lib/efreet_ini.c
new file mode 100644 (file)
index 0000000..388d9ad
--- /dev/null
@@ -0,0 +1,651 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static Ecore_Hash *efreet_ini_parse(const char *file);
+static char *efreet_ini_unescape(const char *str);
+
+static void efreet_ini_section_save(Ecore_Hash_Node *node, FILE *f);
+static void efreet_ini_value_save(Ecore_Hash_Node *node, FILE *f);
+
+/**
+ * The number of times the Ini subsytem has been initialized
+ */
+static int init = 0;
+
+/**
+ * @internal
+ * @return Returns > 0 on success or 0 on failure
+ * @brief Initialize the Ini parser subsystem
+ */
+int
+efreet_ini_init(void)
+{
+    if (init++) return init;
+    if (!ecore_string_init()) return --init;
+    return init;
+}
+
+/**
+ * @internal
+ * @returns the number of initializations left for this system
+ * @brief Attempts to shut down the subsystem if nothing else is using it
+ */
+int
+efreet_ini_shutdown(void)
+{
+    if (--init) return init;
+    ecore_string_shutdown();
+    return init;
+}
+
+/**
+ * @internal
+ * @param file: The file to parse
+ * @return Returns a new Efreet_Ini structure initialized with the contents
+ * of @a file, or NULL on failure
+ * @brief Creates and initializes a new Ini structure with the contents of
+ * @a file, or NULL on failure 
+ */ 
+Efreet_Ini *
+efreet_ini_new(const char *file)
+{
+    Efreet_Ini *ini;
+
+    ini = NEW(Efreet_Ini, 1);
+    if (!ini) return NULL;
+
+    ini->data = efreet_ini_parse(file);
+
+    return ini;
+}
+
+/**
+ * @internal
+ * @param file The file to parse
+ * @return Returns an Ecore_Hash with the contents of @a file, or NULL on failure
+ * @brief Parses the ini file @a file into an Ecore_Hash
+ */
+static Ecore_Hash *
+efreet_ini_parse(const char *file)
+{
+    FILE *f;
+
+    /* a static buffer for quick reading of lines that fit */
+    char static_buf[4096];
+    int static_buf_len = 4096;
+
+    /* a big buffer to allocate for lines that are larger than the static one */
+    char *big_buf = NULL;
+    int big_buf_len = 0;
+    int big_buf_step = static_buf_len;
+
+    /* the current location to read into (with fgets) and the amount to read */
+    char *read_buf;
+    int read_len;
+
+    /* the current buffer to parse */
+    char *buf;
+   
+    Ecore_Hash *data, *section = NULL;
+
+    /* start with the static buffer */
+    buf = read_buf = static_buf;
+    read_len = static_buf_len;
+
+    f = fopen(file, "r");
+    if (!f) return NULL;
+
+    data = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(data, ECORE_FREE_CB(ecore_string_release));
+    ecore_hash_set_free_value(data, ECORE_FREE_CB(ecore_hash_destroy));
+
+    /* if a line is longer than the buffer size, this \n will get overwritten. */
+    read_buf[read_len - 2] = '\n';
+    while(fgets(read_buf, read_len, f) != NULL) 
+    {
+        char *key, *value, *p;
+        char *sep;
+
+        /* handle lines longer than the buffer size */
+        if (read_buf[read_len-2] != '\n')
+        {
+            int len;
+            len = strlen(buf);
+
+            if (!big_buf) 
+            {
+              /* create new big buffer and copy in contents of static buf */
+              big_buf_len = 2 * big_buf_step;
+              big_buf = malloc(big_buf_len * sizeof(char));
+              strncpy(big_buf, buf, len + 1);
+            }
+            else if (buf == big_buf)
+            {
+              /* already using the big buffer. increase its size for the next read */
+              big_buf_len += big_buf_step;
+              big_buf = realloc(big_buf, big_buf_len);
+            }
+            else
+            {
+              /* the big buffer exists, but we aren't using it yet. copy contents of static buf in */
+              strncpy(big_buf, buf, len);
+            }
+
+            /* use big_buffer for next fgets and subsequent parsing */
+            buf = big_buf;
+            read_buf = big_buf + len;
+            read_len = big_buf_len - len;
+            read_buf[read_len-2] = '\n';
+
+            continue;
+        }
+
+        /* skip empty lines and comments */
+        if (buf[0] == '\0' || buf[0] == '\n' || buf[0] == '#') goto next_line;
+
+        /* new section */
+        if (buf[0] == '[')
+        {
+            char *header, *p;
+            header = buf + 1;
+
+            p = strchr(header, ']');
+            if (p)
+            {
+                Ecore_Hash *old;
+                *p = '\0';
+                section = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+                ecore_hash_set_free_key(section, ECORE_FREE_CB(ecore_string_release));
+                ecore_hash_set_free_value(section, ECORE_FREE_CB(free));
+
+                old = ecore_hash_remove(data, header);
+                //if (old) printf("[efreet] Warning: duplicate section '%s' in file '%s'\n", header, file);
+                IF_FREE_HASH(old);
+                ecore_hash_set(data, (void *)ecore_string_instance(header), 
+                                                                section);
+            }
+            else
+            {
+                /* invalid file - skip line? or refuse to parse file? */
+                /* just printf for now till we figure out what to do */
+                printf("Invalid file (%s) (missing ] on group name)\n", file);
+            }
+            goto next_line;
+        }
+
+        /* parse key=value pair */
+        sep = strchr(buf, '=');
+        key = buf;
+
+        if (sep)
+        {
+            /* trim whitespace from end of key */
+            p = sep;
+            while (p > key && isspace(*(p - 1))) p--;
+            *p = '\0';
+
+            value = sep + 1;
+
+            /* trim whitespace from start of value */
+            while (*value && isspace(*value)) value++;
+
+            /* trim \n off of end of value */
+            p = value + strlen(value) - 1;
+            while (p > value && (*p == '\n' || *p == '\r')) p--;
+            *(p + 1) = '\0';
+
+            if (key && value && *key && *value) 
+            {
+                char *old;
+
+                old = ecore_hash_remove(section, key);
+                //if (old) printf("[efreet] Warning: duplicate key '%s' in file '%s'\n", key, file);
+                IF_FREE(old);
+
+                ecore_hash_set(section, (void *)ecore_string_instance(key), 
+                               efreet_ini_unescape(value));
+            }
+        }
+        else
+        {
+            /* check if line is all whitespace, if so, skip it */
+            int nonwhite = 0;
+            p = buf;
+            while (*p)
+            {
+                if (!isspace(*p))
+                {
+                    nonwhite = 1;
+                    break;
+                }
+                p++;
+            }
+            if (!nonwhite) goto next_line;
+
+            /* invalid file... */
+            printf("Invalid file (%s) (missing = from key=value pair)\n", file);
+        }
+
+next_line:
+        /* finished parsing a line. use static buffer for next line */
+        buf = read_buf = static_buf;
+        read_len = static_buf_len;
+        read_buf[read_len - 2] = '\n';
+    }
+
+    fclose(f);
+    if (big_buf) free(big_buf);
+
+    return data;
+}
+
+/**
+ * @internal
+ * @param ini: The Efreet_Ini to work with
+ * @return Returns no value
+ * @brief Frees the given Efree_Ini structure.
+ */
+void
+efreet_ini_free(Efreet_Ini *ini)
+{
+    if (!ini) return;
+
+    IF_FREE_HASH(ini->data);
+    FREE(ini);
+}
+
+/**
+ * @internal
+ * @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.
+ */
+int
+efreet_ini_save(Efreet_Ini *ini, const char *file)
+{
+    FILE *f;
+    if (!ini) return 0;
+
+    f = fopen(file, "w");
+    if (!f) return 0;
+    ecore_hash_for_each_node(ini->data, ECORE_FOR_EACH(efreet_ini_section_save), f);
+    fclose(f);
+
+    return 1;
+}
+
+/**
+ * @internal
+ * @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
+ */
+int
+efreet_ini_section_set(Efreet_Ini *ini, const char *section)
+{
+    if (!ini || !section) return 0;
+
+    ini->section = ecore_hash_get(ini->data, section);
+    return (ini->section ? 1 : 0);
+}
+
+/**
+ * @internal
+ * @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
+ */
+void
+efreet_ini_section_add(Efreet_Ini *ini, const char *section)
+{
+    Ecore_Hash *hash;
+
+    if (!ini || !section) return;
+
+    if (!ini->data)
+    {
+        ini->data = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+        ecore_hash_set_free_key(ini->data, ECORE_FREE_CB(ecore_string_release));
+        ecore_hash_set_free_value(ini->data, ECORE_FREE_CB(ecore_hash_destroy));
+    }
+    if (ecore_hash_get(ini->data, section)) return;
+
+    hash = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(hash, ECORE_FREE_CB(ecore_string_release));
+    ecore_hash_set_free_value(hash, ECORE_FREE_CB(free));
+    ecore_hash_set(ini->data, (void *)ecore_string_instance(section), hash);
+}
+
+/**
+ * @internal
+ * @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.
+ */
+const char *
+efreet_ini_string_get(Efreet_Ini *ini, const char *key)
+{
+    if (!ini || !key || !ini->section) return NULL;
+
+    return ecore_hash_get(ini->section, key);
+}
+
+/**
+ * @internal
+ * @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
+ */
+void
+efreet_ini_string_set(Efreet_Ini *ini, const char *key, const char *value)
+{
+    if (!ini || !key || !ini->section) return;
+
+    ecore_hash_set(ini->section, (void *)ecore_string_instance(key), strdup(value));
+}
+
+/**
+ * @internal
+ * @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.
+ */
+int
+efreet_ini_int_get(Efreet_Ini *ini, const char *key)
+{
+    const char *str;
+
+    if (!ini || !key || !ini->section) return -1;
+
+    str = efreet_ini_string_get(ini, key);
+    if (str) return atoi(str);
+
+    return -1;
+}
+
+/**
+ * @internal
+ * @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
+ */
+void
+efreet_ini_int_set(Efreet_Ini *ini, const char *key, int value)
+{
+    char str[12];
+
+    if (!ini || !key || !ini->section) return;
+
+    snprintf(str, 12, "%d", value);
+    efreet_ini_string_set(ini, key, str);
+}
+
+/**
+ * @internal
+ * @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.
+ */
+double
+efreet_ini_double_get(Efreet_Ini *ini, const char *key)
+{
+    const char *str;
+
+    if (!ini || !key || !ini->section) return -1;
+
+    str = efreet_ini_string_get(ini, key);
+    if (str) return atof(str);
+
+    return -1;
+}
+
+/**
+ * @internal
+ * @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
+ */
+void
+efreet_ini_double_set(Efreet_Ini *ini, const char *key, double value)
+{
+    char str[512];
+    size_t len;
+
+    if (!ini || !key || !ini->section) return;
+
+    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);
+}
+
+/**
+ * @internal
+ * @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
+ */
+unsigned int
+efreet_ini_boolean_get(Efreet_Ini *ini, const char *key)
+{
+    const char *str;
+
+    if (!ini || !key || !ini->section) return 0;
+
+    str = efreet_ini_string_get(ini, key);
+    if (str && !strcmp("true", str)) return 1;
+
+    return 0;
+}
+
+/**
+ * @internal
+ * @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
+ */
+void
+efreet_ini_boolean_set(Efreet_Ini *ini, const char *key, unsigned int value)
+{
+    if (!ini || !key || !ini->section) return;
+
+    if (value) efreet_ini_string_set(ini, key, "true");
+    else efreet_ini_string_set(ini, key, "false");
+}
+
+/**
+ * @internal
+ * @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
+ */
+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;
+
+    if (!ini || !key || !ini->section) return 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 = malloc(maxlen * sizeof(char));
+
+    if (lang && modifier && country)
+    {
+        snprintf(buf, maxlen, "%s[%s_%s@%s]", key, lang, country, modifier);
+        val = efreet_ini_string_get(ini, buf);
+        if (val) found = 1;
+    }
+
+    if (!found && lang && country)
+    {
+        snprintf(buf, maxlen, "%s[%s_%s]", key, lang, country);
+        val = efreet_ini_string_get(ini, buf);
+        if (val) found = 1;
+    }
+
+    if (!found && lang && modifier)
+    {
+        snprintf(buf, maxlen, "%s[%s@%s]", key, lang, modifier);
+        val = efreet_ini_string_get(ini, buf);
+        if (val) found = 1;
+    }
+
+    if (!found && lang)
+    {
+        snprintf(buf, maxlen, "%s[%s]", key, lang);
+        val = efreet_ini_string_get(ini, buf);
+        if (val) found = 1;
+
+    }
+
+    if (!found)
+        val = efreet_ini_string_get(ini, key);
+
+    FREE(buf);
+
+    return val;
+}
+
+/**
+ * @internal
+ * @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
+ */
+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 */
+
+    if (!ini || !key || !ini->section) return;
+
+    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 = malloc(maxlen * sizeof(char));
+
+    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);
+    FREE(buf);
+}
+
+/**
+ * @param str The string to unescape
+ * @return An allocated unescaped string
+ * @brief Unescapes backslash escapes in a string
+ */
+static char *
+efreet_ini_unescape(const char *str)
+{
+    char *buf, *dest;
+    const char *p;
+
+    if (!str) return NULL;
+    if (!strchr(str, '\\')) return strdup(str);
+    buf = malloc(strlen(str) + 1);
+
+    p = str;
+    dest = buf;
+    while(*p)
+    {
+        if (*p == '\\')
+        {
+            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 buf;
+}
+
+static void
+efreet_ini_section_save(Ecore_Hash_Node *node, FILE *f)
+{
+    fprintf(f, "[%s]\n", (char *)node->key);
+    ecore_hash_for_each_node(node->value, ECORE_FOR_EACH(efreet_ini_value_save), f);
+}
+
+static void
+efreet_ini_value_save(Ecore_Hash_Node *node, FILE *f)
+{
+    fprintf(f, "%s=%s\n", (char *)node->key, (char *)node->value);
+}
diff --git a/legacy/efreet/src/lib/efreet_ini.h b/legacy/efreet/src/lib/efreet_ini.h
new file mode 100644 (file)
index 0000000..25e5997
--- /dev/null
@@ -0,0 +1,60 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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
+{
+  Ecore_Hash *data;     /**< Hash of string => (Hash of string => string) */
+  Ecore_Hash *section;  /**< currently selected section */
+};
+
+Efreet_Ini  *efreet_ini_new(const char *file);
+void         efreet_ini_free(Efreet_Ini *ini);
+int          efreet_ini_save(Efreet_Ini *ini, const char *path);
+
+int          efreet_ini_section_set(Efreet_Ini *ini, const char *section);
+void         efreet_ini_section_add(Efreet_Ini *ini, const char *section);
+
+const char  *efreet_ini_string_get(Efreet_Ini *ini, const char *key);
+void         efreet_ini_string_set(Efreet_Ini *ini, const char *key, 
+                                                    const char *value);
+
+const char  *efreet_ini_localestring_get(Efreet_Ini *ini, const char *key);
+void         efreet_ini_localestring_set(Efreet_Ini *ini, const char *key, 
+                                                    const char *value);
+
+unsigned int efreet_ini_boolean_get(Efreet_Ini *ini, const char *key);
+void         efreet_ini_boolean_set(Efreet_Ini *ini, const char *key, 
+                                                    unsigned int value);
+
+int          efreet_ini_int_get(Efreet_Ini *ini, const char *key);
+void         efreet_ini_int_set(Efreet_Ini *ini, const char *key, int value);
+
+double       efreet_ini_double_get(Efreet_Ini *ini, const char *key);
+void         efreet_ini_double_set(Efreet_Ini *ini, const char *key, 
+                                                    double value);
+
+/**
+ * @}
+ */
+
+#endif
+
diff --git a/legacy/efreet/src/lib/efreet_menu.c b/legacy/efreet/src/lib/efreet_menu.c
new file mode 100644 (file)
index 0000000..c42c0d7
--- /dev/null
@@ -0,0 +1,3946 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+
+/**
+ * Efreet_Menu_Move
+ */
+typedef struct Efreet_Menu_Move Efreet_Menu_Move;
+
+/**
+ * Efreet_Menu_Move
+ * Info on a menu movement
+ */
+struct Efreet_Menu_Move
+{
+    char *old_name;     /**< The menu path to move from */
+    char *new_name;     /**< The menu path to move too */
+};
+
+/**
+ * Efreet_Menu_Internal
+ */
+typedef struct Efreet_Menu_Internal Efreet_Menu_Internal;
+
+/**
+ * Efreet_Menu_Internal
+ * Contains the information about a menu
+ */
+struct Efreet_Menu_Internal
+{
+    struct
+    {
+        char *path;         /**< The base file path */
+        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 */
+    Ecore_DList *directories;  /**< All the directories set in the menu file */
+
+    Efreet_Menu_Move *current_move; /**< The current move */
+
+    Ecore_List *app_dirs;        /**< .desktop application directories */
+
+    Ecore_List *app_pool;           /**< application pool */
+    Ecore_List *applications;       /**< applications in this menu */
+
+    Ecore_DList *directory_dirs;    /**< .directory file directories */
+    Ecore_Hash *directory_cache;    /**< .directory dirs */
+
+    Ecore_List *moves;      /**< List of moves to be handled by the menu */
+    Ecore_List *filters;    /**< Include and Exclude filters */
+
+    Efreet_Menu_Internal *parent;     /**< Our parent menu */
+    Ecore_List           *sub_menus;  /**< Our sub menus */
+
+    Ecore_List *layout;            /**< This menus layout */
+    Ecore_List *default_layout;    /**< Default layout */
+    char show_empty;    /**< Whether to show empty menus */
+    char in_line;       /**< Whether this meny can be inlined */
+    char inline_limit;  /**< Number of elements which triggers inline */
+    char inline_header; /**< Whether we should use the header name when this menu is inlined */
+    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 */
+};
+
+/**
+ * Efreet_Menu_App_Dir
+ */
+typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir;
+
+/**
+ * Holds information on an app dir
+ */
+struct Efreet_Menu_App_Dir
+{
+    char *path;                 /**< directory path */
+    char *prefix;               /**< If it's legacy it can have a prefix */
+    unsigned int legacy:1;      /**< is this a legacy dir */
+};
+
+/**
+ * The type of operations we can perform with a filter
+ */
+enum Efreet_Menu_Filter_Op_Type
+{
+    EFREET_MENU_FILTER_OP_OR,
+    EFREET_MENU_FILTER_OP_AND,
+    EFREET_MENU_FILTER_OP_NOT
+};
+
+/**
+ * Efreet_Menu_Filter_Op_Type
+ */
+typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type;
+
+/**
+ * The type of filter
+ */
+enum Efreet_Menu_Filter_Type
+{
+    EFREET_MENU_FILTER_INCLUDE,
+    EFREET_MENU_FILTER_EXCLUDE
+};
+
+/**
+ * Efreet_Menu_Filter_Type
+ */
+typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type;
+
+/**
+ * Efreet_Menu_Filter_Op 
+ */
+typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op;
+
+/**
+ * Efreet_Menu_Filter_Op
+ * Contains information on a filter operation
+ */
+struct Efreet_Menu_Filter_Op
+{
+    Efreet_Menu_Filter_Op_Type type; /**< The type of operation */
+    Ecore_List *categories;          /**< The categories this op applies too */
+    Ecore_List *filenames;           /**< The filenames this op applies too */
+
+    Ecore_List *filters;             /**< Child filters */
+
+    unsigned char all:1;             /**< Applies to all .desktop files */
+};
+
+/**
+ * Efreet_Menu_Filter
+ */
+typedef struct Efreet_Menu_Filter Efreet_Menu_Filter;
+
+/**
+ * Efreet_Menu_Filter
+ * Stores information on a filter 
+ */
+struct Efreet_Menu_Filter
+{
+    Efreet_Menu_Filter_Type type;   /**< The type of filter */
+    Efreet_Menu_Filter_Op *op;      /**< The filter operations */
+};
+
+/**
+ * The type of layout
+ */
+enum Efreet_Menu_Layout_Type
+{
+    EFREET_MENU_LAYOUT_MENUNAME,
+    EFREET_MENU_LAYOUT_FILENAME,
+    EFREET_MENU_LAYOUT_SEPARATOR,
+    EFREET_MENU_LAYOUT_MERGE
+};
+
+/**
+ * Efreet_Menu_Layout_Type
+ */
+typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type;
+
+/**
+ * Efreet_Menu_Layout
+ */
+typedef struct Efreet_Menu_Layout Efreet_Menu_Layout;
+
+/**
+ * Efreet_Menu_Layout
+ * Stores information on a layout 
+ */
+struct Efreet_Menu_Layout
+{
+    Efreet_Menu_Layout_Type  type;   /**< The type of layout */
+    char *name;                      /**< The name of the element */
+
+    /* The items below are for Menuname Layout elements */
+    char show_empty;    /**< Whether to show empty menus */
+    char in_line;       /**< Whether this meny can be inlined */
+    char inline_limit;  /**< Number of elements which triggers inline */
+    char inline_header; /**< Whether we should use the header name when this menu is inlined */
+    char inline_alias;  /**< Whether we should use the menu name when inlining */
+};
+
+/**
+ * Efreet_Menu_Desktop
+ */
+typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop;
+
+/**
+ * Efreet_Menu_Desktop
+ * Stores information on a desktop for the menu
+ */
+struct Efreet_Menu_Desktop
+{
+    Efreet_Desktop *desktop;   /**< The desktop we refer too */
+    char *id;                  /**< The desktop file id */
+    unsigned char allocated:1; /**< If this desktop has been allocated */
+};
+
+static char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */
+Ecore_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */
+static const char *efreet_tag_menu = NULL;
+
+static Ecore_Hash *efreet_merged_menus = NULL;
+static Ecore_Hash *efreet_merged_dirs = NULL;
+
+static Ecore_Hash *efreet_menu_handle_cbs = NULL;
+static Ecore_Hash *efreet_menu_filter_cbs = NULL;
+static Ecore_Hash *efreet_menu_move_cbs = NULL;
+static Ecore_Hash *efreet_menu_layout_cbs = NULL;
+
+static const char *efreet_menu_prefix_get(void);
+static Ecore_List *efreet_default_dirs_get(const char *user_dir, 
+                                                    Ecore_List *system_dirs, 
+                                                    const char *suffix);
+
+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, 
+                                            Ecore_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 void efreet_menu_process_app_pool(Ecore_List *pool, Ecore_List *applications,
+                                        Ecore_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 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 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(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b);
+
+static int efreet_menu_save_menu(Efreet_Menu_Internal *internal, FILE *f, int indent);
+static int efreet_menu_save_layout(Ecore_List *list, FILE *f, int indent);
+static int efreet_menu_save_filter(Ecore_List *list, FILE *f, int indent);
+static int efreet_menu_save_filter_op(Efreet_Menu_Filter_Op *op, 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);
+
+/**
+ * @return Returns 1 on success, 0 on failure
+ * @brief Initializes the Efreet Menu system.
+ */
+int
+efreet_menu_init(void)
+{
+    int i;
+
+    struct
+    {
+        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
+    {
+        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
+    {
+        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
+    {
+        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}
+    };
+
+    if (!ecore_string_init()) return 0;
+    if (!efreet_xml_init()) return 0;
+
+    efreet_menu_handle_cbs = ecore_hash_new(NULL, NULL);
+    efreet_menu_filter_cbs = ecore_hash_new(NULL, NULL);
+    efreet_menu_move_cbs = ecore_hash_new(NULL, NULL);
+    efreet_menu_layout_cbs = ecore_hash_new(NULL, NULL);
+    if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs 
+            || !efreet_menu_move_cbs || !efreet_menu_layout_cbs) 
+        return 0;
+
+    ecore_hash_set_free_key(efreet_menu_handle_cbs, 
+                        ECORE_FREE_CB(ecore_string_release));
+    ecore_hash_set_free_key(efreet_menu_filter_cbs, 
+                        ECORE_FREE_CB(ecore_string_release));
+    ecore_hash_set_free_key(efreet_menu_move_cbs, 
+                        ECORE_FREE_CB(ecore_string_release));
+    ecore_hash_set_free_key(efreet_menu_layout_cbs, 
+                        ECORE_FREE_CB(ecore_string_release));
+
+    /* set Menu into it's own so we can check the XML is valid before trying
+     * to handle it */
+    efreet_tag_menu = ecore_string_instance(menu_cbs[0].key);
+
+    for (i = 0; menu_cbs[i].key != NULL; i++)
+        ecore_hash_set(efreet_menu_handle_cbs, 
+                        (void *)ecore_string_instance(menu_cbs[i].key),
+                        menu_cbs[i].cb);
+
+    for (i = 0; filter_cbs[i].key != NULL; i++)
+        ecore_hash_set(efreet_menu_filter_cbs, 
+                        (void *)ecore_string_instance(filter_cbs[i].key),
+                        filter_cbs[i].cb);
+
+    for (i = 0; move_cbs[i].key != NULL; i++)
+        ecore_hash_set(efreet_menu_move_cbs, 
+                        (void *)ecore_string_instance(move_cbs[i].key),
+                        move_cbs[i].cb);
+
+    for (i = 0; layout_cbs[i].key != NULL; i++)
+        ecore_hash_set(efreet_menu_layout_cbs, 
+                        (void *)ecore_string_instance(layout_cbs[i].key),
+                        layout_cbs[i].cb);
+
+    return 1;
+}
+
+/**
+ * @return Returns no value
+ * @brief Initialize legacy kde support. This function blocks while 
+ * the kde-config script is run.
+ */
+int
+efreet_menu_kde_legacy_init(void)
+{
+    FILE *f;
+    char buf[PATH_MAX];
+    char *p, *s;
+    
+    IF_FREE_LIST(efreet_menu_kde_legacy_dirs);
+
+    f = popen("kde-config --path apps", "r");
+    if (!f) return 0;
+
+    efreet_menu_kde_legacy_dirs = ecore_list_new();
+    ecore_list_set_free_cb(efreet_menu_kde_legacy_dirs, 
+                            ECORE_FREE_CB(ecore_string_release));
+
+    /* 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, PATH_MAX, f))
+    {
+        printf("Error initializing KDE legacy information\n");
+        return 0;
+    }
+    s = buf;
+
+    p = strchr(s, ':');
+    while (p)
+    {
+        *p = '\0';
+        ecore_list_append(efreet_menu_kde_legacy_dirs, 
+                            (void *)ecore_string_instance(s));
+        s = p + 1;
+        p = strchr(s, ':');
+    }
+
+    if (*s) 
+        ecore_list_append(efreet_menu_kde_legacy_dirs, 
+                            (void *)ecore_string_instance(s));
+
+    pclose(f);
+    return 1;
+}
+
+/**
+ * @return Returns no value
+ * @brief Shuts down the Efreet menu system
+ */
+void
+efreet_menu_shutdown(void)
+{
+    IF_FREE(efreet_menu_prefix);
+
+    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);
+
+    IF_FREE_HASH(efreet_merged_menus);
+    IF_FREE_HASH(efreet_merged_dirs);
+
+    efreet_xml_shutdown();
+    ecore_string_shutdown();
+}
+
+/**
+ * @return Returns the Efreet_Menu_Internal representation of the default menu or
+ * NULL if none found
+ * @brief Creates the default menu representation
+ */
+Efreet_Menu *
+efreet_menu_get(void)
+{
+    char menu[PATH_MAX];
+    const char *dir;
+    Ecore_List *config_dirs;
+
+    /* 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();
+    ecore_list_goto_first(config_dirs);
+    while ((dir = ecore_list_next(config_dirs)))
+    {
+        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;
+}
+
+/**
+ * @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
+ */
+Efreet_Menu *
+efreet_menu_parse(const char *path)
+{
+    Efreet_Xml *xml;
+    Efreet_Menu_Internal *internal = NULL;
+    Efreet_Menu *entry = NULL;
+    Ecore_List *search_dirs;
+
+    xml = efreet_xml_new(path);
+    if (!xml) return NULL;
+
+    /* make sure we've got a <Menu> to start with */
+    if (xml->tag != efreet_tag_menu)
+    {
+        printf("Menu file didn't start with <Menu> tag.\n");
+        efreet_xml_del(xml);
+        return NULL;
+    }
+
+    IF_FREE_HASH(efreet_merged_menus);
+    efreet_merged_menus = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(efreet_merged_menus, ECORE_FREE_CB(free));
+
+    IF_FREE_HASH(efreet_merged_dirs);
+    efreet_merged_dirs = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+    ecore_hash_set_free_key(efreet_merged_dirs, ECORE_FREE_CB(free));
+
+    /* split appart the filename and the path */
+    internal = efreet_menu_internal_new();
+
+    /* Set default values */
+    internal->show_empty = 0;
+    internal->in_line = 0;
+    internal->inline_limit = 4;
+    internal->inline_header = 1;
+    internal->inline_alias = 0;
+
+    search_dirs = efreet_config_dirs_get();
+
+    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;
+}
+
+/**
+ * @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
+ */
+int
+efreet_menu_save(Efreet_Menu_Internal *internal, const char *path __UNUSED__)
+{
+    FILE *f;
+    int ret;
+
+    f = stdout; //fopen(path, "w");
+    if (!f) return 0;
+    fprintf(f, "<?xml version=\"1.0\"?>\n");
+    fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
+                "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
+    ret = efreet_menu_save_menu(internal, f, 0);
+//    fclose(f);
+    return ret;
+}
+
+static int
+efreet_menu_save_menu(Efreet_Menu_Internal *internal, FILE *f, int indent)
+{
+    /* XXX: LegacyDir, KDELegacyDir */
+    /* We don't support saving moves. The menu saved has already gone thorugh
+     * moves. (Move (Old, New)) */
+    /* We don't support deletes, as we will directly modify this menu.
+     * (Deleted, NotDeleted) */
+    /* We don't support merges. The menu saved has already gone thorugh merges.
+     * (MergeFile, MergeDir, DefaultMergeDirs */
+    Ecore_List *dirs;
+    int default_app_dirs = 0, default_directory_dirs = 0;
+
+    efreet_menu_save_indent(f, indent);
+    fprintf(f, "<Menu>\n");
+    efreet_menu_save_indent(f, indent + 1);
+    fprintf(f, "<Name>%s</Name>\n", internal->name.internal);
+    if (internal->app_dirs)
+    {
+        Efreet_Menu_App_Dir *dir;
+        dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
+                "applications");
+
+        ecore_list_goto_first(internal->app_dirs);
+        while ((dir = ecore_list_next(internal->app_dirs)))
+        {
+            if (dirs && ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), dir->path))
+                default_app_dirs = 1;
+            else
+            {
+                size_t len;
+
+                efreet_menu_save_indent(f, indent + 1);
+                len = strlen(internal->file.path);
+                if (!strncmp(dir->path, internal->file.path, len))
+                    fprintf(f, "<AppDir>%s</AppDir>\n", dir->path + len + 1);
+                else
+                    fprintf(f, "<AppDir>%s</AppDir>\n", dir->path);
+            }
+        }
+        IF_FREE_LIST(dirs);
+    }
+    if (default_app_dirs)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<DefaultAppDirs/>\n");
+    }
+
+    if (internal->directory_dirs)
+    {
+        const char *dir;
+        dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
+                "desktop-directories");
+
+        ecore_dlist_goto_first(internal->directory_dirs);
+        while ((dir = ecore_dlist_next(internal->directory_dirs)))
+        {
+            if (dirs && ecore_list_find(dirs, ECORE_COMPARE_CB(strcmp), dir))
+                default_directory_dirs = 1;
+            else
+            {
+                size_t len;
+
+                efreet_menu_save_indent(f, indent + 1);
+                len = strlen(internal->file.path);
+                if (!strncmp(dir, internal->file.path, len))
+                    fprintf(f, "<DirectoryDir>%s</DirectoryDir>\n", dir + len + 1);
+                else
+                    fprintf(f, "<DirectoryDir>%s</DirectoryDir>\n", dir);
+            }
+        }
+        IF_FREE_LIST(dirs);
+    }
+    if (default_directory_dirs)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<DefaultDirectoryDirs/>\n");
+    }
+    if (internal->directories)
+    {
+        const char *dir;
+
+        ecore_list_goto_first(internal->directories);
+        while ((dir = ecore_list_next(internal->directories)))
+        {
+            efreet_menu_save_indent(f, indent + 1);
+            fprintf(f, "<Directory>%s</Directory>\n", dir);
+        }
+    }
+
+    if (internal->default_layout)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<DefaultLayout show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\""
+                " inline_limit=\"%d\" inline_alias=\"%s\">\n",
+                internal->show_empty ? "true" : "false",
+                internal->in_line ? "true" : "false",
+                internal->inline_header ? "true" : "false",
+                internal->inline_limit,
+                internal->inline_alias ? "true" : "false");
+        if (!efreet_menu_save_layout(internal->default_layout, f, indent + 2)) return 0;
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "</DefaultLayout>\n");
+    }
+    if (internal->layout)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "<Layout>\n");
+        if (!efreet_menu_save_layout(internal->layout, f, indent + 2)) return 0;
+        efreet_menu_save_indent(f, indent + 1);
+        fprintf(f, "</Layout>\n");
+    }
+    if (internal->seen_allocated)
+    {
+        efreet_menu_save_indent(f, indent + 1);
+        if (internal->only_unallocated)
+            fprintf(f, "<OnlyUnallocated/>\n");
+        else
+            fprintf(f, "<NotOnlyUnallocated/>\n");
+    }
+    /* XXX: Print filters, not just the applications in this menu */
+    if (internal->filters)
+    {
+        if (!efreet_menu_save_filter(internal->filters, f, indent + 1)) return 0;
+    }
+    if (internal->sub_menus)
+    {
+        Efreet_Menu_Internal *sub;
+        ecore_list_goto_first(internal->sub_menus);
+        while ((sub = ecore_list_next(internal->sub_menus)))
+            efreet_menu_save_menu(sub, f, indent + 1);
+    }
+    efreet_menu_save_indent(f, indent);
+    fprintf(f, "</Menu>\n");
+    return 1;
+}
+
+static int
+efreet_menu_save_layout(Ecore_List *list, FILE *f, int indent)
+{
+    Efreet_Menu_Layout *layout;
+
+    if (ecore_list_is_empty(list)) return 1;
+
+    ecore_list_goto_first(list);
+    while ((layout = ecore_list_next(list)))
+    {
+        efreet_menu_save_indent(f, indent);
+        if (layout->type == EFREET_MENU_LAYOUT_MENUNAME)
+            fprintf(f, "<Menuname>%s</Menuname>\n", layout->name);
+        else if (layout->type == EFREET_MENU_LAYOUT_FILENAME)
+            fprintf(f, "<Filename>%s</Filename>\n", layout->name);
+        else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
+            fprintf(f, "<Merge type=\"%s\"/>\n", layout->name);
+        else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
+            fprintf(f, "<Separator/>\n");
+    }
+    return 1;
+}
+
+static int
+efreet_menu_save_filter(Ecore_List *list, FILE *f, int indent)
+{
+    Efreet_Menu_Filter *filter;
+
+    ecore_list_goto_first(list);
+    while ((filter = ecore_list_next(list)))
+    {
+        const char *type = "";
+        if (filter->type == EFREET_MENU_FILTER_INCLUDE) type = "Include";
+        else type = "Exclude";
+
+        efreet_menu_save_indent(f, indent);
+        fprintf(f, "<%s>\n", type);
+        if (!efreet_menu_save_filter_op(filter->op, f, indent + 1)) return 0;
+        efreet_menu_save_indent(f, indent);
+        fprintf(f, "</%s>\n", type);
+    }
+    return 1;
+}
+
+static int
+efreet_menu_save_filter_op(Efreet_Menu_Filter_Op *op, FILE *f, int indent)
+{
+    if (op->all)
+    {
+        efreet_menu_save_indent(f, indent);
+        fprintf(f, "<All/>\n");
+    }
+    if (op->categories)
+    {
+        char *cat;
+
+        ecore_list_goto_first(op->categories);
+        while ((cat = ecore_list_next(op->categories)))
+        {
+            efreet_menu_save_indent(f, indent);
+            fprintf(f, "<Category>%s</Category>\n", cat);
+        }
+    }
+    if (op->filenames)
+    {
+        char *file;
+
+        ecore_list_goto_first(op->filenames);
+        while ((file = ecore_list_next(op->filenames)))
+        {
+            efreet_menu_save_indent(f, indent);
+            fprintf(f, "<Filename>%s</Filename>\n", file);
+        }
+    }
+    if (op->filters)
+    {
+        Efreet_Menu_Filter_Op *child;
+
+        ecore_list_goto_first(op->filters);
+        while ((child = ecore_list_next(op->filters)))
+        {
+            const char *type = "";
+            if (child->type == EFREET_MENU_FILTER_OP_OR) type = "Or";
+            else if (child->type == EFREET_MENU_FILTER_OP_AND) type = "And";
+            else type = "Not";
+
+            efreet_menu_save_indent(f, indent);
+            fprintf(f, "<%s>\n", type);
+            efreet_menu_save_filter_op(child, f, indent + 1);
+            efreet_menu_save_indent(f, indent);
+            fprintf(f, "</%s>\n", type);
+        }
+    }
+    return 1;
+}
+
+static int
+efreet_menu_save_indent(FILE *f, int indent)
+{
+    int i;
+
+    for (i = 0; i < indent; i++)
+        fprintf(f, "  ");
+    return 1;
+}
+
+/**
+ * @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
+ */
+void
+efreet_menu_dump(Efreet_Menu *menu, const char *indent)
+{
+    printf("%s%s: ", indent, menu->name);
+    printf("%s\n", (menu->icon ? menu->icon : "No icon"));
+
+    /* XXX dump the rest of the menu info */
+
+    if (menu->entries)
+    {
+        Efreet_Menu *entry;
+        char *new_indent;
+        int len;
+
+        len = strlen(indent) + 3;
+        new_indent = malloc(sizeof(char *) * len);
+        snprintf(new_indent, len, "%s  ", indent);
+
+        ecore_list_goto_first(menu->entries);
+        while ((entry = ecore_list_next(menu->entries)))
+        {
+            if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
+                printf("%s|---\n", indent);
+            else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
+                printf("%s|-%s\n", 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)
+                printf("%s|---%s\n", indent, entry->name);
+        }
+
+        FREE(new_indent);
+    }
+}
+
+/**
+ * @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);
+    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_FREE(internal->file.path);
+    IF_FREE(internal->file.name);
+
+    IF_RELEASE(internal->name.internal);
+    internal->name.name = NULL;
+
+    IF_FREE_LIST(internal->applications);
+
+    IF_FREE_DLIST(internal->directories);
+    IF_FREE_DLIST(internal->app_dirs);
+    IF_FREE_LIST(internal->app_pool);
+    IF_FREE_DLIST(internal->directory_dirs);
+    IF_FREE_HASH(internal->directory_cache);
+
+    IF_FREE_LIST(internal->moves);
+    IF_FREE_LIST(internal->filters);
+
+    IF_FREE_LIST(internal->sub_menus);
+
+    IF_FREE_LIST(internal->layout);
+    IF_FREE_LIST(internal->default_layout);
+
+    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)
+{
+    char *prefix;
+
+    if (efreet_menu_prefix) return efreet_menu_prefix;
+
+    prefix = getenv("XDG_MENU_PREFIX");
+    if (prefix) efreet_menu_prefix = strdup(prefix);
+    else efreet_menu_prefix = strdup("");
+
+    return efreet_menu_prefix;
+}
+
+/**
+ * @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.
+ */
+static Ecore_List *
+efreet_default_dirs_get(const char *user_dir, Ecore_List *system_dirs, 
+                                                    const char *suffix)
+{
+    const char *xdg_dir;
+    char dir[PATH_MAX];
+    Ecore_List *list;
+
+    list = ecore_list_new();
+    ecore_list_set_free_cb(list, ECORE_FREE_CB(free));
+
+    snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
+    ecore_list_append(list, strdup(dir));
+
+    ecore_list_goto_first(system_dirs);
+    while ((xdg_dir = ecore_list_next(system_dirs)))
+    {
+        snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
+        ecore_list_append(list, strdup(dir));
+    }
+
+    return list;
+}
+
+/**
+ * @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;
+    int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+
+    ecore_list_goto_last(xml->children);
+    while ((child = ecore_dlist_previous(xml->children)))
+    {
+        cb = ecore_hash_get(efreet_menu_handle_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(internal, child))
+                return 0;
+        }
+        else
+        {
+            printf("Unknown XML tag: %s\n", 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();
+    internal->file.path = strdup(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 = ecore_list_find(parent->sub_menus, 
+                ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), internal)))
+    {
+
+        efreet_menu_concatenate(match, internal);
+        efreet_menu_internal_free(internal);
+    }
+    else
+        ecore_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)
+{
+    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 (ecore_list_find(parent->app_dirs, 
+            ECORE_COMPARE_CB(efreet_menu_cb_app_dirs_compare), path))
+    {
+        FREE(path);
+        return 1;
+    }
+
+    app_dir = efreet_menu_app_dir_new();
+    app_dir->path = path;
+
+    ecore_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__)
+{
+    Ecore_List *dirs, *prepend;
+    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");
+    prepend = ecore_list_new();
+    ecore_list_goto_first(dirs);
+    while ((dir = ecore_list_next(dirs)))
+    {
+        Efreet_Menu_App_Dir *app_dir;
+
+        if (ecore_list_find(parent->app_dirs, 
+                    ECORE_COMPARE_CB(efreet_menu_cb_app_dirs_compare), dir))
+            continue;
+
+        app_dir = efreet_menu_app_dir_new();
+        app_dir->path = strdup(dir);
+
+        ecore_list_append(prepend, app_dir);
+    }
+    ecore_list_destroy(dirs);
+    ecore_list_prepend_list(parent->app_dirs, prepend);
+    ecore_list_destroy(prepend);
+
+    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)
+{
+    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 (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), path))
+    {
+        FREE(path);
+        return 1;
+    }
+
+    ecore_dlist_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__)
+{
+    Ecore_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");
+    ecore_list_goto_first(dirs);
+    while ((dir = ecore_list_next(dirs)))
+    {
+        if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), dir))
+            continue;
+
+        ecore_dlist_prepend(parent->directory_dirs, strdup(dir));
+    }
+
+    ecore_list_destroy(dirs);
+
+    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) 
+    {
+        printf("efreet_menu_handle_name() setting second name into menu\n");
+        return 0;
+    }
+
+    /* ignore the name if it contains a / */
+    if (strchr(xml->text, '/')) return 1;
+
+    parent->name.internal = ecore_string_instance(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);
+    ecore_dlist_prepend(parent->directories, strdup(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;
+
+    if (!op->filenames)
+    {
+        op->filenames = ecore_list_new();
+        ecore_list_set_free_cb(op->filenames, free);
+    }
+
+    ecore_list_append(op->filenames, strdup(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;
+
+    if (!op->categories)
+    {
+        op->categories = ecore_list_new();
+        ecore_list_set_free_cb(op->categories, free);
+    }
+
+    ecore_list_append(op->categories, strdup(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)
+{
+    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
+    {
+        Ecore_List *search_dirs;
+        const char *dir, *p;
+        int len = 0;
+
+        if (!parent->file.path) 
+        {
+            printf("efreet_menu_handle_merge_file() missing menu path ...\n");
+            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 */
+
+        ecore_list_goto_first(search_dirs);
+        dir = efreet_config_home_get();
+        len = strlen(dir);
+        if (strncmp(dir, parent->file.path, len))
+        {
+            while ((dir = ecore_list_next(search_dirs)))
+            {
+                if (!strncmp(dir, parent->file.path, len))
+                    break;
+            }
+        }
+
+        if (!dir)
+        {
+            printf("efreet_menu_handle_merge_file() failed to find "
+                    "menu parent directory\n");
+            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 + len;
+
+        /* whatever dirs are left in the search dir we need to look for the
+         * menu with the same relative filename */
+        while ((dir = ecore_list_next(search_dirs)))
+        {
+            char file[PATH_MAX];
+
+            snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""), 
+                                                        parent->file.name); 
+            if (ecore_file_exists(file))        
+            {
+                path = strdup(file);
+                break;
+            }
+        }
+    }
+
+    /* nothing to do if no file found */
+    if (!path) return 1;
+
+    if (!efreet_menu_merge(parent, xml, path))
+        ret = 0;
+
+    FREE(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 *realpath;
+
+    if (!parent || !xml || !path) return 0;
+
+    /* do nothing if the file doesn't exist */
+    if (!ecore_file_exists(path)) return 1;
+
+    realpath = ecore_file_realpath(path);
+    if (realpath[0] == '\0') 
+    {
+        printf("efreet_menu_merge() unable to get real path for %s\n", path);
+        return 0;
+    }
+
+    /* don't merge the same path twice */
+    if (ecore_hash_get(efreet_merged_menus, realpath))
+        return 1;
+
+    ecore_hash_set(efreet_merged_menus, strdup(realpath), (void *)1);
+
+    merge_xml = efreet_xml_new(realpath);
+    FREE(realpath);
+
+    if (!merge_xml) 
+    {
+        printf("efreet_menu_merge() failed to read in the "
+                "merge file (%s)\n", realpath);
+        return 0;
+    }
+
+    internal = efreet_menu_internal_new();
+    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)
+{
+    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)) 
+    {
+        FREE(path);
+        return 1;
+    }
+
+    ret = efreet_menu_merge_dir(parent, xml, path);
+    FREE(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)
+{
+    char dir_path[PATH_MAX];
+    DIR *files;
+    struct dirent *file;
+
+    if (!parent || !xml || !path) return 0;
+
+    /* check to see if we've merged this directory already */
+    if (ecore_hash_get(efreet_merged_dirs, path)) return 1;
+    ecore_hash_set(efreet_merged_dirs, strdup(path), (void *)1);
+
+    files = opendir(path);
+    if (!files) return 1;
+
+    while ((file = readdir(files)))
+    {
+        char *p;
+
+        if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
+        p = strrchr(file->d_name, '.');
+        if (!p) continue;
+        if (strcmp(p, ".menu")) continue;
+
+        snprintf(dir_path, PATH_MAX, "%s/%s", path, file->d_name);
+        if (!efreet_menu_merge(parent, xml, dir_path))
+            return 0;
+    }
+    closedir(files);
+
+    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)
+{
+    Ecore_List *dirs;
+    char path[PATH_MAX], *p;
+    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 = strdup("applications");
+
+    else if ((!strcmp(prefix, "kde-") && 
+            (!strcmp(parent->file.name, "kde-applications.menu"))))
+        p = strdup("applications");
+
+    else 
+    {
+        char *s;
+
+        p = strdup(parent->file.name);
+        s = strrchr(p, '.');
+        if (s) *s = '\0';
+    }
+    snprintf(path, sizeof(path), "menus/%s-merged", p);
+    FREE(p);
+
+    dirs = efreet_default_dirs_get(efreet_config_home_get(), 
+                                    efreet_config_dirs_get(), path);
+    ecore_list_goto_first(dirs);
+    while ((p = ecore_list_remove_first(dirs)))
+    {
+        efreet_menu_merge_dir(parent, xml, p);
+        FREE(p);
+    }
+    ecore_list_destroy(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 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"));
+    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)
+{
+    char *path, file_path[PATH_MAX];
+    Efreet_Menu_Internal *legacy_internal;
+    Efreet_Menu_Filter *filter;
+    Efreet_Menu_App_Dir *app_dir;
+    int path_len, count = 0;
+    DIR *files;
+    struct dirent *file;
+
+    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 (!ecore_file_exists(path)) 
+    {
+        FREE(path);
+        return NULL;
+    }
+
+    legacy_internal = efreet_menu_internal_new();
+    legacy_internal->name.internal = ecore_string_instance(ecore_file_get_file(path));
+
+    /* add the legacy dir as an app dir */
+    app_dir = efreet_menu_app_dir_new();
+    app_dir->path = strdup(path);
+    app_dir->legacy = 1;
+    if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
+
+    efreet_menu_create_app_dirs_list(legacy_internal);
+    ecore_list_append(legacy_internal->app_dirs, app_dir);
+#if !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 = strdup(path);
+        app_dir->legacy = 1;
+        if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
+        ecore_list_append(root->app_dirs, app_dir);
+    }
+#endif
+
+    /* add the legacy dir as a directory dir */
+    efreet_menu_create_directory_dirs_list(legacy_internal);
+    ecore_dlist_append(legacy_internal->directory_dirs, strdup(path));
+
+    /* setup a filter for all the conforming .desktop files in the legacy
+     * dir */
+    filter = efreet_menu_filter_new();
+    filter->type = EFREET_MENU_FILTER_INCLUDE;
+
+    filter->op->type = EFREET_MENU_FILTER_OP_OR;
+    filter->op->filenames = ecore_list_new();
+    ecore_list_set_free_cb(filter->op->filenames, free);
+
+    efreet_menu_create_filter_list(legacy_internal);
+    ecore_list_append(legacy_internal->filters, filter);
+
+    path_len = strlen(path);
+    files = opendir(path);
+    while ((file = readdir(files)))
+    {
+        Efreet_Desktop *desktop = NULL;
+        char buf[PATH_MAX];
+        char *exten;
+
+        if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
+        file_path[0] = '\0';
+        ecore_strlcpy(file_path, path, PATH_MAX);
+        ecore_strlcpy(file_path + path_len, "/", PATH_MAX - path_len);
+        ecore_strlcpy(file_path + path_len + 1, file->d_name, PATH_MAX - path_len - 1);
+       
+        /* recurse into sub directories */
+        if (ecore_file_is_dir(file_path))
+        {
+            Efreet_Menu_Internal *ret;
+
+            ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
+                                                        legacy_internal, file_path, prefix);
+            if (!ret)
+            {
+                efreet_menu_internal_free(legacy_internal);
+                FREE(path);
+                closedir(files);
+                return NULL;
+            }
+
+            efreet_menu_create_sub_menu_list(legacy_internal);
+            ecore_list_prepend(legacy_internal->sub_menus, ret);
+
+            continue;
+        }
+
+        if (!strcmp(file->d_name, ".directory"))
+        {
+            legacy_internal->directory = efreet_desktop_get(file_path);
+            if (legacy_internal->directory
+                    && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
+                legacy_internal->directory = NULL;
+            continue;
+        }
+
+        exten = strrchr(file->d_name, '.');
+
+        if (exten && !strcmp(exten, ".desktop"))
+            desktop = efreet_desktop_get(file_path);
+
+        if (!desktop) continue;
+
+        /* if the .desktop has categories it isn't legacy */
+        if (efreet_desktop_category_count_get(desktop) != 0)
+            continue;
+
+        efreet_desktop_category_add(desktop, "Legacy");
+
+        if (prefix)
+        {
+            snprintf(buf, PATH_MAX, "%s%s", prefix, file->d_name);
+            ecore_list_append(filter->op->filenames, strdup(buf));
+        }
+        else
+            ecore_list_append(filter->op->filenames, strdup(file->d_name));
+
+        count ++;
+    }
+    closedir(files);
+    
+    FREE(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__)
+{
+    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) */
+    ecore_list_goto_first(efreet_menu_kde_legacy_dirs);
+    while ((dir = ecore_list_next(efreet_menu_kde_legacy_dirs)))
+    {
+        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;
+
+    if (!parent || !xml) return 0;
+
+    efreet_menu_create_move_list(parent);
+
+    ecore_list_goto_first(xml->children);
+    while ((child = ecore_list_next(xml->children)))
+    {
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
+
+        cb = ecore_hash_get(efreet_menu_move_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(parent, child))
+                return 0;
+        }
+        else
+        {
+            printf("efreet_menu_handle_move() unknown tag found "
+                    "in Move (%s)\n", 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) 
+    {
+        printf("efreet_menu_handle_old() saw second <Old> "
+                "before seeing <New>\n");
+        return 0;
+    }
+
+    /* If we already moved this menu, remove the old move */
+    /* XXX This seems wrong, but it makes efreet pass the fdo tests */
+#if !STRICT_SPEC
+    move = ecore_list_find(parent->moves,
+            ECORE_COMPARE_CB(efreet_menu_cb_move_compare), xml->text);
+    if (move) ecore_list_remove_destroy(parent->moves);
+#endif
+
+    move = efreet_menu_move_new();
+    move->old_name = strdup(xml->text);
+
+    parent->current_move = move;
+    ecore_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)
+    {
+        printf("efreet_menu_handle_new() saw New before seeing Old\n");
+        return 0;
+    }
+
+    parent->current_move->new_name = strdup(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;
+
+    if (!parent || !xml) return 0;
+
+    /* We use the last existing layout */
+    if (parent->layout) return 1;
+
+    efreet_menu_create_layout_list(parent);
+
+    ecore_list_goto_first(xml->children);
+    while ((child = ecore_list_next(xml->children)))
+    {
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+
+        cb = ecore_hash_get(efreet_menu_layout_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(parent, child, 0))
+                return 0;
+        }
+        else
+        {
+            printf("efreet_menu_handle_move() unknown tag found "
+                    "in Layout (%s)\n", 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;
+
+    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);
+
+    ecore_list_goto_first(xml->children);
+    while ((child = ecore_list_next(xml->children)))
+    {
+        int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
+
+        cb = ecore_hash_get(efreet_menu_layout_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(parent, child, 1))
+                return 0;
+        }
+        else
+        {
+            printf("efreet_menu_handle_move() unknown tag found in "
+                    "DefaultLayout (%s)\n", 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)
+    {
+        printf("efreet_menu_handle_layout_menuname() The Menuname tag in "
+                "layout needs a filename.\n");
+        return 0;
+    }
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_MENUNAME;
+    layout->name = strdup(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) ecore_list_append(parent->default_layout, layout);
+    else ecore_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)
+    {
+        printf("efreet_menu_handle_layout_filename() The Filename tag in "
+                "layout needs a filename.\n");
+        return 0;
+    }
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_FILENAME;
+    layout->name = strdup(xml->text);
+
+    if (def) ecore_list_append(parent->default_layout, layout);
+    else ecore_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)
+        ecore_list_append(parent->default_layout, layout);
+    else
+        ecore_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)
+    {
+        printf("efreet_menu_handle_layout_merge() The Merge tag in layout "
+                "needs a type attribute.\n");
+        return 0;
+    }
+
+    if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
+    {
+        printf("efreet_menu_handle_layout_merge() The type attribute for "
+                "the Merge tag contains an unknown value (%s).\n", attr);
+        return 0;
+    }
+
+    layout = efreet_menu_layout_new();
+    layout->type = EFREET_MENU_LAYOUT_MERGE;
+    layout->name = strdup(attr);
+
+    if (def) ecore_list_append(parent->default_layout, layout);
+    else ecore_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();
+    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;
+    }
+
+    ecore_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;
+
+    ecore_list_goto_first(xml->children);
+    while ((child = ecore_list_next(xml->children)))
+    {
+        int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
+
+        cb = ecore_hash_get(efreet_menu_filter_cbs, child->tag);
+        if (cb)
+        {
+            if (!cb(op, child))
+                return 0;
+        }
+        else
+        {
+            printf("efreet_menu_handle_filter_op() unknown tag in filter (%s)\n", 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);
+    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 layout: The layout to work with
+ * @return Returns no data
+ * @brief Frees the given layout and all data
+ */
+static void
+efreet_menu_layout_free(Efreet_Menu_Layout *layout)
+{
+    if (!layout) return;
+
+    IF_FREE(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);
+    IF_FREE_LIST(op->filenames);
+    IF_FREE_LIST(op->filters);
+
+    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_FREE(md->id);
+    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;
+}
+
+/**
+ * @internal
+ * @param entry: The Efreet_Menu to free
+ * @return Returns no value
+ * @brief Frees the given structure
+ */
+void
+efreet_menu_free(Efreet_Menu *entry)
+{
+    IF_RELEASE(entry->name);
+    IF_RELEASE(entry->icon);
+    IF_FREE_LIST(entry->entries);
+    IF_FREE(entry->id);
+    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;
+    }
+
+    if (!op->filters)
+    {
+        op->filters = ecore_list_new();
+        ecore_list_set_free_cb(op->filters, 
+                    ECORE_FREE_CB(efreet_menu_filter_op_free));
+    }
+
+    ecore_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)
+{
+    /* 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;
+
+        ecore_list_goto_first(internal->sub_menus);
+        while ((sub_internal = ecore_list_next(internal->sub_menus)))
+        {
+            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)
+{
+    /* 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;
+
+        ecore_list_goto_first(internal->sub_menus);
+        while ((sub_internal = ecore_list_next(internal->sub_menus)))
+        {
+            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 taht 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;
+    int included = 0;
+
+    /* nothing to do if we're checking the other option */
+    if (only_unallocated != internal->only_unallocated) return;
+
+    if (!internal->applications) internal->applications = ecore_list_new();
+    else ecore_list_clear(internal->applications);
+
+    if (!internal->filters) return;
+
+    ecore_list_goto_first(internal->filters);
+    while ((filter = ecore_list_next(internal->filters)))
+    {
+        Efreet_Menu_Desktop *md;
+
+        /* 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)
+        {
+            Ecore_Hash *matches;
+
+            matches = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+            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 {
+                    efreet_menu_process_app_pool(parent->app_pool, 
+                                                internal->applications, matches, filter,
+                                                internal->only_unallocated);
+                } while ((parent = parent->parent));
+            }
+            ecore_hash_destroy(matches);
+        }
+        else
+        {
+            /* check each item in our menu so far and see if it's excluded */
+            ecore_list_goto_first(internal->applications);
+            while ((md = ecore_list_current(internal->applications)))
+            {
+                if (efreet_menu_filter_matches(filter->op, md))
+                    ecore_list_remove(internal->applications);
+                else
+                    ecore_list_next(internal->applications);
+            }
+        }
+    }
+
+    /* sort the menu applications. we do this in process filters so it will only
+     * be done once per menu.*/
+    if (internal->applications)
+    {
+        int count;
+
+        count = ecore_list_nodes(internal->applications);
+        if (count)
+        {
+            Ecore_Sheap *sheap;
+            Efreet_Menu_Desktop *md;
+
+            sheap = ecore_sheap_new(
+                        ECORE_COMPARE_CB(efreet_menu_cb_md_compare), count);
+            while ((md = ecore_list_remove_first(internal->applications)))
+                ecore_sheap_insert(sheap, md);
+
+            while ((md = ecore_sheap_extract(sheap)))
+            {
+                if (md->desktop->no_display) continue;
+                ecore_list_append(internal->applications, md);
+            }
+
+            ecore_sheap_destroy(sheap);
+        }
+
+        /* Don't keep the list if it is empty */
+        if (ecore_list_is_empty(internal->applications))
+            IF_FREE_LIST(internal->applications);
+    }
+}
+
+/**
+ * @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
+void efreet_menu_process_app_pool(Ecore_List *pool, Ecore_List *applications, 
+                                        Ecore_Hash *matches,
+                                        Efreet_Menu_Filter *filter,
+                                        unsigned int only_unallocated)
+{
+    Efreet_Menu_Desktop *md;
+
+    if (!pool) return;
+
+    ecore_list_goto_first(pool);
+    while ((md = ecore_list_next(pool)))
+    {
+        if (ecore_hash_get(matches, md->id)) continue;
+        if (only_unallocated && md->allocated) continue;
+        if (efreet_menu_filter_matches(filter->op, md))
+        {
+            ecore_list_append(applications, md);
+            ecore_hash_set(matches, md->id, md);
+            md->allocated = 1;
+        }
+    }
+}
+
+/**
+ * @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;
+    char *t;
+
+    if (op->all) return 1;
+
+    if (op->categories && md->desktop->categories)
+    {
+        ecore_list_goto_first(op->categories);
+        while ((t = ecore_list_next(op->categories)))
+        {
+            if (ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t))
+                return 1;
+        }
+    }
+
+    if (op->filenames)
+    {
+        ecore_list_goto_first(op->filenames);
+        while ((t = ecore_list_next(op->filenames)))
+            if (!strcmp(t, md->id)) return 1;
+    }
+
+    if (op->filters)
+    {
+        ecore_list_goto_first(op->filters);
+        while ((child = ecore_list_next(op->filters)))
+        {
+            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;
+    char *t;
+
+    if (op->categories) 
+    {
+        if ((ecore_list_nodes(op->categories) > 0) && !md->desktop->categories)
+            return 0;
+
+        ecore_list_goto_first(op->categories);
+        while ((t = ecore_list_next(op->categories)))
+        {
+            if (!ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t))
+                return 0;
+        }
+    }
+
+    if (op->filenames)
+    {
+        ecore_list_goto_first(op->filenames);
+        while ((t = ecore_list_next(op->filenames)))
+        {
+            if (strcmp(t, md->id)) return 0;
+        }
+    }
+
+    if (op->filters)
+    {
+        ecore_list_goto_first(op->filters);
+        while ((child = ecore_list_next(op->filters)))
+        {
+            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;
+    char *t;
+
+    /* !all means no desktops match */
+    if (op->all) return 0;
+
+    if (op->categories)
+    {
+        if ((ecore_list_nodes(op->categories) > 0) && !md->desktop->categories)
+            return 1;
+
+        ecore_list_goto_first(op->categories);
+        while ((t = ecore_list_next(op->categories)))
+        {
+            if (ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t))
+                return 0;
+        }
+    }
+
+    if (op->filenames)
+    {
+        ecore_list_goto_first(op->filenames);
+        while ((t = ecore_list_next(op->filenames)))
+        {
+            if (!strcmp(t, md->id)) return 0;
+        }
+    }
+
+    if (op->filters)
+    {
+        ecore_list_goto_first(op->filters);
+        while ((child = ecore_list_next(op->filters)))
+        {
+            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);
+        ecore_dlist_prepend_list(dest->directories, src->directories);
+    }
+
+    if (src->app_dirs)
+    {
+        efreet_menu_create_app_dirs_list(dest);
+        ecore_list_prepend_list(dest->app_dirs, src->app_dirs);
+    }
+
+    if (src->directory_dirs)
+    {
+        efreet_menu_create_directory_dirs_list(dest);
+        ecore_dlist_prepend_list(dest->directory_dirs, src->directory_dirs);
+    }
+
+    if (src->moves)
+    {
+        efreet_menu_create_move_list(dest);
+        ecore_list_prepend_list(dest->moves, src->moves);
+    }
+
+    if (src->filters)
+    {
+        efreet_menu_create_filter_list(dest);
+        ecore_list_prepend_list(dest->filters, src->filters);
+    }
+
+    if (src->sub_menus)
+    {
+        efreet_menu_create_sub_menu_list(dest);
+
+        while ((submenu = ecore_list_remove_last(src->sub_menus)))
+        {
+            Efreet_Menu_Internal *match;
+    
+            /* if this menu is in the list already we just add to that */
+            if ((match = ecore_list_find(dest->sub_menus,
+                        ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), submenu)))
+            {
+                efreet_menu_concatenate(match, submenu);
+                efreet_menu_internal_free(submenu);
+            }
+            else
+                ecore_list_prepend(dest->sub_menus, submenu);
+        }
+    }
+}
+
+/**
+ * @internal
+ * @param menu: The menu to work with
+ * @return Returns no value
+ * @brief Handles any \<Move\> commands in the menus
+ */
+static void
+efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
+{
+    Efreet_Menu_Internal *child;
+    Efreet_Menu_Move *move;
+
+    /* child moves are handled before parent moves */
+    if (internal->sub_menus)
+    {
+        ecore_list_goto_first(internal->sub_menus);
+        while ((child = ecore_list_next(internal->sub_menus)))
+            efreet_menu_resolve_moves(child);
+    }
+
+    /* nothing to do if this menu has no moves */
+    if (!internal->moves) return;
+    
+    ecore_list_goto_first(internal->moves);
+    while ((move = ecore_list_next(internal->moves)))
+    {
+        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 */
+        ecore_list_remove(parent->sub_menus);
+
+        /* 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;
+
+            /* if the dest path has /'s in it then we need to add menus to
+             * fill out the paths */
+            t = strdup(move->new_name);
+            tmp = t; 
+            path = strchr(tmp, '/');
+            while (path)
+            {
+                Efreet_Menu_Internal *ancestor;
+
+                *path = '\0';
+
+                ancestor = efreet_menu_internal_new();
+                ancestor->name.internal = ecore_string_instance(tmp);
+
+                efreet_menu_create_sub_menu_list(parent);
+                ecore_list_append(parent->sub_menus, ancestor);
+
+                parent = ancestor;
+                tmp = ++path;
+                path = strchr(tmp, '/');
+            }
+
+            IF_RELEASE(origin->name.internal);
+            origin->name.internal = ecore_string_instance(tmp);
+
+            efreet_menu_create_sub_menu_list(parent);
+            ecore_list_append(parent->sub_menus, origin);
+
+            FREE(t);
+        }
+        else
+        {
+            efreet_menu_concatenate(dest, origin);
+            efreet_menu_internal_free(origin);
+        }
+    }
+    IF_FREE_LIST(internal->moves);
+}
+
+/**
+ * @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;
+
+    if (parent) *parent = internal;
+
+    /* find the correct parent menu */
+    tmp = strdup(name);
+    ptr = tmp;
+    part = strchr(ptr, '/');
+    while (part)
+    {
+        *part = '\0';
+
+        if (!ecore_list_find(internal->sub_menus, 
+                    ECORE_COMPARE_CB(efreet_menu_cb_compare_names), ptr))
+        {
+            FREE(tmp);
+            return NULL;
+        }
+
+        internal = ecore_list_current(internal->sub_menus);
+        ptr = ++part;
+        part = strchr(ptr, '/');
+    }
+
+    if (parent) *parent = internal;
+
+    /* find the menu in the parent list */
+    if (!ecore_list_find(internal->sub_menus, 
+                ECORE_COMPARE_CB(efreet_menu_cb_compare_names), ptr))
+    {
+        FREE(tmp);
+        return NULL;
+    }
+
+    FREE(tmp);
+    return ecore_list_current(internal->sub_menus);
+}
+
+static void
+efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
+{
+    char *tmp, *p;
+
+    tmp = strdup(path);
+    p = strrchr(tmp, '/');
+    if (p)
+    {
+        *p = 0;
+        p++;
+
+        internal->file.path = strdup(tmp);
+        internal->file.name = strdup(p);
+    }
+    FREE(tmp);
+}
+
+/**
+ * @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_FREE(move->old_name);
+    IF_FREE(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 app dir to free
+ * @return Returns no value
+ * @brief Frees @a dir 
+ */
+static void
+efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
+{
+    if (!dir) return;
+
+    IF_FREE(dir->path);
+    IF_FREE(dir->prefix);
+
+    FREE(dir);
+}
+
+/**
+ * @internal
+ * @param a: The app dir to compare too
+ * @param b: The path to compare too
+ * @return Returns 1 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)
+{
+    return ecore_str_compare(a->path, b);
+}
+
+static void
+efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->sub_menus) return;
+
+    internal->sub_menus = ecore_list_new();
+    ecore_list_set_free_cb(internal->sub_menus, 
+                        ECORE_FREE_CB(efreet_menu_internal_free));
+}
+
+static void
+efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->app_dirs) return;
+
+    internal->app_dirs = ecore_dlist_new();
+    ecore_list_set_free_cb(internal->app_dirs, 
+                        ECORE_FREE_CB(efreet_menu_app_dir_free));
+}
+
+static void
+efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->directory_dirs) return;
+
+    internal->directory_dirs = ecore_dlist_new();
+    ecore_list_set_free_cb(internal->directory_dirs, ECORE_FREE_CB(free));
+}
+
+static void
+efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->moves) return;
+
+    internal->moves = ecore_list_new();
+    ecore_list_set_free_cb(internal->moves, ECORE_FREE_CB(efreet_menu_move_free));
+}
+
+static void
+efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->filters) return;
+
+    internal->filters = ecore_list_new();
+    ecore_list_set_free_cb(internal->filters, 
+                        ECORE_FREE_CB(efreet_menu_filter_free));
+}
+
+static void
+efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->layout) return;
+
+    internal->layout = ecore_list_new();
+    ecore_list_set_free_cb(internal->layout, 
+                        ECORE_FREE_CB(efreet_menu_layout_free));
+}
+
+static void
+efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->default_layout) return;
+
+    internal->default_layout = ecore_list_new();
+    ecore_list_set_free_cb(internal->default_layout, 
+                        ECORE_FREE_CB(efreet_menu_layout_free));
+}
+
+static void
+efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
+{
+    if (!internal || internal->directories) return;
+    
+    internal->directories = ecore_dlist_new();
+    ecore_list_set_free_cb(internal->directories, free);
+}
+
+static 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) 
+        {
+            printf("efreet_menu_handle_app_dir() missing menu path ...\n");
+            return NULL;
+        }
+        snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
+    }
+
+    len = strlen(path);
+    while (path[len] == '/') path[len--] = 0;
+
+    return strdup(path);
+}
+
+static int
+efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
+{
+    return ecore_str_compare(a->name.internal, b->name.internal);
+}
+
+static int
+efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
+{
+    Efreet_Menu_App_Dir *app_dir;
+
+    if (internal->app_pool)
+    {
+        ecore_list_destroy(internal->app_pool);
+        internal->app_pool = NULL;
+    }
+
+    if (internal->app_dirs)
+    {
+        internal->app_pool = ecore_list_new();
+        ecore_list_set_free_cb(internal->app_pool, ECORE_FREE_CB(efreet_menu_desktop_free));
+
+        ecore_list_goto_first(internal->app_dirs);
+        while ((app_dir = ecore_list_next(internal->app_dirs)))
+            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;
+    DIR *files;
+    char buf[PATH_MAX], buf2[PATH_MAX];
+    struct dirent *file;
+    char *ext;
+
+    files = opendir(path);
+    if (!files) return 1;
+
+    while ((file = readdir(files)))
+    {
+        if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
+        snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
+        if (id)
+            snprintf(buf2, PATH_MAX, "%s-%s", id, file->d_name);
+        else
+            strcpy(buf2, file->d_name);
+
+        if (ecore_file_is_dir(buf))
+        {
+            if (!legacy)
+                efreet_menu_app_dir_scan(internal, buf, buf2, legacy);
+        }
+        else
+        {
+            ext = strrchr(buf, '.');
+
+            if (!ext || strcmp(ext, ".desktop")) continue;
+            desktop = efreet_desktop_get(buf);
+
+            if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION) continue;
+            /* Don't add two files with the same id in the app pool */
+            if (ecore_list_find(internal->app_pool,
+                    ECORE_COMPARE_CB(efreet_menu_cb_md_compare_ids), buf2)) continue;
+
+            menu_desktop = efreet_menu_desktop_new();
+            menu_desktop->desktop = desktop;
+            menu_desktop->id = strdup(buf2);
+            ecore_list_prepend(internal->app_pool, menu_desktop);
+        }
+    }
+    closedir(files);
+
+    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;
+
+    if (internal->directory_dirs)
+    {
+        internal->directory_cache = ecore_hash_new(ecore_str_hash, ecore_str_compare);
+        ecore_hash_set_free_key(internal->directory_cache, ECORE_FREE_CB(free));
+
+        ecore_dlist_goto_last(internal->directory_dirs);
+        while ((path = ecore_dlist_previous(internal->directory_dirs)))
+            efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
+    }
+
+    if (internal->directories)
+    {
+        ecore_dlist_goto_last(internal->directories);
+        while ((path = ecore_dlist_previous(internal->directories)))
+        {
+            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, 
+                                                            Ecore_Hash *cache)
+{
+    Efreet_Desktop *desktop;
+    DIR *files;
+    char buf[PATH_MAX], buf2[PATH_MAX];
+    struct dirent *file;
+    char *ext;
+
+    files = opendir(path);
+    if (!files) return 1;
+
+    while ((file = readdir(files)))
+    {
+        if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
+        snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
+        if (relative_path)
+            snprintf(buf2, PATH_MAX, "%s/%s", relative_path, file->d_name);
+        else
+            strcpy(buf2, file->d_name);
+
+        if (ecore_file_is_dir(buf))
+            efreet_menu_directory_dir_scan(buf, buf2, cache);
+
+        else
+        {
+            ext = strrchr(buf, '.');
+            if (!ext || strcmp(ext, ".directory")) continue;
+
+            desktop = efreet_desktop_get(buf);
+            if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY) continue;
+
+            ecore_hash_set(cache, (void *)strdup(buf2), desktop);
+        }
+    }
+    closedir(files);
+
+    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 = ecore_hash_get(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(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b)
+{
+#if STRICT_SPEC
+    return strcmp(ecore_file_get_file(a->desktop->orig_path), ecore_file_get_file(b->desktop->orig_path));
+#else
+    return strcasecmp(a->desktop->name, b->desktop->name);
+#endif
+}
+
+static int
+efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
+{
+    return strcmp(internal->name.internal, name);
+}
+
+static int
+efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
+{
+    return strcmp(md->id, name);
+}
+
+static Efreet_Menu *
+efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
+{
+    Efreet_Menu *entry;
+    Ecore_List *layout = NULL;
+
+    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;
+    if (internal->file.name) entry->id = strdup(internal->file.name);
+    entry->name = ecore_string_instance(internal->name.name);
+    if (internal->directory) entry->icon = ecore_string_instance(internal->directory->icon);
+    entry->entries = ecore_list_new();
+    ecore_list_set_free_cb(entry->entries, ECORE_FREE_CB(efreet_menu_free));
+
+#if 1 //STRICT_SPEC
+    if (internal->sub_menus)
+    {
+        ecore_list_sort(internal->sub_menus,
+                        ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), ECORE_SORT_MIN);
+    }
+#endif
+
+    if (layout)
+    {
+        Efreet_Menu_Layout *lay;
+
+        ecore_list_goto_first(layout);
+        while ((lay = ecore_list_next(layout)))
+            efreet_menu_layout_entries_get(entry, internal, lay);
+    }
+    else
+    {
+        /* Default layout, first menus, then desktop */
+        if (internal->sub_menus)
+        {
+            Efreet_Menu_Internal *sub;
+
+            ecore_list_goto_first(internal->sub_menus);
+            while ((sub = ecore_list_next(internal->sub_menus)))
+            {
+                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) continue;
+                ecore_list_append(entry->entries, sub_entry);
+            }
+        }
+
+        if (internal->applications)
+        {
+            Efreet_Menu_Desktop *md;
+
+            ecore_list_goto_first(internal->applications);
+            while ((md = ecore_list_next(internal->applications)))
+            {
+                Efreet_Menu *sub_entry;
+                sub_entry = efreet_menu_layout_desktop(md);
+                ecore_list_append(entry->entries, sub_entry);
+            }
+        }
+    }
+
+    /* Don't keep this list around if it is empty */
+    if (ecore_list_is_empty(entry->entries)) IF_FREE_LIST(entry->entries);
+
+    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 = strdup(md->id);
+    entry->name = ecore_string_instance(md->desktop->name);
+    if (md->desktop->icon) entry->icon = ecore_string_instance(md->desktop->icon);
+    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 = ecore_list_find(internal->sub_menus,
+                ECORE_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 && !sub_entry->entries)
+                    efreet_menu_free(sub_entry);
+#if 0
+                else if (in_line &&
+                        ((inline_limit == 0) ||
+                         (!sub->entries || (ecore_list_nodes(sub->entries) <= inline_limit))))
+                {
+                    /* We don't delete the submenu when inlining, as we use information
+                     * from it. */
+                    /* Inline */
+                    if (!sub->entries)
+                    {
+                        /* Can't inline an empty submenu */
+                        entry = efreet_menu_entry_new();
+                        entry->type = EFREET_MENU_ENTRY_MENU;
+                        entry->name = sub->name.name;
+                        if (sub->directory) entry->icon = sub->directory->icon;
+                        entry->content.menu = sub;
+                        ecore_list_append(internal->entries, entry);
+                    }
+                    else if (inline_alias && (ecore_list_nodes(sub->entries) == 1))
+                    {
+                        entry = ecore_list_remove_first(sub->entries);
+                        entry->name = sub->name.name;
+                        if (sub->directory) entry->icon = sub->directory->icon;
+                        ecore_list_append(internal->entries, entry);
+                    }
+                    else
+                    {
+                        if (inline_header)
+                        {
+                            entry = efreet_menu_entry_new();
+                            entry->type = EFREET_MENU_ENTRY_HEADER;
+                            entry->name = sub->name.name;
+                            if (sub->directory) entry->icon = sub->directory->icon;
+                            ecore_list_append(internal->entries, entry);
+                        }
+                        ecore_list_goto_first(sub->entries);
+                        while ((entry = ecore_list_remove_first(sub->entries)))
+                            ecore_list_append(internal->entries, entry);
+                    }
+                }
+#endif
+                else
+                    ecore_list_append(entry->entries, sub_entry);
+            }
+            ecore_list_remove(internal->sub_menus);
+            efreet_menu_internal_free(sub);
+        }
+        if (ecore_list_is_empty(internal->sub_menus)) IF_FREE_LIST(internal->sub_menus);
+    }
+    else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
+    {
+        Efreet_Menu_Desktop *md;
+        md = ecore_list_find(internal->applications,
+                ECORE_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name);
+        if (md)
+        {
+            sub_entry = efreet_menu_layout_desktop(md);
+            ecore_list_append(entry->entries, sub_entry);
+            ecore_list_remove(internal->applications);
+        }
+        if (ecore_list_is_empty(internal->applications)) IF_FREE_LIST(internal->applications);
+    }
+    else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
+    {
+        if (internal->applications && !strcmp(layout->name, "files"))
+        {
+            Efreet_Menu_Desktop *md;
+
+            ecore_list_goto_first(internal->applications);
+            while ((md = ecore_list_remove_first(internal->applications)))
+            {
+                sub_entry = ecore_list_find(entry->entries,
+                        ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_desktop), md->desktop);
+                if (!sub_entry)
+                {
+                    sub_entry = efreet_menu_layout_desktop(md);
+                    ecore_list_append(entry->entries, sub_entry);
+                }
+            }
+            IF_FREE_LIST(internal->applications);
+        }
+        else if (internal->sub_menus && !strcmp(layout->name, "menus"))
+        {
+            Efreet_Menu_Internal *sub;
+
+            ecore_list_goto_first(internal->sub_menus);
+            while ((sub = ecore_list_remove_first(internal->sub_menus)))
+            {
+                if ((sub->directory && sub->directory->no_display) || sub->deleted)
+                {
+                    efreet_menu_internal_free(sub);
+                    continue;
+                }
+                sub_entry = ecore_list_find(entry->entries,
+                        ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_menu), sub);
+                if (!sub_entry)
+                {
+                    sub_entry = efreet_menu_layout_menu(sub);
+                    if (!internal->show_empty && !sub_entry->entries)
+                        efreet_menu_free(sub_entry);
+#if 0
+                    else if (internal->in_line &&
+                            ((internal->inline_limit == 0) ||
+                             (!sub->entries || (ecore_list_nodes(sub->entries) <= internal->inline_limit))))
+                    {
+                        /* We don't delete the submenu when inlining, as we
+                         * use information from it. */
+                        /* Inline */
+                        if (!sub->entries)
+                        {
+                            /* Can't inline an empty submenu */
+                            entry = efreet_menu_entry_new();
+                            entry->type = EFREET_MENU_ENTRY_MENU;
+                            entry->name = sub->name.name;
+                            if (sub->directory) entry->icon = sub->directory->icon;
+                            entry->content.menu = sub;
+                            ecore_list_append(internal->entries, entry);
+                        }
+                        else if (internal->inline_alias && (ecore_list_nodes(sub->entries) == 1))
+                        {
+                            entry = ecore_list_remove_first(sub->entries);
+                            entry->name = sub->name.name;
+                            if (sub->directory) entry->icon = sub->directory->icon;
+                            ecore_list_append(internal->entries, entry);
+                        }
+                        else
+                        {
+                            if (internal->inline_header)
+                            {
+                                entry = efreet_menu_entry_new();
+                                entry->type = EFREET_MENU_ENTRY_HEADER;
+                                entry->name = sub->name.name;
+                                if (sub->directory) entry->icon = sub->directory->icon;
+                                ecore_list_append(internal->entries, entry);
+                            }
+                            ecore_list_goto_first(sub->entries);
+                            while ((entry = ecore_list_remove_first(sub->entries)))
+                                ecore_list_append(internal->entries, entry);
+                        }
+                    }
+#endif
+                    else
+                        ecore_list_append(entry->entries, sub_entry);
+                }
+                efreet_menu_internal_free(sub);
+            }
+            IF_FREE_LIST(internal->sub_menus);
+        }
+        else if (internal->sub_menus && !strcmp(layout->name, "all"))
+        {
+            /* XXX: Add all menus and files, and sort them. */
+        }
+    }
+    else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
+    {
+        sub_entry = efreet_menu_entry_new();
+        sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
+        ecore_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;
+    return ecore_str_compare(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;
+    return ecore_str_compare(entry->name, desktop->name);
+}
+
+static int
+efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
+{
+    return ecore_str_compare(move->old_name, old);
+}
+
diff --git a/legacy/efreet/src/lib/efreet_menu.h b/legacy/efreet/src/lib/efreet_menu.h
new file mode 100644 (file)
index 0000000..5ee4dd8
--- /dev/null
@@ -0,0 +1,69 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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
+ */
+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
+ */
+typedef enum Efreet_Menu_Entry_Type 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;
+    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 */
+    Ecore_List     *entries;   /**< The menu items */
+};
+
+int              efreet_menu_kde_legacy_init(void);
+
+Efreet_Menu     *efreet_menu_get(void);
+Efreet_Menu     *efreet_menu_parse(const char *path);
+
+#if 0
+int              efreet_menu_save(Efreet_Menu *menu, const char *path);
+#endif
+void             efreet_menu_free(Efreet_Menu *menu);
+
+void             efreet_menu_dump(Efreet_Menu *menu, const char *indent);
+
+/**
+ * @}
+ */
+
+#endif
+
diff --git a/legacy/efreet/src/lib/efreet_private.h b/legacy/efreet/src/lib/efreet_private.h
new file mode 100644 (file)
index 0000000..4c9ac9d
--- /dev/null
@@ -0,0 +1,183 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#ifndef EFREET_PRIVATE_H
+#define EFREET_PRIVATE_H
+
+/**
+ * @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
+ *
+ * @{
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <Ecore.h>
+#include <Ecore_File.h>
+#include "Ecore_Str.h"
+
+#include "config.h"
+#include "efreet_xml.h"
+#include "efreet_ini.h"
+
+/**
+ * @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) { free(x); x = NULL; }
+
+/**
+ * @def IF_FREE(x) 
+ * If x is set, free x and set to NULL 
+ */
+#define IF_FREE(x) { if (x) FREE(x) }
+
+/**
+ * @def IF_RELEASE(x)
+ * If x is set, ecore_string_release x and set to NULL
+ */
+#define IF_RELEASE(x) { if (x) ecore_string_release(x); x = NULL; }
+
+/**
+ * @def IF_FREE_LIST(x) 
+ * If x is a valid pointer destroy x and set to NULL
+ */
+#define IF_FREE_LIST(x) { if (x) ecore_list_destroy(x); x = NULL; }
+
+/**
+ * @def IF_FREE_DLIST(x) 
+ * If x is a valid pointer destroy x and set to NULL
+ */
+#define IF_FREE_DLIST(x) { if (x) ecore_dlist_destroy(x); x = NULL; }
+
+/**
+ * @def IF_FREE_HASH(x) 
+ * If x is a valid pointer destroy x and set to NULL
+ */
+#define IF_FREE_HASH(x) { if (x) ecore_hash_destroy(x); x = NULL; }
+
+/**
+ * @def __UNUSED__
+ * A flag to mark a function parameter as unused
+ */
+#if HAVE___ATTRIBUTE__
+#define __UNUSED__ __attribute__((unused))
+#else
+#define __UNUSED__
+#endif
+
+#ifndef PATH_MAX
+/**
+ * @def PATH_MAX
+ * Convenience define to set the maximim path length
+ */
+#define PATH_MAX 4096
+#endif
+
+/**
+ * @internal
+ * The different types of commands in an Exec entry
+ */
+enum Efreet_Desktop_Command_Flag 
+{
+    EFREET_DESKTOP_EXEC_FLAG_FULLPATH = 0x0001,
+    EFREET_DESKTOP_EXEC_FLAG_URI      = 0x0002,
+    EFREET_DESKTOP_EXEC_FLAG_DIR      = 0x0004,
+    EFREET_DESKTOP_EXEC_FLAG_FILE     = 0x0008
+};
+
+/**
+ * @internal
+ * Efreet_Desktop_Command_Flag
+ */
+typedef enum Efreet_Desktop_Command_Flag 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;
+
+  Ecore_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;
+};
+
+int efreet_base_init(void);
+void efreet_base_shutdown(void);
+
+int efreet_icon_init(void);
+void efreet_icon_shutdown(void);
+
+int efreet_menu_init(void);
+void efreet_menu_shutdown(void);
+
+int efreet_ini_init(void);
+int efreet_ini_shutdown(void);
+
+int efreet_desktop_init(void);
+int efreet_desktop_shutdown(void);
+
+const char *efreet_home_dir_get(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[]);
+
+/**
+ * @}
+ */
+
+#endif
+
diff --git a/legacy/efreet/src/lib/efreet_xml.c b/legacy/efreet/src/lib/efreet_xml.c
new file mode 100644 (file)
index 0000000..c6cfb61
--- /dev/null
@@ -0,0 +1,579 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#include "Efreet.h"
+#include "efreet_private.h"
+
+static void efreet_xml_dump(Efreet_Xml *xml, int level);
+
+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, 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 init = 0;
+
+/**
+ * @internal
+ * @return Returns > 0 on success or 0 on failure
+ * @brief Initialize the XML parser subsystem
+ */
+int
+efreet_xml_init(void)
+{
+    if (init++) return init;
+    if (!ecore_string_init()) return --init;
+    return init;
+}
+
+/**
+ * @internal
+ * @returns the number of initializations left for this system
+ * @brief Attempts to shut down the subsystem if nothing else is using it
+ */
+int
+efreet_xml_shutdown(void)
+{
+    if (--init) return init;
+    ecore_string_shutdown();
+    return init;
+}
+
+/**
+ * @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 = (void *)-1;
+
+    if (!file) return NULL;
+
+    size = ecore_file_size(file);
+    if (size <= 0) goto ERROR;
+
+    fd = open(file, O_RDONLY);
+    if (fd == -1) goto ERROR;
+
+    data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+    if (data == (void *)-1) goto ERROR;
+
+    error = 0;
+    xml = efreet_xml_parse(&data, &size);
+    if (error) goto ERROR;
+
+    munmap(data, size);
+    close(fd);
+    return xml;
+
+ERROR:
+    fprintf(stderr, "[efreet]: could not parse xml file\n");
+    if (data != (void *)-1) 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 (xml->children) ecore_dlist_destroy(xml->children);
+    xml->children = NULL;
+
+    if (xml->tag) ecore_string_release(xml->tag);
+    if (xml->attributes)
+    {
+        Efreet_Xml_Attribute **curr;
+
+        curr = xml->attributes;
+        while (*curr)
+        {
+            ecore_string_release((*curr)->key);
+            ecore_string_release((*curr)->value);
+
+            FREE(*curr);
+            curr++;
+        }
+        FREE(xml->attributes);
+    }
+    IF_FREE(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);
+}
+
+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;
+
+        printf(">\n");
+
+        ecore_dlist_goto_first(xml->children);
+        while ((child = ecore_dlist_next(xml->children)))
+            efreet_xml_dump(child, level + 1);
+
+        for (i = 0; i < level; i++) 
+            printf("\t");
+        printf("</%s>\n", xml->tag);
+    }
+    else if (xml->text)
+        printf(">%s</%s>\n", xml->text, xml->tag);
+
+    else
+        printf("/>\n");
+}
+
+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)
+    {
+        ecore_string_release(tag);
+        return NULL;
+    }
+
+    xml->children = ecore_dlist_new();
+    ecore_dlist_set_free_cb(xml->children, efreet_xml_cb_attribute_free);
+
+    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)))
+        ecore_dlist_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)
+    {
+        fprintf(stderr, "[efreet]: missing start tag\n");
+        error = 1;
+        return 0;
+    }
+
+    while (*size > 0)
+    {
+        if (!isalpha(**data))
+        {
+            end = *data;
+            break;
+        }
+        (*size)--;
+        (*data)++;
+    }
+
+    if (!end)
+    {
+        fprintf(stderr, "[efreet]: no end of tag\n");
+        error = 1;
+        return 0;
+    }
+
+    buf_size = end - start + 1;
+    if (buf_size <= 1)
+    {
+        fprintf(stderr, "[efreet]: no tag name\n");
+        error = 1;
+        return 0;
+    }
+
+    if (buf_size > 256) buf_size = 256;
+    memcpy(buf, start, buf_size - 1);
+    buf[buf_size - 1] = 0;
+    *tag = ecore_string_instance(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)
+            {
+                fprintf(stderr, "[efreet]: zero length key\n");
+                goto ERROR;
+            }
+
+            if (buf_size > 256) buf_size = 256;
+            memcpy(buf, start, buf_size - 1);
+            buf[buf_size - 1] = 0;
+            attr[count].key = ecore_string_instance(buf);
+
+            /* search for '=', key/value seperator */
+            start = NULL;
+            while (*size > 0)
+            {
+                if (**data == '=')
+                {
+                    start = *data;
+                    break;
+                }
+                (*size)--;
+                (*data)++;
+            }
+
+            if (!start)
+            {
+                fprintf(stderr, "[efreet]: missing value for attribute!\n");
+                goto ERROR;
+            }
+
+            /* search for '"', beginning of value */
+            start = NULL;
+            while (*size > 0)
+            {
+                if (**data == '"')
+                {
+                    start = *data;
+                    break;
+                }
+                (*size)--;
+                (*data)++;
+            }
+
+            if (!start)
+            {
+                fprintf(stderr, "[efreet]: erroneous value for attribute!\n");
+                goto ERROR;
+            }
+
+            /* skip '"' */
+            start++;
+            (*size)--;
+            (*data)++;
+
+            /* search for '"', end of value */
+            end = NULL;
+            while (*size > 0)
+            {
+                if (**data == '"')
+                {
+                    end = *data;
+                    break;
+                }
+                (*size)--;
+                (*data)++;
+            }
+
+            if (!end)
+            {
+                fprintf(stderr, "[efreet]: erroneous value for attribute!\n");
+                goto ERROR;
+            }
+
+            buf_size = end - start + 1;
+            if (buf_size <= 1)
+            {
+                fprintf(stderr, "[efreet]: zero length value\n");
+                goto ERROR;
+            }
+
+            if (buf_size > 256) buf_size = 256;
+            memcpy(buf, start, buf_size - 1);
+            buf[buf_size - 1] = 0;
+            attr[count].value = ecore_string_instance(buf);
+
+            count++;
+        }
+
+        (*size)--;
+        (*data)++;
+    }
+
+    *attributes = NEW(Efreet_Xml_Attribute *, count + 1);
+    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;
+
+ERROR:
+    while (count >= 0)
+    {
+        if (attr[count].key) ecore_string_release(attr[count].key);
+        if (attr[count].value) ecore_string_release(attr[count].value);
+        count--;
+    }
+    error = 1;
+    return;
+}
+
+static void
+efreet_xml_text_parse(char **data, int *size, 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 = malloc(buf_size);
+    memcpy(*text, start, buf_size - 1);
+    (*text)[buf_size - 1] = 0;
+}
+
+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)++;
+    }
+    fprintf(stderr, "[efreet]: missing end of tag\n");
+    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)
+                {
+                    fprintf(stderr, "[efreet]: wrong end tag\n");
+                    error = 1;
+                    return 1;
+                }
+                else
+                {
+                    char *tmp;
+                    tmp = *data;
+                    while ((*tag) && (*tmp == *tag))
+                    {
+                        tmp++;
+                        tag++;
+                    }
+
+                    if (*tag)
+                    {
+                        fprintf(stderr, "[efreet]: wrong end tag\n");
+                        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/legacy/efreet/src/lib/efreet_xml.h b/legacy/efreet/src/lib/efreet_xml.h
new file mode 100644 (file)
index 0000000..47cb8ec
--- /dev/null
@@ -0,0 +1,61 @@
+/* vim: set sw=4 ts=4 sts=4 et: */
+#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
+{
+    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 */
+
+    Ecore_DList *children;               /**< Child nodes */
+};
+
+int efreet_xml_init(void);
+int 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
+