]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added tests/suite which contains tests to be executed during
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 16 Oct 2010 13:49:30 +0000 (15:49 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 16 Oct 2010 13:52:29 +0000 (15:52 +0200)
development time and will not be distributed (not included in make dist).
Added "ecore" and a new mini-eagain to test EAGAIN behavior.

126 files changed:
tests/Makefile.am
tests/safe-renegotiation/Makefile.am
tests/suite/Makefile.am [new file with mode: 0644]
tests/suite/README [new file with mode: 0644]
tests/suite/eagain [new file with mode: 0755]
tests/suite/ecore/eina_config.h [new file with mode: 0644]
tests/suite/ecore/src/include/Eina.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_accessor.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_array.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_benchmark.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_binshare.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_config.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_convert.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_counter.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_cpu.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_error.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_file.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_fp.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_hamster.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_hash.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_array.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_f16p16.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_f32p32.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_f8p24.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_fp.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_hash.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_list.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_log.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_mempool.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_rbtree.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_rectangle.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_str.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_stringshare.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_tiler.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_trash.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inline_ustringshare.x [new file with mode: 0644]
tests/suite/ecore/src/include/eina_inlist.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_iterator.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_lalloc.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_list.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_log.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_magic.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_main.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_matrixsparse.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_mempool.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_module.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_quadtree.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_rbtree.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_rectangle.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_safety_checks.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_sched.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_str.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_strbuf.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_stringshare.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_tiler.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_trash.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_types.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_unicode.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_ustrbuf.h [new file with mode: 0644]
tests/suite/ecore/src/include/eina_ustringshare.h [new file with mode: 0644]
tests/suite/ecore/src/lib/Ecore.h [new file with mode: 0644]
tests/suite/ecore/src/lib/Ecore_Getopt.h [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_anim.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_app.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_events.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_exe.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_getopt.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_glib.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_idle_enterer.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_idle_exiter.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_idler.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_job.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_main.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_pipe.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_poll.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_private.h [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_signal.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_thread.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_time.c [new file with mode: 0644]
tests/suite/ecore/src/lib/ecore_timer.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_accessor.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_array.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_benchmark.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_binshare.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_chained_mempool.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_convert.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_counter.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_cpu.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_error.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_file.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_fp.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_hamster.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_hash.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_inlist.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_iterator.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_lalloc.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_list.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_log.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_magic.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_main.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_matrixsparse.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_mempool.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_module.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_private.h [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_quadtree.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_rbtree.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_rectangle.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_safety_checks.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_sched.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_share_common.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_share_common.h [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_str.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_strbuf.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_strbuf_common.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_strbuf_common.h [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_strbuf_template_c.x [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_stringshare.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_tiler.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_unicode.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_ustrbuf.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_ustringshare.c [new file with mode: 0644]
tests/suite/ecore/src/lib/eina_value.c [new file with mode: 0644]
tests/suite/mini-eagain2.c [new file with mode: 0644]
tests/suite/params.dh [moved from tests/safe-renegotiation/params.dh with 100% similarity]
tests/suite/testsrn [moved from tests/safe-renegotiation/testsrn with 100% similarity]

index 4a42a098d64938adcc264a8acf632a0cb872174e..15d03a44275779b2d6893ee7e89e0dc3d0db9222 100644 (file)
@@ -96,3 +96,6 @@ TESTS_ENVIRONMENT =                                           \
        EXEEXT=$(EXEEXT)                                        \
        srcdir="$(srcdir)"                                      \
        $(VALGRIND)
+
+check-local:
+       [ -d suite ] && $(MAKE) -C suite check
index e175c78c37c31823b696953c843b4d5da9d54584..a76e437295fa6bcfa13928b5dafee4b3d60d346c 100644 (file)
@@ -31,7 +31,5 @@ check_PROGRAMS = $(ctests)
 TESTS = $(ctests)
 TESTS_ENVIRONMENT = $(VALGRIND)
 
-EXTRA_DIST = README params.dh
+EXTRA_DIST = README
 
-dist_check_SCRIPTS = testsrn
-#TESTS = testsrn
diff --git a/tests/suite/Makefile.am b/tests/suite/Makefile.am
new file mode 100644 (file)
index 0000000..184acbe
--- /dev/null
@@ -0,0 +1,79 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+# Foundation, Inc.
+#
+# Author: Simon Josefsson
+#
+# This file is part of GnuTLS.
+#
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this file; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+EXTRA_DIST = params.dh
+
+AM_CFLAGS = $(WARN_CFLAGS)
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/gl                      \
+       -I$(top_builddir)/gl                    \
+       -I$(top_srcdir)/lib/includes            \
+       -I$(top_builddir)/lib/includes          \
+       -I$(top_srcdir)/libextra/includes       \
+       -I$(top_builddir)/libextra/includes     \
+       -I$(top_srcdir)/lib                     \
+       -I$(top_srcdir)/doc/examples    \
+       -I$(top_srcdir)/tests/suite/ecore/src/include \
+       -I$(top_srcdir)/tests/suite/ecore/src/lib
+
+AM_LDFLAGS = -no-install
+LDADD = ../../lib/libgnutls.la \
+       ../../gl/libgnu.la \
+       ../libutils.la \
+       $(LTLIBGCRYPT)  \
+       $(LIBSOCKET) $(INET_NTOP_LIB) $(INET_PTON_LIB)
+
+noinst_LTLIBRARIES = libecore.la                                                                                                                                                                                                                                  
+
+libecore_la_CPPFLAGS = -I$(top_srcdir)/tests/suite/ecore/ -I$(top_srcdir)/tests/suite/ecore/src/include \
+       -I$(top_srcdir)/tests/suite/ecore/src/lib -DHAVE_CONFIG_H -D__UNUSED__= -DVMAJ=1 \
+       -D VMIC=0 -DVMIN=0 -DVREV=0 -DEFL_HAVE_POSIX_THREADS=1 -DEFL_HAVE_POSIX_THREADS_SPINLOCK=1 \
+       -DEFL_HAVE_THREADS=1 -DEINA_BUILD_CHAINED_POOL=1 -DEINA_STATIC_BUILD_CHAINED_POOL=1 \
+       -DHAVE_CLOCK_GETTIME -DHAVE_GETTIMEOFDAY -DPACKAGE_LIB_DIR=\"/usr/lib\" -DMODULE_ARCH=\"unix\" \
+       -DSHARED_LIB_SUFFIX=\".so\"
+libecore_la_CFLAGS = -w
+libecore_la_SOURCES = ecore/src/lib/ecore_anim.c ecore/src/lib/ecore_app.c \
+       ecore/src/lib/ecore.c ecore/src/lib/ecore_events.c ecore/src/lib/ecore_exe.c \
+       ecore/src/lib/ecore_getopt.c ecore/src/lib/ecore_glib.c ecore/src/lib/ecore_idle_enterer.c \
+       ecore/src/lib/ecore_idle_exiter.c ecore/src/lib/ecore_idler.c ecore/src/lib/ecore_job.c \
+       ecore/src/lib/ecore_main.c ecore/src/lib/ecore_pipe.c ecore/src/lib/ecore_poll.c \
+       ecore/src/lib/ecore_signal.c ecore/src/lib/ecore_thread.c ecore/src/lib/ecore_time.c \
+       ecore/src/lib/ecore_timer.c ecore/src/lib/eina_accessor.c ecore/src/lib/eina_array.c \
+       ecore/src/lib/eina_benchmark.c ecore/src/lib/eina_binshare.c ecore/src/lib/eina_chained_mempool.c \
+       ecore/src/lib/eina_convert.c ecore/src/lib/eina_counter.c ecore/src/lib/eina_cpu.c ecore/src/lib/eina_error.c \
+       ecore/src/lib/eina_file.c ecore/src/lib/eina_fp.c ecore/src/lib/eina_hamster.c ecore/src/lib/eina_hash.c \
+       ecore/src/lib/eina_inlist.c ecore/src/lib/eina_iterator.c ecore/src/lib/eina_lalloc.c ecore/src/lib/eina_list.c \
+       ecore/src/lib/eina_log.c ecore/src/lib/eina_magic.c ecore/src/lib/eina_main.c ecore/src/lib/eina_matrixsparse.c \
+       ecore/src/lib/eina_mempool.c ecore/src/lib/eina_module.c ecore/src/lib/eina_quadtree.c ecore/src/lib/eina_rbtree.c \
+       ecore/src/lib/eina_rectangle.c ecore/src/lib/eina_safety_checks.c ecore/src/lib/eina_sched.c ecore/src/lib/eina_share_common.c \
+       ecore/src/lib/eina_strbuf.c ecore/src/lib/eina_strbuf_common.c ecore/src/lib/eina_str.c \
+       ecore/src/lib/eina_stringshare.c ecore/src/lib/eina_tiler.c ecore/src/lib/eina_unicode.c \
+       ecore/src/lib/eina_ustrbuf.c ecore/src/lib/eina_ustringshare.c ecore/src/lib/eina_value.c
+
+eagain_cli_LDADD = $(LDADD) libecore.la -lrt -lm
+eagain_cli_SOURCES = mini-eagain2.c
+
+noinst_PROGRAMS = eagain-cli
+
+dist_check_SCRIPTS = eagain #testsrn
+
+TESTS = eagain #testsrn
diff --git a/tests/suite/README b/tests/suite/README
new file mode 100644 (file)
index 0000000..ece7004
--- /dev/null
@@ -0,0 +1,2 @@
+Here are tests that will not be distributed with a release. Only
+done during development.
diff --git a/tests/suite/eagain b/tests/suite/eagain
new file mode 100755 (executable)
index 0000000..0acf21b
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# Copyright (C) 2006, 2007, 2008, 2010 Free Software Foundation, Inc.
+#
+# Author: Simon Josefsson
+#
+# This file is part of GnuTLS.
+#
+# GnuTLS is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GnuTLS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+srcdir="${srcdir:-.}"
+SERV="${SERV:-../../src/gnutls-serv$EXEEXT} -q"
+PORT="${PORT:-4445}"
+
+
+$SERV -p $PORT --echo --priority "NORMAL:+ANON-DH" --dhparams $srcdir/params.dh >/dev/null 2>&1 &
+pid=$!
+
+sleep 2
+
+./eagain-cli
+if [ $? != 0 ];then
+       exit 1
+fi
+
+if [ "$pid" != "" ];then
+kill $pid
+wait
+fi
+
+exit 0
diff --git a/tests/suite/ecore/eina_config.h b/tests/suite/ecore/eina_config.h
new file mode 100644 (file)
index 0000000..f89327d
--- /dev/null
@@ -0,0 +1,52 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_CONFIG_H_
+#define EINA_CONFIG_H_
+
+#ifdef EINA_MAGIC_DEBUG
+# undef EINA_MAGIC_DEBUG
+#endif
+#define EINA_MAGIC_DEBUG
+
+#ifdef EINA_DEFAULT_MEMPOOL
+# undef EINA_DEFAULT_MEMPOOL
+#endif
+
+
+#ifdef EINA_SAFETY_CHECKS
+# undef EINA_SAFETY_CHECKS
+#endif
+#define EINA_SAFETY_CHECKS
+
+#ifdef EINA_HAVE_INTTYPES_H
+# undef EINA_HAVE_INTTYPES_H
+#endif
+#define EINA_HAVE_INTTYPES_H
+
+#ifdef EINA_HAVE_STDINT_H
+# undef EINA_HAVE_STDINT_H
+#endif
+#define EINA_HAVE_STDINT_H
+
+#ifdef EINA_SIZEOF_WCHAR_T
+# undef EINA_SIZEOF_WCHAR_T
+#endif
+#define EINA_SIZEOF_WCHAR_T 4
+
+#endif /* EINA_CONFIG_H_ */
diff --git a/tests/suite/ecore/src/include/Eina.h b/tests/suite/ecore/src/include/Eina.h
new file mode 100644 (file)
index 0000000..eb0263f
--- /dev/null
@@ -0,0 +1,165 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008-2010 Enlightenment Developers:
+ *           Albin "Lutin" Tonnerre <albin.tonnerre@gmail.com>
+ *           Alexandre "diaxen" Becoulet <diaxen@free.fr>
+ *           Andre Dieb <andre.dieb@gmail.com>
+ *           Arnaud de Turckheim "quarium" <quarium@gmail.com>
+ *           Carsten Haitzler <raster@rasterman.com>
+ *           Cedric Bail <cedric.bail@free.fr>
+ *           Corey "atmos" Donohoe <atmos@atmos.org>
+ *           Fabiano Fidêncio <fidencio@profusion.mobi>
+ *           Gustavo Chaves <glima@profusion.mobi>
+ *           Gustavo Sverzut Barbieri <barbieri@gmail.com>
+ *           Jorge Luis "turran" Zapata <jorgeluis.zapata@gmail.com>
+ *           Peter "pfritz" Wehrfritz <peter.wehrfritz@web.de>
+ *           Raphael Kubo da Costa <kubo@profusion.mobi>
+ *           Tilman Sauerbeck <tilman@code-monkey.de>
+ *           Vincent "caro" Torri  <vtorri at univ-evry dot fr>
+ *           Tom Hacohen <tom@stosb.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_H_
+#define EINA_H_
+
+/**
+ * @mainpage Eina
+ *
+ * @author Albin "Lutin" Tonnerre <albin.tonnerre@@gmail.com>
+ * @author Alexandre "diaxen" Becoulet <diaxen@@free.fr>
+ * @author Andre Dieb <andre.dieb@@gmail.com>
+ * @author Arnaud de Turckheim "quarium" <quarium@@gmail.com>
+ * @author Carsten Haitzler <raster@@rasterman.com>
+ * @author Cedric Bail <cedric.bail@@free.fr>
+ * @author Corey "atmos" Donohoe <atmos@@atmos.org>
+ * @author Fabiano Fidêncio <fidencio@@profusion.mobi>
+ * @author Gustavo Chaves <glima@@profusion.mobi>
+ * @author Gustavo Sverzut Barbieri <barbieri@@profusion.mobi>
+ * @author Jorge Luis "turran" Zapata <jorgeluis.zapata@@gmail.com>
+ * @author Peter "pfritz" Wehrfritz <peter.wehrfritz@@web.de>
+ * @author Raphael Kubo da Costa <kubo@@profusion.mobi>
+ * @author Tilman Sauerbeck <tilman@@code-monkey.de>
+ * @author Vincent "caro" Torri  <vtorri at univ-evry dot fr>
+ * @author Tom Hacohen <tom@@stosb.com>
+ * @date 2008-2010
+ *
+ * @section eina_intro_sec Introduction
+ *
+ * The Eina library is a library that implements an API for data types
+ * in an efficient way. It also provides some useful tools like
+ * openin shared libraries, errors management, type conversion,
+ * time accounting and memory pool.
+ *
+ * This library is cross-platform and can be compiled and used on
+ * Linux, BSD, Opensolaris and Windows (XP and CE).
+ *
+ * The data types that are available are (see @ref Eina_Data_Types_Group):
+ * @li @ref Eina_Array_Group standard array of @c void* data.
+ * @li @ref Eina_Hash_Group standard hash of @c void* data.
+ * @li @ref Eina_Inline_List_Group list with nodes inlined into user type.
+ * @li @ref Eina_List_Group standard list of @c void* data.
+ * @li @ref Eina_Matrixsparse_Group sparse matrix of @c void* data.
+ * @li @ref Eina_Rbtree_Group red-black tree with nodes inlined into user type.
+ * @li @ref Eina_String_Buffer_Group mutable string to prepend, insert or append strings to a buffer.
+ * @li @ref Eina_Stringshare_Group saves memory by sharing read-only string references.
+ * @li @ref Eina_Tiler_Group split, merge and navigates into 2D tiled regions.
+ * @li @ref Eina_Trash_Group container of unused but allocated data.
+ *
+ * The tools that are available are (see @ref Eina_Tools_Group):
+ * @li @ref Eina_Benchmark_Group helper to write benchmarks.
+ * @li @ref Eina_Convert_Group faster conversion from strings to integers, double, etc.
+ * @li @ref Eina_Counter_Group measures number of calls and their time.
+ * @li @ref Eina_Error_Group error identifiers.
+ * @li @ref Eina_File_Group simple file list and path split.
+ * @li @ref Eina_Lalloc_Group simple lazy allocator.
+ * @li @ref Eina_Log_Group full-featured logging system.
+ * @li @ref Eina_Magic_Group provides runtime type checking.
+ * @li @ref Eina_Memory_Pool_Group abstraction for various memory allocators.
+ * @li @ref Eina_Module_Group lists, loads and share modules using Eina_Module standard.
+ * @li @ref Eina_Rectangle_Group rectangle structure and standard manipulation methods.
+ * @li @ref Eina_Safety_Checks_Group extra checks that will report unexpected conditions and can be disabled at compile time.
+ * @li @ref Eina_String_Group a set of functions that manages C strings.
+ *
+ * @defgroup Eina_Data_Types_Group Data types.
+ *
+ * Eina provide easy to use and optimized data types and structures.
+ *
+ *
+ * @defgroup Eina_Containers_Group Containers
+ *
+ * Containers are data types that hold data and allow iteration over
+ * their elements with an @ref Eina_Iterator_Group, or eventually an
+ * @ref Eina_Accessor_Group.
+ *
+ *
+ * @defgroup Eina_Tools_Group Tools
+ *
+ * Eina tools aims to help application development, providing ways to
+ * make it safer, log errors, manage memory more efficiently and more.
+ */
+
+#include <dirent.h>
+
+#ifdef _WIN32
+# include <Evil.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "eina_config.h"
+#include "eina_types.h"
+#include "eina_main.h"
+#include "eina_fp.h"
+#include "eina_rectangle.h"
+#include "eina_inlist.h"
+#include "eina_file.h"
+#include "eina_list.h"
+#include "eina_hash.h"
+#include "eina_trash.h"
+#include "eina_lalloc.h"
+#include "eina_module.h"
+#include "eina_mempool.h"
+#include "eina_error.h"
+#include "eina_log.h"
+#include "eina_array.h"
+#include "eina_binshare.h"
+#include "eina_stringshare.h"
+#include "eina_ustringshare.h"
+#include "eina_magic.h"
+#include "eina_counter.h"
+#include "eina_rbtree.h"
+#include "eina_accessor.h"
+#include "eina_iterator.h"
+#include "eina_benchmark.h"
+#include "eina_convert.h"
+#include "eina_cpu.h"
+#include "eina_sched.h"
+#include "eina_tiler.h"
+#include "eina_hamster.h"
+#include "eina_matrixsparse.h"
+#include "eina_str.h"
+#include "eina_strbuf.h"
+#include "eina_ustrbuf.h"
+#include "eina_unicode.h"
+#include "eina_quadtree.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EINA_H */
diff --git a/tests/suite/ecore/src/include/eina_accessor.h b/tests/suite/ecore/src/include/eina_accessor.h
new file mode 100644 (file)
index 0000000..4d2f92e
--- /dev/null
@@ -0,0 +1,148 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_ACCESSOR_H__
+#define EINA_ACCESSOR_H__
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_magic.h"
+
+/**
+ * @addtogroup Eina_Content_Access_Group Content Access
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Accessor_Group Accessor Functions
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Accessor
+ * Type for accessors.
+ */
+typedef struct _Eina_Accessor Eina_Accessor;
+
+typedef Eina_Bool (*Eina_Accessor_Get_At_Callback)(Eina_Accessor *it,
+                                                   unsigned int index,
+                                                   void **data);
+typedef void *(*Eina_Accessor_Get_Container_Callback)(Eina_Accessor *it);
+typedef void (*Eina_Accessor_Free_Callback)(Eina_Accessor *it);
+typedef Eina_Bool (*Eina_Accessor_Lock_Callback)(Eina_Accessor *it);
+
+struct _Eina_Accessor
+{
+#define EINA_ACCESSOR_VERSION 1
+   int version;
+
+   Eina_Accessor_Get_At_Callback get_at               EINA_ARG_NONNULL(1, 3) EINA_WARN_UNUSED_RESULT;
+   Eina_Accessor_Get_Container_Callback get_container EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+   Eina_Accessor_Free_Callback free                   EINA_ARG_NONNULL(1);
+
+   Eina_Accessor_Lock_Callback lock   EINA_WARN_UNUSED_RESULT;
+   Eina_Accessor_Lock_Callback unlock EINA_WARN_UNUSED_RESULT;
+
+#define EINA_MAGIC_ACCESSOR 0x98761232
+   EINA_MAGIC
+};
+
+#define FUNC_ACCESSOR_GET_AT(Function) ((Eina_Accessor_Get_At_Callback)Function)
+#define FUNC_ACCESSOR_GET_CONTAINER(Function) ((Eina_Accessor_Get_Container_Callback)Function)
+#define FUNC_ACCESSOR_FREE(Function) ((Eina_Accessor_Free_Callback)Function)
+#define FUNC_ACCESSOR_LOCK(Function) ((Eina_Accessor_Lock_Callback)Function)
+
+EAPI void      eina_accessor_free(Eina_Accessor *accessor) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool eina_accessor_data_get(Eina_Accessor *accessor,
+                                      unsigned int position,
+                                      void **data) EINA_ARG_NONNULL(1);
+EAPI void *    eina_accessor_container_get(Eina_Accessor *accessor) EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI void      eina_accessor_over(Eina_Accessor *accessor,
+                                  Eina_Each_Cb cb,
+                                  unsigned int start,
+                                  unsigned int end,
+                                  const void *fdata) EINA_ARG_NONNULL(1, 2);
+EAPI Eina_Bool eina_accessor_lock(Eina_Accessor *accessor) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool eina_accessor_unlock(Eina_Accessor *accessor) EINA_ARG_NONNULL(1);
+
+/**
+ * @def EINA_ACCESSOR_FOREACH
+ * @brief Macro to iterate over all elements easily.
+ *
+ * @param accessor The accessor to use.
+ * @param counter A counter used by eina_accessor_data_get() when
+ * iterating over the container.
+ * @param data Where to store * data, must be a pointer support getting
+ * its address since * eina_accessor_data_get() requires a pointer to
+ * pointer!
+ *
+ * This macro allows a convenient way to loop over all elements in an
+ * accessor, very similar to EINA_LIST_FOREACH().
+ *
+ * This macro can be used for freeing the data of a list, like in the
+ * following example. It has the same goal as the one documented in
+ * EINA_LIST_FOREACH(), but using accessors:
+ *
+ * @code
+ * Eina_List     *list;
+ * Eina_Accessor *accessor;
+ * unsigned int   i;
+ * char          *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings
+ *
+ * accessor = eina_list_accessor_new(list);
+ * EINA_ACCESSOR_FOREACH(accessor, i, data)
+ *   free(data);
+ * eina_accessor_free(accessor);
+ * eina_list_free(list);
+ * @endcode
+ *
+ * @note if the datatype provides both iterators and accessors prefer
+ *    to use iterators to iterate over, as they're likely to be more
+ *    optimized for such task.
+ *
+ * @note this example is not optimal algorithm to release a list since
+ *    it will walk the list twice, but it serves as an example. For
+ *    optimized version use EINA_LIST_FREE()
+ *
+ * @warning unless explicitly stated in functions returning accessors,
+ *    do not modify the accessed object while you walk it, in this
+ *    example using lists, do not remove list nodes or you might
+ *    crash!  This is not a limitiation of accessors themselves,
+ *    rather in the accessors implementations to keep them as simple
+ *    and fast as possible.
+ */
+#define EINA_ACCESSOR_FOREACH(accessor, counter, data)                 \
+  for ((counter) = 0;                                                  \
+       eina_accessor_data_get((accessor), (counter), (void **)&(data)); \
+       (counter)++)
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_array.h b/tests/suite/ecore/src/include/eina_array.h
new file mode 100644 (file)
index 0000000..cb0d12a
--- /dev/null
@@ -0,0 +1,153 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_ARRAY_H_
+#define EINA_ARRAY_H_
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_error.h"
+#include "eina_iterator.h"
+#include "eina_accessor.h"
+#include "eina_magic.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Array_Group Array
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Array
+ * Type for a generic vector.
+ */
+typedef struct _Eina_Array Eina_Array;
+
+/**
+ * @typedef Eina_Array_Iterator
+ * Type for an iterator on arrays, used with #EINA_ARRAY_ITER_NEXT.
+ */
+typedef void **Eina_Array_Iterator;
+
+/**
+ * @struct _Eina_Array
+ * Type for an array of data.
+ */
+struct _Eina_Array
+{
+#define EINA_ARRAY_VERSION 1
+   int version; /**< Should match EINA_ARRAY_VERSION used when compiled your apps, provided for ABI compatibility */
+
+   void **data; /**< Pointer to a vector of pointer to payload */
+   unsigned int total; /**< Total number of slots in the vector */
+   unsigned int count; /**< Number of active slots in the vector */
+   unsigned int step; /**< How much must we grow the vector when it is full */
+   EINA_MAGIC
+};
+
+EAPI Eina_Array *               eina_array_new(unsigned int step) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI void                       eina_array_free(Eina_Array *array) EINA_ARG_NONNULL(1);
+EAPI void                       eina_array_step_set(Eina_Array *array,
+                                                    unsigned int sizeof_eina_array,
+                                                    unsigned int step) EINA_ARG_NONNULL(1);
+EAPI void                       eina_array_clean(Eina_Array *array) EINA_ARG_NONNULL(1);
+EAPI void                       eina_array_flush(Eina_Array *array) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool                  eina_array_remove(Eina_Array *array,
+                                                  Eina_Bool(*keep)(void *data, void *gdata),
+                                                  void *gdata) EINA_ARG_NONNULL(1, 2);
+static inline Eina_Bool         eina_array_push(Eina_Array *array,
+                                                const void *data) EINA_ARG_NONNULL(1, 2);
+static inline void *            eina_array_pop(Eina_Array *array) EINA_ARG_NONNULL(1);
+static inline void *            eina_array_data_get(const Eina_Array *array,
+                                                    unsigned int idx) EINA_ARG_NONNULL(1);
+static inline void              eina_array_data_set(const Eina_Array *array,
+                                                    unsigned int idx,
+                                                    const void *data) EINA_ARG_NONNULL(1, 3);
+static inline unsigned int      eina_array_count_get(const Eina_Array *array) EINA_ARG_NONNULL(1);
+EAPI Eina_Iterator *            eina_array_iterator_new(const Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Accessor *            eina_array_accessor_new(const Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline Eina_Bool                eina_array_foreach(Eina_Array *array,
+                                                   Eina_Each_Cb cb,
+                                                   void *data);
+/**
+ * @def EINA_ARRAY_ITER_NEXT
+ * @brief Macro to iterate over an array easily.
+ *
+ * @param array The array to iterate over.
+ * @param index The integer number that is increased while itareting.
+ * @param item The data
+ * @param iterator The iterator
+ *
+ * This macro allows the iteration over @p array in an easy way. It
+ * iterates from the first element to the last one. @p index is an
+ * integer that increases from 0 to the number of elements. @p item is
+ * the data of each element of @p array, so it is a pointer to a type
+ * chosen by the user. @p iterator is of type #Eina_Array_Iterator.
+ *
+ * This macro can be used for freeing the data of an array, like in
+ * the following example:
+ *
+ * @code
+ * Eina_Array         *array;
+ * char               *item;
+ * Eina_Array_Iterator iterator;
+ * unsigned int        i;
+ *
+ * // array is already filled,
+ * // its elements are just duplicated strings,
+ * // EINA_ARRAY_ITER_NEXT will be used to free those strings
+ *
+ * EINA_ARRAY_ITER_NEXT(array, i, item, iterator)
+ *   free(item);
+ * @endcode
+ */
+#define EINA_ARRAY_ITER_NEXT(array, index, item, iterator)             \
+  for (index = 0, iterator = (array)->data;                            \
+       (index < eina_array_count_get(array)) && ((item = *((iterator)++))); \
+       ++(index))
+
+#include "eina_inline_array.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_benchmark.h b/tests/suite/ecore/src/include/eina_benchmark.h
new file mode 100644 (file)
index 0000000..e0faa72
--- /dev/null
@@ -0,0 +1,77 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_BENCHMARK_H_
+#define EINA_BENCHMARK_H_
+
+#include "eina_array.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Benchmark_Group Benchmark
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Benchmark
+ * Type for a benchmark.
+ */
+typedef struct _Eina_Benchmark Eina_Benchmark;
+
+/**
+ * @typedef Eina_Benchmark_Specimens
+ * Type for a test function to be called when running a benchmark.
+ */
+typedef void (*Eina_Benchmark_Specimens)(int request);
+
+/**
+ * @def EINA_BENCHMARK
+ * @brief cast to an #Eina_Benchmark_Specimens.
+ *
+ * @param function The function to cast.
+ *
+ * This macro casts @p function to Eina_Benchmark_Specimens.
+ */
+#define EINA_BENCHMARK(function) ((Eina_Benchmark_Specimens)function)
+
+EAPI Eina_Benchmark * eina_benchmark_new(const char *name,
+                                         const char *run);
+EAPI void             eina_benchmark_free(Eina_Benchmark *bench);
+EAPI Eina_Bool        eina_benchmark_register(Eina_Benchmark *bench,
+                                              const char *name,
+                                              Eina_Benchmark_Specimens bench_cb,
+                                              int count_start,
+                                              int count_end,
+                                              int count_set);
+EAPI Eina_Array *     eina_benchmark_run(Eina_Benchmark *bench);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_BENCHMARK_H_ */
diff --git a/tests/suite/ecore/src/include/eina_binshare.h b/tests/suite/ecore/src/include/eina_binshare.h
new file mode 100644 (file)
index 0000000..e785ed0
--- /dev/null
@@ -0,0 +1,105 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2008 Peter Wehrfritz
+ *
+ *  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 and its Copyright notices. In addition publicly
+ *  documented acknowledgment must be given that this software has been used if no
+ *  source code of this software is made available publicly. This includes
+ *  acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+ *  documents or any documentation provided with any product containing this
+ *  software. This License does not apply to any software that links to the
+ *  libraries provided by this software (statically or dynamically), but only to
+ *  the software provided.
+ *
+ *  Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice
+ *  and it's intent.
+ *
+ *  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.
+ */
+
+#ifndef EINA_BINSHARE_H_
+#define EINA_BINSHARE_H_
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Binshare_Group Binary Share
+ *
+ * @{
+ */
+
+EAPI Eina_Bool    eina_binshare_init(void);
+EAPI Eina_Bool    eina_binshare_shutdown(void);
+EAPI const void * eina_binshare_add_length(const void *obj,
+                                          unsigned int olen) EINA_PURE EINA_WARN_UNUSED_RESULT;
+EAPI const void * eina_binshare_ref(const void *obj);
+EAPI void         eina_binshare_del(const void *obj);
+EAPI int          eina_binshare_length(const void *obj) EINA_WARN_UNUSED_RESULT;
+EAPI void         eina_binshare_dump(void);
+
+/**
+ * @brief Retrieve an instance of a blob for use in a program.
+ *
+ * @param   ptr The binary blob to retrieve an instance of.
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This macro retrieves an instance of @p obj. If @p obj is
+ * @c NULL, then @c NULL is returned. If @p obj is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the blobs to be searched and a duplicated blob
+ * of @p obj is returned.
+ *
+ * This macro essentially calls eina_binshare_add_length with ptr and sizeof(*ptr)
+ * as the parameters. It's useful for pointers to structures.
+ *
+ * @see eina_stringshare_add_length()
+ */
+#define eina_binshare_add(ptr) eina_binshare_add_length(ptr, sizeof(*ptr))
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STRINGSHARE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_config.h b/tests/suite/ecore/src/include/eina_config.h
new file mode 100644 (file)
index 0000000..f89327d
--- /dev/null
@@ -0,0 +1,52 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_CONFIG_H_
+#define EINA_CONFIG_H_
+
+#ifdef EINA_MAGIC_DEBUG
+# undef EINA_MAGIC_DEBUG
+#endif
+#define EINA_MAGIC_DEBUG
+
+#ifdef EINA_DEFAULT_MEMPOOL
+# undef EINA_DEFAULT_MEMPOOL
+#endif
+
+
+#ifdef EINA_SAFETY_CHECKS
+# undef EINA_SAFETY_CHECKS
+#endif
+#define EINA_SAFETY_CHECKS
+
+#ifdef EINA_HAVE_INTTYPES_H
+# undef EINA_HAVE_INTTYPES_H
+#endif
+#define EINA_HAVE_INTTYPES_H
+
+#ifdef EINA_HAVE_STDINT_H
+# undef EINA_HAVE_STDINT_H
+#endif
+#define EINA_HAVE_STDINT_H
+
+#ifdef EINA_SIZEOF_WCHAR_T
+# undef EINA_SIZEOF_WCHAR_T
+#endif
+#define EINA_SIZEOF_WCHAR_T 4
+
+#endif /* EINA_CONFIG_H_ */
diff --git a/tests/suite/ecore/src/include/eina_convert.h b/tests/suite/ecore/src/include/eina_convert.h
new file mode 100644 (file)
index 0000000..8cd6433
--- /dev/null
@@ -0,0 +1,80 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric BAIL, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_CONVERT_H_
+#define EINA_CONVERT_H_
+
+#include "eina_types.h"
+#include "eina_error.h"
+
+#include "eina_fp.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Convert_Group Convert
+ *
+ * @{
+ */
+
+/**
+ * @var EINA_ERROR_CONVERT_P_NOT_FOUND
+ * Error identifier corresponding to string not containing 'p'.
+ */
+EAPI extern Eina_Error EINA_ERROR_CONVERT_P_NOT_FOUND;
+
+/**
+ * @var EINA_ERROR_CONVERT_0X_NOT_FOUND
+ * Error identifier corresponding to string not containing '0x'.
+ */
+EAPI extern Eina_Error EINA_ERROR_CONVERT_0X_NOT_FOUND;
+
+/**
+ * @var EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH
+ * Error identifier corresponding to length of the string being too small.
+ */
+EAPI extern Eina_Error EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH;
+
+EAPI int       eina_convert_itoa(int n, char *s)  EINA_ARG_NONNULL(2);
+EAPI int       eina_convert_xtoa(unsigned int n, char *s) EINA_ARG_NONNULL(2);
+
+EAPI int       eina_convert_dtoa(double d, char *des) EINA_ARG_NONNULL(2);
+EAPI Eina_Bool eina_convert_atod(const char *src,
+                                 int length,
+                                 long long *m,
+                                 long *e) EINA_ARG_NONNULL(1,3,4);
+
+EAPI int       eina_convert_fptoa(Eina_F32p32 fp,
+                                  char *des) EINA_ARG_NONNULL(2);
+EAPI Eina_Bool eina_convert_atofp(const char *src,
+                                  int length,
+                                  Eina_F32p32 *fp) EINA_ARG_NONNULL(1,3);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_CONVERT_H_ */
diff --git a/tests/suite/ecore/src/include/eina_counter.h b/tests/suite/ecore/src/include/eina_counter.h
new file mode 100644 (file)
index 0000000..fc7b23f
--- /dev/null
@@ -0,0 +1,57 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_COUNTER_H_
+#define EINA_COUNTER_H_
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Counter_Group Counter
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Counter
+ * Counter type.
+ */
+typedef struct _Eina_Counter Eina_Counter;
+
+EAPI Eina_Counter * eina_counter_new(const char *name) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void           eina_counter_free(Eina_Counter *counter) EINA_ARG_NONNULL(1);
+EAPI void           eina_counter_start(Eina_Counter *counter) EINA_ARG_NONNULL(1);
+EAPI void           eina_counter_stop(Eina_Counter *counter,
+                                      int specimen) EINA_ARG_NONNULL(1);
+EAPI char *         eina_counter_dump(Eina_Counter *counter) EINA_ARG_NONNULL(1);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_COUNTER_H_ */
diff --git a/tests/suite/ecore/src/include/eina_cpu.h b/tests/suite/ecore/src/include/eina_cpu.h
new file mode 100644 (file)
index 0000000..ac32e1d
--- /dev/null
@@ -0,0 +1,39 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_CPU_H_
+#define EINA_CPU_H_
+
+#include "eina_types.h"
+
+typedef enum _Eina_Cpu_Features
+{
+   EINA_CPU_MMX = 0x00000001,
+   EINA_CPU_SSE = 0x00000002,
+   EINA_CPU_SSE2 = 0x00000004,
+   EINA_CPU_SSE3 = 0x00000008,
+   /* TODO 3DNow! */
+   EINA_CPU_ALTIVEC = 0x00000010,
+   EINA_CPU_VIS = 0x00000020,
+   EINA_CPU_NEON = 0x00000040,
+} Eina_Cpu_Features;
+
+EAPI Eina_Cpu_Features eina_cpu_features_get(void);
+EAPI int               eina_cpu_count(void);
+
+#endif /* EINA_CPU_H_ */
diff --git a/tests/suite/ecore/src/include/eina_error.h b/tests/suite/ecore/src/include/eina_error.h
new file mode 100644 (file)
index 0000000..8c74b66
--- /dev/null
@@ -0,0 +1,66 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_ERROR_H_
+#define EINA_ERROR_H_
+
+#include <stdarg.h>
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Error_Group Error
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Error
+ * Error type.
+ */
+typedef int Eina_Error;
+
+/**
+ * @var EINA_ERROR_OUT_OF_MEMORY
+ * Error identifier corresponding to a lack of memory.
+ */
+EAPI extern Eina_Error EINA_ERROR_OUT_OF_MEMORY;
+
+EAPI Eina_Error   eina_error_msg_register(const char *msg) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Error   eina_error_msg_static_register(const char *msg) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Bool    eina_error_msg_modify(Eina_Error error,
+                                       const char *msg) EINA_ARG_NONNULL(2);
+EAPI Eina_Error   eina_error_get(void);
+EAPI void         eina_error_set(Eina_Error err);
+EAPI const char * eina_error_msg_get(Eina_Error error) EINA_PURE;
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_ERROR_H_ */
diff --git a/tests/suite/ecore/src/include/eina_file.h b/tests/suite/ecore/src/include/eina_file.h
new file mode 100644 (file)
index 0000000..4c3740f
--- /dev/null
@@ -0,0 +1,92 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_FILE_H_
+#define EINA_FILE_H_
+
+#include <limits.h>
+
+#include "eina_types.h"
+#include "eina_array.h"
+#include "eina_iterator.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_File_Group File
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_File_Direct_Info
+ * A typedef to #_Eina_File_Direct_Info.
+ */
+typedef struct _Eina_File_Direct_Info Eina_File_Direct_Info;
+
+/**
+ * @typedef Eina_File_Dir_List_Cb
+ * Type for a callback to be called when iterating over the files of a
+ * directory.
+ */
+typedef void (*Eina_File_Dir_List_Cb)(const char *name, const char *path, void *data);
+
+/**
+ * @struct _Eina_File_Direct_Info
+ * A structure to store informations of a path.
+ */
+struct _Eina_File_Direct_Info
+{
+   size_t path_length; /**< size of the whole path */
+   size_t name_length; /**< size of the filename/basename component */
+   size_t name_start; /**< where the filename/basename component starts */
+   char path[PATH_MAX]; /**< the path */
+   const struct dirent *dirent; /**< the dirent structure of the path */
+};
+
+/**
+ * @def EINA_FILE_DIR_LIST_CB
+ * @brief cast to an #Eina_File_Dir_List_Cb.
+ *
+ * @param function The function to cast.
+ *
+ * This macro casts @p function to Eina_File_Dir_List_Cb.
+ */
+#define EINA_FILE_DIR_LIST_CB(function) ((Eina_File_Dir_List_Cb)function)
+
+EAPI Eina_Bool       eina_file_dir_list(const char *dir,
+                                        Eina_Bool recursive,
+                                        Eina_File_Dir_List_Cb cb,
+                                        void *data) EINA_ARG_NONNULL(1, 3);
+EAPI Eina_Array *    eina_file_split(char *path) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC;
+EAPI Eina_Iterator * eina_file_ls(const char *dir) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC;
+EAPI Eina_Iterator * eina_file_direct_ls(const char *dir) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC;
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_FILE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_fp.h b/tests/suite/ecore/src/include/eina_fp.h
new file mode 100644 (file)
index 0000000..47dbaa4
--- /dev/null
@@ -0,0 +1,111 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ * Copyright (C) 2009 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_FP_H_
+# define EINA_FP_H_
+
+#include "eina_types.h"
+
+#ifdef _MSC_VER
+typedef unsigned __int64 uint64_t;
+typedef signed __int64 int64_t;
+typedef signed int int32_t;
+#else
+# include <stdint.h>
+#endif
+
+#define EINA_F32P32_PI 0x00000003243f6a89
+
+typedef int64_t Eina_F32p32;
+typedef int32_t Eina_F16p16;
+typedef int32_t Eina_F8p24;
+
+static inline Eina_F32p32  eina_f32p32_int_from(int32_t v);
+static inline int32_t      eina_f32p32_int_to(Eina_F32p32 v);
+static inline Eina_F32p32  eina_f32p32_double_from(double v);
+static inline double       eina_f32p32_double_to(Eina_F32p32 v);
+
+static inline Eina_F32p32  eina_f32p32_add(Eina_F32p32 a,
+                                           Eina_F32p32 b);
+static inline Eina_F32p32  eina_f32p32_sub(Eina_F32p32 a,
+                                           Eina_F32p32 b);
+static inline Eina_F32p32  eina_f32p32_mul(Eina_F32p32 a,
+                                           Eina_F32p32 b);
+static inline Eina_F32p32  eina_f32p32_scale(Eina_F32p32 a,
+                                             int b);
+static inline Eina_F32p32  eina_f32p32_div(Eina_F32p32 a,
+                                           Eina_F32p32 b);
+static inline Eina_F32p32  eina_f32p32_sqrt(Eina_F32p32 a);
+static inline unsigned int eina_f32p32_fracc_get(Eina_F32p32 v);
+
+// dont use llabs - issues if not on 64bit
+#define eina_fp32p32_llabs(a) ((a < 0) ? -(a) : (a))
+
+EAPI Eina_F32p32           eina_f32p32_cos(Eina_F32p32 a);
+EAPI Eina_F32p32           eina_f32p32_sin(Eina_F32p32 a);
+
+static inline Eina_F16p16  eina_f16p16_int_from(int32_t v);
+static inline int32_t      eina_f16p16_int_to(Eina_F16p16 v);
+static inline Eina_F16p16  eina_f16p16_float_from(float v);
+static inline float        eina_f16p16_float_to(Eina_F16p16 v);
+
+static inline Eina_F16p16  eina_f16p16_add(Eina_F16p16 a,
+                                           Eina_F16p16 b);
+static inline Eina_F16p16  eina_f16p16_sub(Eina_F16p16 a,
+                                           Eina_F16p16 b);
+static inline Eina_F16p16  eina_f16p16_mul(Eina_F16p16 a,
+                                           Eina_F16p16 b);
+static inline Eina_F16p16  eina_f16p16_scale(Eina_F16p16 a,
+                                             int b);
+static inline Eina_F16p16  eina_f16p16_div(Eina_F16p16 a,
+                                           Eina_F16p16 b);
+static inline Eina_F16p16  eina_f16p16_sqrt(Eina_F16p16 a);
+static inline unsigned int eina_f16p16_fracc_get(Eina_F16p16 v);
+
+static inline Eina_F8p24   eina_f8p24_int_from(int32_t v);
+static inline int32_t      eina_f8p24_int_to(Eina_F8p24 v);
+static inline Eina_F8p24   eina_f8p24_float_from(float v);
+static inline float        eina_f8p24_float_to(Eina_F8p24 v);
+
+static inline Eina_F8p24   eina_f8p24_add(Eina_F8p24 a,
+                                          Eina_F8p24 b);
+static inline Eina_F8p24   eina_f8p24_sub(Eina_F8p24 a,
+                                          Eina_F8p24 b);
+static inline Eina_F8p24   eina_f8p24_mul(Eina_F8p24 a,
+                                          Eina_F8p24 b);
+static inline Eina_F8p24   eina_f8p24_scale(Eina_F8p24 a,
+                                            int b);
+static inline Eina_F8p24   eina_f8p24_div(Eina_F8p24 a,
+                                          Eina_F8p24 b);
+static inline Eina_F8p24   eina_f8p24_sqrt(Eina_F8p24 a);
+static inline unsigned int eina_f8p24_fracc_get(Eina_F8p24 v);
+
+static inline Eina_F32p32  eina_f16p16_to_f32p32(Eina_F16p16 a);
+static inline Eina_F32p32  eina_f8p24_to_f32p32(Eina_F8p24 a);
+static inline Eina_F16p16  eina_f32p32_to_f16p16(Eina_F32p32 a);
+static inline Eina_F16p16  eina_f8p24_to_f16p16(Eina_F8p24 a);
+static inline Eina_F8p24   eina_f32p32_to_f8p24(Eina_F32p32 a);
+static inline Eina_F8p24   eina_f16p16_to_f8p24(Eina_F16p16 a);
+
+#include "eina_inline_f32p32.x"
+#include "eina_inline_f16p16.x"
+#include "eina_inline_f8p24.x"
+#include "eina_inline_fp.x"
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_hamster.h b/tests/suite/ecore/src/include/eina_hamster.h
new file mode 100644 (file)
index 0000000..0818b15
--- /dev/null
@@ -0,0 +1,44 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_HAMSTER_H_
+#define EINA_HAMSTER_H_
+
+/**
+ * @addtogroup Eina_Core_Group Core
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Hamster_Group Hamster
+ *
+ * @{
+ */
+
+EAPI int eina_hamster_count(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_HAMSTER_H_ */
diff --git a/tests/suite/ecore/src/include/eina_hash.h b/tests/suite/ecore/src/include/eina_hash.h
new file mode 100644 (file)
index 0000000..0345133
--- /dev/null
@@ -0,0 +1,168 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri,
+ *                         Vincent Torri, Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_HASH_H_
+#define EINA_HASH_H_
+
+#include "eina_types.h"
+#include "eina_iterator.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Hash_Group Hash Table
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Hash
+ * Type for a generic hash table.
+ */
+typedef struct _Eina_Hash Eina_Hash;
+
+typedef struct _Eina_Hash_Tuple Eina_Hash_Tuple;
+
+struct _Eina_Hash_Tuple
+{
+   const void *key; /**< The key */
+   void *data; /**< The data associated to the key */
+   unsigned int key_length; /**< The length of the key */
+};
+
+typedef unsigned int (*Eina_Key_Length)(const void *key);
+#define EINA_KEY_LENGTH(Function) ((Eina_Key_Length)Function)
+typedef int (*Eina_Key_Cmp)(const void *key1, int key1_length, const void *key2, int key2_length);
+#define EINA_KEY_CMP(Function) ((Eina_Key_Cmp)Function)
+typedef int (*Eina_Key_Hash)(const void *key, int key_length);
+#define EINA_KEY_HASH(Function) ((Eina_Key_Hash)Function)
+typedef Eina_Bool (*Eina_Hash_Foreach)(const Eina_Hash *hash, const void *key, void *data, void *fdata);
+
+EAPI Eina_Hash *     eina_hash_new(Eina_Key_Length key_length_cb,
+                                   Eina_Key_Cmp key_cmp_cb,
+                                   Eina_Key_Hash key_hash_cb,
+                                   Eina_Free_Cb data_free_cb,
+                                   int buckets_power_size) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(2, 3);
+EAPI Eina_Hash *     eina_hash_string_djb2_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *     eina_hash_string_superfast_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *     eina_hash_string_small_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *     eina_hash_int32_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *     eina_hash_int64_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *     eina_hash_pointer_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *     eina_hash_stringshared_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Bool       eina_hash_add(Eina_Hash *hash,
+                                   const void *key,
+                                   const void *data) EINA_ARG_NONNULL(1, 2, 3);
+EAPI Eina_Bool       eina_hash_direct_add(Eina_Hash *hash,
+                                          const void *key,
+                                          const void *data) EINA_ARG_NONNULL(1, 2, 3);
+EAPI Eina_Bool       eina_hash_del(Eina_Hash *hash,
+                                   const void *key,
+                                   const void *data) EINA_ARG_NONNULL(1);
+EAPI void *          eina_hash_find(const Eina_Hash *hash,
+                                    const void *key) EINA_ARG_NONNULL(1, 2);
+EAPI void *          eina_hash_modify(Eina_Hash *hash,
+                                      const void *key,
+                                      const void *data) EINA_ARG_NONNULL(1, 2, 3);
+EAPI void *          eina_hash_set(Eina_Hash *hash,
+                                   const void *key,
+                                   const void *data) EINA_ARG_NONNULL(1, 2, 3);
+EAPI Eina_Bool       eina_hash_move(Eina_Hash *hash,
+                                    const void *old_key,
+                                    const void *new_key) EINA_ARG_NONNULL(1, 2, 3);
+EAPI void            eina_hash_free(Eina_Hash *hash) EINA_ARG_NONNULL(1);
+EAPI void            eina_hash_free_buckets(Eina_Hash *hash) EINA_ARG_NONNULL(1);
+EAPI int             eina_hash_population(const Eina_Hash *hash) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool       eina_hash_add_by_hash(Eina_Hash *hash,
+                                           const void *key,
+                                           int key_length,
+                                           int key_hash,
+                                           const void *data) EINA_ARG_NONNULL(1, 2, 5);
+EAPI Eina_Bool       eina_hash_direct_add_by_hash(Eina_Hash *hash,
+                                                  const void *key,
+                                                  int key_length,
+                                                  int key_hash,
+                                                  const void *data) EINA_ARG_NONNULL(1, 2, 5);
+EAPI Eina_Bool       eina_hash_del_by_key_hash(Eina_Hash *hash,
+                                               const void *key,
+                                               int key_length,
+                                               int key_hash) EINA_ARG_NONNULL(1, 2);
+EAPI Eina_Bool       eina_hash_del_by_key(Eina_Hash *hash,
+                                          const void *key) EINA_ARG_NONNULL(1, 2);
+EAPI Eina_Bool       eina_hash_del_by_data(Eina_Hash *hash,
+                                           const void *data) EINA_ARG_NONNULL(1, 2);
+EAPI Eina_Bool       eina_hash_del_by_hash(Eina_Hash *hash,
+                                           const void *key,
+                                           int key_length,
+                                           int key_hash,
+                                           const void *data) EINA_ARG_NONNULL(1);
+EAPI void *          eina_hash_find_by_hash(const Eina_Hash *hash,
+                                            const void *key,
+                                            int key_length,
+                                            int key_hash) EINA_ARG_NONNULL(1, 2);
+EAPI void *          eina_hash_modify_by_hash(Eina_Hash *hash,
+                                              const void *key,
+                                              int key_length,
+                                              int key_hash,
+                                              const void *data) EINA_ARG_NONNULL(1, 2, 5);
+EAPI Eina_Iterator * eina_hash_iterator_key_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Iterator * eina_hash_iterator_data_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Iterator * eina_hash_iterator_tuple_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI void            eina_hash_foreach(const Eina_Hash *hash,
+                                       Eina_Hash_Foreach cb,
+                                       const void *fdata) EINA_ARG_NONNULL(1, 2);
+/* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html) hash function used by WebCore (http://webkit.org/blog/8/hashtables-part-2/) */
+EAPI int             eina_hash_superfast(const char *key,
+                                         int len) EINA_ARG_NONNULL(1);
+/* Hash function first reported by dan bernstein many years ago in comp.lang.c */
+static inline int    eina_hash_djb2(const char *key,
+                                    int len) EINA_ARG_NONNULL(1);
+static inline int    eina_hash_djb2_len(const char *key,
+                                        int *plen) EINA_ARG_NONNULL(1, 2);
+/* Hash function from http://www.concentric.net/~Ttwang/tech/inthash.htm */
+static inline int    eina_hash_int32(const unsigned int *pkey,
+                                     int len) EINA_ARG_NONNULL(1);
+static inline int    eina_hash_int64(const unsigned long int *pkey,
+                                     int len) EINA_ARG_NONNULL(1);
+
+#include "eina_inline_hash.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /*EINA_HASH_H_*/
diff --git a/tests/suite/ecore/src/include/eina_inline_array.x b/tests/suite/ecore/src/include/eina_inline_array.x
new file mode 100644 (file)
index 0000000..883d728
--- /dev/null
@@ -0,0 +1,181 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_ARRAY_X_
+#define EINA_INLINE_ARRAY_X_
+
+#include <stdio.h>
+
+/**
+ * @cond LOCAL
+ */
+
+EAPI Eina_Bool eina_array_grow(Eina_Array *array);
+
+/**
+ * @endcond
+ */
+
+/**
+ * @addtogroup Eina_Array_Group Array
+ *
+ * @brief These functions provide array management.
+ *
+ * @{
+ */
+
+/**
+ * @brief Append a data to an array.
+ *
+ * @param array The array.
+ * @param data The data to add.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function appends @p data to @p array. For performance
+ * reasons, there is no check of @p array. If it is @c NULL or
+ * invalid, the program may crash. If @p data is @c NULL, or if an
+ * allocation is necessary and fails, #EINA_FALSE is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, #EINA_TRUE is
+ * returned.
+ */
+
+static inline Eina_Bool
+eina_array_push(Eina_Array *array, const void *data)
+{
+   if (!data) return EINA_FALSE;
+
+   if (EINA_UNLIKELY((array->count + 1) > array->total))
+     if (!eina_array_grow(array))
+       return EINA_FALSE;
+
+   array->data[array->count++] = (void*) data;
+
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Remove the last data of an array.
+ *
+ * @param array The array.
+ * @return The retrieved data.
+ *
+ * This function removes the last data of @p array, decreases the count
+ * of @p array and returns the data. For performance reasons, there
+ * is no check of @p array. If it is @c NULL or invalid, the program
+ * may crash. If the count member is less or equal than 0, @c NULL is
+ * returned.
+ */
+static inline void *
+eina_array_pop(Eina_Array *array)
+{
+   void *ret = NULL;
+
+   if (array->count <= 0)
+     goto on_empty;
+
+   ret = array->data[--array->count];
+
+ on_empty:
+   return ret;
+}
+
+/**
+ * @brief Return the data at a given position in an array.
+ *
+ * @param array The array.
+ * @param idx The potition of the data to retrieve.
+ * @return The retrieved data.
+ *
+ * This function returns the data at the position @p idx in @p
+ * array. For performance reasons, there is no check of @p array or @p
+ * idx. If it is @c NULL or invalid, the program may crash.
+ */
+static inline void *
+eina_array_data_get(const Eina_Array *array, unsigned int idx)
+{
+   return array->data[idx];
+}
+
+/**
+ * @brief Set the data at a given position in an array.
+ *
+ * @param array The array.
+ * @param idx The potition of the data to set.
+ * @param data The data to set.
+ *
+ * This function sets the data at the position @p idx in @p
+ * array. For performance reasons, there is no check of @p array or @p
+ * idx. If it is @c NULL or invalid, the program may crash.
+ */
+static inline void
+eina_array_data_set(const Eina_Array *array, unsigned int idx, const void *data)
+{
+   array->data[idx] = (void*) data;
+}
+
+/**
+ * @brief Return the number of elements in an array.
+ *
+ * @param array The array.
+ * @return The number of elements.
+ *
+ * This function returns the number of elements in @p array. For
+ * performance reasons, there is no check of @p array. If it is
+ * @c NULL or invalid, the program may crash.
+ */
+static inline unsigned int
+eina_array_count_get(const Eina_Array *array)
+{
+   return array->count;
+}
+
+/**
+ * @brief Provide a safe way to iterate over an array
+ *
+ * @param array The array to iterate over.
+ * @param cb The callback to call for each item.
+ * @param fdata The user data to pass to the callback.
+ * @return EINA_TRUE if it successfully iterate all items of the array.
+ *
+ * This function provide a safe way to iterate over an array. @p cb should
+ * return EINA_TRUE as long as you want the function to continue iterating,
+ * by returning EINA_FALSE it will stop and return EINA_FALSE as a result.
+ */
+static inline Eina_Bool
+eina_array_foreach(Eina_Array *array, Eina_Each_Cb cb, void *fdata)
+{
+   void *data;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+   Eina_Bool ret = EINA_TRUE;
+
+   EINA_ARRAY_ITER_NEXT(array, i, data, iterator)
+     if (cb(array, data, fdata) != EINA_TRUE)
+       {
+         ret = EINA_FALSE;
+         break;
+       }
+
+   return ret;
+}
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_f16p16.x b/tests/suite/ecore/src/include/eina_inline_f16p16.x
new file mode 100644 (file)
index 0000000..e16d188
--- /dev/null
@@ -0,0 +1,83 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ * Copyright (C) 2009 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_F16P16_X_
+#define EINA_INLINE_F16P16_X_
+
+static inline Eina_F16p16
+eina_f16p16_add(Eina_F16p16 a, Eina_F16p16 b)
+{
+   return a + b;
+}
+
+static inline Eina_F16p16
+eina_f16p16_sub(Eina_F16p16 a, Eina_F16p16 b)
+{
+   return a - b;
+}
+
+static inline Eina_F16p16
+eina_f16p16_mul(Eina_F16p16 a, Eina_F16p16 b)
+{
+   return (Eina_F16p16)(((int64_t)a * (int64_t)b) >> 16);
+}
+
+static inline Eina_F16p16
+eina_f16p16_scale(Eina_F16p16 a, int b)
+{
+   return a * b;
+}
+
+static inline Eina_F16p16
+eina_f16p16_div(Eina_F16p16 a, Eina_F16p16 b)
+{
+   return (Eina_F16p16) ((((int64_t) a) << 16) / (int64_t) b);
+}
+
+static inline Eina_F16p16
+eina_f16p16_sqrt(Eina_F16p16 a)
+{
+   unsigned int root, remHi, remLo, testDiv, count;
+
+   root = 0; /* Clear root */
+   remHi = 0; /* Clear high part of partial remainder */
+   remLo = a; /* Get argument into low part of partial remainder */
+   count = (15 + (16 >> 1)); /* Load loop counter */
+   do {
+      remHi = (remHi << 2) | (remLo >> 30);
+      remLo <<= 2; /* get 2 bits of arg */
+      root <<= 1; /* Get ready for the next bit in the root */
+      testDiv = (root << 1) + 1; /* Test radical */
+      if (remHi >= testDiv)
+       {
+          remHi -= testDiv;
+          root++;
+       }
+   } while (count-- != 0);
+
+   return root;
+}
+
+static inline unsigned int
+eina_f16p16_fracc_get(Eina_F16p16 v)
+{
+   return (v & 0xffff);
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_f32p32.x b/tests/suite/ecore/src/include/eina_inline_f32p32.x
new file mode 100644 (file)
index 0000000..73480de
--- /dev/null
@@ -0,0 +1,110 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2009 Jorge Luis Zapata Muga, Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_F32P32_X_
+# define EINA_INLINE_F32P32_X_
+
+#include <stdlib.h>
+
+static inline Eina_F32p32
+eina_f32p32_add(Eina_F32p32 a, Eina_F32p32 b)
+{
+   return a + b;
+}
+
+static inline Eina_F32p32
+eina_f32p32_sub(Eina_F32p32 a, Eina_F32p32 b)
+{
+   return a - b;
+}
+
+static inline Eina_F32p32
+eina_f32p32_mul(Eina_F32p32 a, Eina_F32p32 b)
+{
+   /* Prevent overflow and do '(a * b) >> 32' */
+   /* Basically do: Eina_F16p16 * Eina_F16p16 = Eina_F32p32 */
+   Eina_F32p32 up;
+   Eina_F32p32 down;
+   Eina_F32p32 result;
+   uint64_t as, bs;
+   Eina_F32p32 sign;
+
+   sign = a ^ b;
+   as = eina_fp32p32_llabs(a);
+   bs = eina_fp32p32_llabs(b);
+
+   up = (as >> 16) * (bs >> 16);
+   down = (as & 0xFFFF) * (bs & 0xFFFF);
+
+   result = up + (down >> 32);
+
+   return sign < 0 ? - result : result;
+}
+
+static inline Eina_F32p32
+eina_f32p32_scale(Eina_F32p32 a, int b)
+{
+   return a * b;
+}
+
+static inline Eina_F32p32
+eina_f32p32_div(Eina_F32p32 a, Eina_F32p32 b)
+{
+   Eina_F32p32 sign;
+   Eina_F32p32 result;
+
+   sign = a ^ b;
+
+   if (b == 0)
+     return sign < 0 ? (Eina_F32p32) 0x8000000000000000ull : (Eina_F32p32) 0x7FFFFFFFFFFFFFFFull;
+
+   result = (eina_f32p32_mul(eina_fp32p32_llabs(a),  (((uint64_t) 1 << 62) / ((uint64_t)(eina_fp32p32_llabs(b)) >> 2))));
+
+   return sign < 0 ? - result : result;
+}
+
+static inline Eina_F32p32
+eina_f32p32_sqrt(Eina_F32p32 a)
+{
+   uint64_t root, remHi, remLo, testDiv, count;
+
+   root = 0; /* Clear root */
+   remHi = 0; /* Clear high part of partial remainder */
+   remLo = a; /* Get argument into low part of partial remainder */
+   count = (31 + (32 >> 1)); /* Load loop counter */
+   do {
+      remHi = (remHi << 2) | (remLo >> 30);
+      remLo <<= 2; /* get 2 bits of arg */
+      root <<= 1; /* Get ready for the next bit in the root */
+      testDiv = (root << 1) + 1; /* Test radical */
+      if (remHi >= testDiv) {
+        remHi -= testDiv;
+        root++;
+      }
+   } while (count-- != 0);
+
+   return root;
+}
+
+static inline unsigned int
+eina_f32p32_fracc_get(Eina_F32p32 v)
+{
+   return (unsigned int)v;
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_f8p24.x b/tests/suite/ecore/src/include/eina_inline_f8p24.x
new file mode 100644 (file)
index 0000000..f80bf61
--- /dev/null
@@ -0,0 +1,82 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ * Copyright (C) 2009 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_F8P24_X_
+#define EINA_INLINE_F8P24_X_
+
+static inline Eina_F8p24
+eina_f8p24_add(Eina_F8p24 a, Eina_F8p24 b)
+{
+   return a + b;
+}
+
+static inline Eina_F8p24
+eina_f8p24_sub(Eina_F8p24 a, Eina_F8p24 b)
+{
+   return a - b;
+}
+
+static inline Eina_F8p24
+eina_f8p24_mul(Eina_F8p24 a, Eina_F8p24 b)
+{
+   return (Eina_F8p24)(((int64_t) a * (int64_t) b) >> 24);
+}
+
+static inline Eina_F8p24
+eina_f8p24_scale(Eina_F8p24 a, int b)
+{
+   return a * b;
+}
+
+static inline Eina_F8p24
+eina_f8p24_div(Eina_F8p24 a, Eina_F8p24 b)
+{
+   return (Eina_F8p24) ((((int64_t) a) << 24) / (int64_t) b);
+}
+
+static inline Eina_F8p24
+eina_f8p24_sqrt(Eina_F8p24 a)
+{
+   unsigned int root, remHi, remLo, testDiv, count;
+
+   root = 0; /* Clear root */
+   remHi = 0; /* Clear high part of partial remainder */
+   remLo = a; /* Get argument into low part of partial remainder */
+   count = (23 + (24 >> 1)); /* Load loop counter */
+   do {
+      remHi = (remHi << 2) | (remLo >> 30);
+      remLo <<= 2; /* get 2 bits of arg */
+      root <<= 1; /* Get ready for the next bit in the root */
+      testDiv = (root << 1) + 1; /* Test radical */
+      if (remHi >= testDiv)
+       {
+          remHi -= testDiv;
+          root++;
+       }
+   } while (count-- != 0);
+   return (root);
+}
+
+static inline unsigned int
+eina_f8p24_fracc_get(Eina_F8p24 v)
+{
+   return (v & 0xffffff);
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_fp.x b/tests/suite/ecore/src/include/eina_inline_fp.x
new file mode 100644 (file)
index 0000000..de44123
--- /dev/null
@@ -0,0 +1,153 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ * Copyright (C) 2009 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_FP_X_
+# define EINA_INLINE_FP_X_
+
+static inline Eina_F32p32
+eina_f32p32_int_from(int32_t v)
+{
+   return (Eina_F32p32)(v) << 32;
+}
+
+static inline int32_t
+eina_f32p32_int_to(Eina_F32p32 v)
+{
+   return (int32_t)(v >> 32);
+}
+
+static inline Eina_F32p32
+eina_f32p32_double_from(double v)
+{
+   Eina_F32p32 r;
+   r = (Eina_F32p32)(v * 4294967296.0 + (v < 0 ? -0.5 : 0.5));
+   return r;
+}
+
+static inline double
+eina_f32p32_double_to(Eina_F32p32 v)
+{
+   double r;
+   r = v / 4294967296.0;
+   return r;
+}
+
+
+
+static inline Eina_F16p16
+eina_f16p16_int_from(int32_t v)
+{
+   return v << 16;
+}
+
+static inline int32_t
+eina_f16p16_int_to(Eina_F16p16 v)
+{
+   return v >> 16;
+}
+
+static inline Eina_F16p16
+eina_f16p16_float_from(float v)
+{
+   Eina_F16p16 r;
+
+   r = (Eina_F16p16)(v * 65536.0f + (v < 0 ? -0.5f : 0.5f));
+   return r;
+}
+
+static inline float
+eina_f16p16_float_to(Eina_F16p16 v)
+{
+   float r;
+
+   r = v / 65536.0f;
+   return r;
+}
+
+
+
+static inline Eina_F8p24
+eina_f8p24_int_from(int32_t v)
+{
+   return v << 24;
+}
+
+static inline int32_t
+eina_f8p24_int_to(Eina_F8p24 v)
+{
+   return v >> 24;
+}
+
+static inline Eina_F8p24
+eina_f8p24_float_from(float v)
+{
+   Eina_F8p24 r;
+
+   r = (Eina_F8p24)(v * 16777216.0f + (v < 0 ? -0.5f : 0.5f));
+   return r;
+}
+
+static inline float
+eina_f8p24_float_to(Eina_F8p24 v)
+{
+   float r;
+
+   r = v / 16777216.0f;
+   return r;
+}
+
+
+
+static inline Eina_F32p32
+eina_f16p16_to_f32p32(Eina_F16p16 a)
+{
+   return ((Eina_F32p32) a) << 16;
+}
+
+static inline Eina_F32p32
+eina_f8p24_to_f32p32(Eina_F8p24 a)
+{
+   return ((Eina_F32p32) a) << 8;
+}
+
+static inline Eina_F16p16
+eina_f32p32_to_f16p16(Eina_F32p32 a)
+{
+   return (Eina_F16p16) (a >> 16);
+}
+
+static inline Eina_F16p16
+eina_f8p24_to_f16p16(Eina_F8p24 a)
+{
+   return (Eina_F16p16) (a >> 8);
+}
+
+static inline Eina_F8p24
+eina_f32p32_to_f8p24(Eina_F32p32 a)
+{
+   return (Eina_F8p24) (a >> 8);
+}
+
+static inline Eina_F8p24
+eina_f16p16_to_f8p24(Eina_F16p16 a)
+{
+   return (Eina_F8p24) (a << 8);
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_hash.x b/tests/suite/ecore/src/include/eina_inline_hash.x
new file mode 100644 (file)
index 0000000..8aa7bec
--- /dev/null
@@ -0,0 +1,88 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_HASH_X_
+#define EINA_INLINE_HASH_X_
+
+/*
+  djb2 hash algorithm was first reported by dan bernstein, and was the old
+  default hash function for evas.
+ */
+static inline int
+eina_hash_djb2(const char *key, int len)
+{
+   unsigned int hash_num = 5381;
+   const unsigned char *ptr;
+
+   if (!key) return 0;
+   for (ptr = (unsigned char *)key; len; ptr++, len--)
+     hash_num = ((hash_num << 5) + hash_num) ^ *ptr; /* hash * 33 ^ c */
+
+   return (int)hash_num;
+}
+
+static inline int
+eina_hash_djb2_len(const char *key, int *plen)
+{
+   unsigned int hash_num = 5381;
+   int len = 0;
+   const unsigned char *ptr;
+
+   if (!key) return 0;
+
+   for (ptr = (unsigned char *)key; *ptr; ptr++, len++)
+     hash_num = ((hash_num << 5) + hash_num) ^ *ptr; /* hash * 33 ^ c */
+
+   *plen = len + 1;
+
+   return (int)hash_num;
+}
+
+static inline int
+eina_hash_int32(const unsigned int *pkey, int len)
+{
+  unsigned int key = *pkey;
+
+  (void) len;
+
+  key = ~key + (key << 15);
+  key = key ^ (key >> 12);
+  key = key + (key << 2);
+  key = key ^ (key >> 4);
+  key = key * 2057;
+  key = key ^ (key >> 16);
+  return key;
+}
+
+static inline int
+eina_hash_int64(const unsigned long int *pkey, int len)
+{
+  unsigned long int key = *pkey;
+
+  (void) len;
+
+  key = (~key) + (key << 18);
+  key = key ^ (key >> 31);
+  key = key * 21;
+  key = key ^ (key >> 11);
+  key = key + (key << 6);
+  key = key ^ (key >> 22);
+  return (int) key;
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_list.x b/tests/suite/ecore/src/include/eina_inline_list.x
new file mode 100644 (file)
index 0000000..8325370
--- /dev/null
@@ -0,0 +1,145 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_LIST_INLINE_H_
+#define EINA_LIST_INLINE_H_
+
+/**
+ * @addtogroup Eina_List_Group List
+ *
+ * @brief These functions provide list management.
+ *
+ * @{
+ */
+
+/**
+ * @brief Get the last list node in the list.
+ *
+ * @param list The list to get the last list node from.
+ * @return The last list node in the list.
+ *
+ * This function returns the last list node in the list @p list. If
+ * @p list is @c NULL or empty, @c NULL is returned.
+ *
+ * This is a order-1 operation (it takes the same short time
+ * regardless of the length of the list).
+ */
+static inline Eina_List *
+eina_list_last(const Eina_List *list)
+{
+   if (!list) return NULL;
+   return list->accounting->last;
+}
+
+/**
+ * @brief Get the next list node after the specified list node.
+ *
+ * @param list The list node to get the next list node from
+ * @return The next list node on success, @c NULL otherwise.
+ *
+ * This function returns the next list node after the current one in
+ * @p list. It is equivalent to list->next. If @p list is @c NULL or
+ * if no next list node exists, it returns @c NULL.
+ */
+static inline Eina_List *
+eina_list_next(const Eina_List *list)
+{
+   if (!list) return NULL;
+   return list->next;
+}
+
+/**
+ * @brief Get the previous list node before the specified list node.
+ *
+ * @param list The list node to get the previous list node from.
+ * @return The previous list node o success, @c NULL otherwise.
+ * if no previous list node exists
+ *
+ * This function returns the previous list node before the current one
+ * in @p list. It is equivalent to list->prev. If @p list is @c NULL or
+ * if no previous list node exists, it returns @c NULL.
+ */
+static inline Eina_List *
+eina_list_prev(const Eina_List *list)
+{
+   if (!list) return NULL;
+   return list->prev;
+}
+
+/**
+ * @brief Get the list node data member.
+ *
+ * @param list The list node to get the data member of.
+ * @return The data member from the list node.
+ *
+ * This function returns the data member of the specified list node @p
+ * list. It is equivalent to list->data. If @p list is @c NULL, this
+ * function returns @c NULL.
+ */
+static inline void *
+eina_list_data_get(const Eina_List *list)
+{
+   if (!list) return NULL;
+   return list->data;
+}
+
+/**
+ * @brief Set the list node data member.
+ *
+ * @param list The list node to get the data member of.
+ * @param data The data member to the list node.
+ * @return The previous data value.
+ *
+ * This function set the data member @p data of the specified list node
+ * @p list. It returns the previous data of the node. If @p list is
+ * @c NULL, this function returns @c NULL.
+ */
+static inline void *
+eina_list_data_set(Eina_List *list, const void *data)
+{
+   void *tmp;
+   if (!list) return NULL;
+   tmp = list->data;
+   list->data = (void*) data;
+   return tmp;
+}
+
+/**
+ * @brief Get the count of the number of items in a list.
+ *
+ * @param list The list whose count to return.
+ * @return The number of members in the list.
+ *
+ * This function returns how many members @p list contains. If the
+ * list is @c NULL, 0 is returned.
+ *
+ * NB: This is an order-1 operation and takes the same time regardless
+ * of the length of the list.
+ */
+static inline unsigned int
+eina_list_count(const Eina_List *list)
+{
+   if (!list) return 0;
+   return list->accounting->count;
+}
+
+/**
+ * @}
+ */
+
+#endif /* EINA_LIST_INLINE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_inline_log.x b/tests/suite/ecore/src/include/eina_inline_log.x
new file mode 100644 (file)
index 0000000..4cdd7d8
--- /dev/null
@@ -0,0 +1,197 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_LOG_INLINE_H_
+#define EINA_LOG_INLINE_H_
+
+/**
+ * @addtogroup Eina_Log_Group Log
+ *
+ * @{
+ */
+
+/**
+ * Checks whenever the given level should be printed out.
+ *
+ * This is useful to enable certain blocks of code just when given
+ * level is to be used.
+ *
+ * @code
+ * #include <Eina.h>
+ *
+ * void my_func(void *data)
+ * {
+ *    if (eina_log_level_check(EINA_LOG_LEVEL_WARN))
+ *       expensive_debugging_code(data);
+ *
+ *    my_func_code(data);
+ * }
+ * @endcode
+ *
+ * @return #EINA_TRUE if level is equal or smaller than the current global
+ *         logging level.
+ */
+static inline Eina_Bool
+eina_log_level_check(int level)
+{
+   return eina_log_level_get() <= level;
+}
+
+/**
+ * Checks whenever the given level should be printed out.
+ *
+ * This is useful to enable certain blocks of code just when given
+ * level is to be used.
+ *
+ * @code
+ * #include <Eina.h>
+ *
+ * extern int _my_log_dom;
+ *
+ * void my_func(void *data)
+ * {
+ *    if (eina_log_domain_level_check(_my_log_dom, EINA_LOG_LEVEL_WARN))
+ *       expensive_debugging_code(data);
+ *
+ *    my_func_code(data);
+ * }
+ * @endcode
+ *
+ * @return #EINA_TRUE if level is equal or smaller than the current
+ *         domain logging level.
+ */
+static inline Eina_Bool
+eina_log_domain_level_check(int domain, int level)
+{
+   int dom_level = eina_log_domain_registered_level_get(domain);
+   if (EINA_UNLIKELY(dom_level == EINA_LOG_LEVEL_UNKNOWN))
+     return EINA_FALSE;
+   return dom_level <= level;
+}
+
+/**
+ * Function to format the level as a 3 character (+1 null byte) string.
+ *
+ * This function converts the given level to a known string name (CRI,
+ * ERR, WRN, INF or DBG) or a zero-padded 3-character string. In any
+ * case the last byte will contain a trailing null byte.
+ *
+ * If extreme level values are used (greater than 999 and smaller than
+ * -99), then the value will just consider the less significant
+ * part. This is so uncommon that users should handle this in their
+ * code.
+ *
+ * @param level what level value to use.
+ * @param name where to write the actual value.
+ *
+ * @return pointer to @p name.
+ */
+static inline const char *
+eina_log_level_name_get(int level, char name[4])
+{
+#define BCPY(A, B, C) \
+   do { name[0] = A; name[1] = B; name[2] = C; } while (0)
+
+   if (EINA_UNLIKELY(level < 0))
+     {
+       name[0] = '-';
+       name[1] = '0' + (-level / 10) % 10;
+       name[2] = '0' + (-level % 10);
+     }
+   else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
+     {
+       name[0] = '0' + (level / 100) % 10;
+       name[1] = '0' + (level / 10) % 10;
+       name[2] = '0' + level % 10;
+     }
+   else if (level == 0)
+     BCPY('C', 'R', 'I');
+   else if (level == 1)
+     BCPY('E', 'R', 'R');
+   else if (level == 2)
+     BCPY('W', 'R', 'N');
+   else if (level == 3)
+     BCPY('I', 'N', 'F');
+   else if (level == 4)
+     BCPY('D', 'B', 'G');
+   else
+     BCPY('?', '?', '?');
+
+   name[3] = '\0';
+
+   return name;
+}
+
+/**
+ * Function to get recommended color value for level.
+ *
+ * This function will not check if colors are enabled or not before
+ * returning the level color. If you desire such check, use
+ * eina_log_level_color_if_enabled_get().
+ *
+ * @param level what level value to use.
+ *
+ * @return pointer to null byte terminated ANSI color string to be
+ *         used in virtual terminals supporting VT100 color codes.
+ *
+ * @see eina_log_level_color_if_enabled_get()
+ */
+static inline const char *
+eina_log_level_color_get(int level)
+{
+   if (level <= 0)
+     return EINA_COLOR_LIGHTRED;
+   else if (level == 1)
+     return EINA_COLOR_RED;
+   else if (level == 2)
+     return EINA_COLOR_YELLOW;
+   else if (level == 3)
+     return EINA_COLOR_GREEN;
+   else if (level == 4)
+     return EINA_COLOR_LIGHTBLUE;
+   else
+     return EINA_COLOR_BLUE;
+}
+
+/**
+ * Function to get recommended color value for level, if colors are
+ * enabled.
+ *
+ * This function will check if colors are enabled or not before
+ * returning the level color. If colors are disabled, then empty
+ * string is returned.
+ *
+ * @param level what level value to use.
+ *
+ * @return pointer to null byte terminated ANSI color string to be
+ *         used in virtual terminals supporting VT100 color codes. If
+ *         colors are disabled, the empty string is returned.
+ */
+static inline const char *
+eina_log_level_color_if_enabled_get(int level)
+{
+   if (eina_log_color_disable_get())
+     return "";
+   return eina_log_level_color_get(level);
+}
+
+/**
+ * @}
+ */
+
+#endif /* EINA_LOG_INLINE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_inline_mempool.x b/tests/suite/ecore/src/include/eina_inline_mempool.x
new file mode 100644 (file)
index 0000000..3f44b90
--- /dev/null
@@ -0,0 +1,105 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_MEMPOOL_X_
+#define EINA_INLINE_MEMPOOL_X_
+
+/**
+ * @addtogroup Eina_Memory_Pool_Group Memory Pool
+ *
+ * @{
+ */
+
+/* Memory Pool */
+struct _Eina_Mempool_Backend
+{
+   const char *name;
+   void *(*init)(const char *context, const char *options, va_list args);
+   void (*free)(void *data, void *element);
+   void *(*alloc)(void *data, unsigned int size);
+   void *(*realloc)(void *data, void *element, unsigned int size);
+   void (*garbage_collect)(void *data);
+   void (*statistics)(void *data);
+   void (*shutdown)(void *data);
+};
+
+struct _Eina_Mempool
+{
+   Eina_Mempool_Backend backend;
+   void *backend_data;
+};
+
+/**
+ * @brief Re-allocate a amount memory by the given mempool.
+ *
+ * @param mp The mempool.
+ * @param element The element to re-allocate.
+ * @param size The size in bytes to re-allocate.
+ * @return The newly re-allocated data.
+ *
+ * This function re-allocates @p element with @p size bytes, using the
+ * mempool @p mp and returns the allocated data. If not used anymore,
+ * the data must be freed with eina_mempool_free(). No check is done
+ * on @p mp, so it must be a valid mempool.
+ */
+static inline void *
+eina_mempool_realloc(Eina_Mempool *mp, void *element, unsigned int size)
+{
+   return mp->backend.realloc(mp->backend_data, element, size);
+}
+
+/**
+ * @brief Allocate a amount memory by the given mempool.
+ *
+ * @param mp The mempool.
+ * @param size The size in bytes to allocate.
+ * @return The newly allocated data.
+ *
+ * This function allocates @p size bytes, using the mempool @p mp and
+ * returns the allocated data. If not used anymore, the data must be
+ * freed with eina_mempool_free(). No check is done on @p mp, so it
+ * must be a valid mempool.
+ */
+static inline void *
+eina_mempool_malloc(Eina_Mempool *mp, unsigned int size)
+{
+   return mp->backend.alloc(mp->backend_data, size);
+}
+
+/**
+ * @brief Free the allocated ressources by the given mempool.
+ *
+ * @param mp The mempool.
+ * @param element The data to free.
+ *
+ * This function frees @p element allocated by @p mp. @p element must
+ * have been obtained by eina_mempool_malloc() or
+ * eina_mempool_realloc(). No check is done on @p mp, so it must be a
+ * valid mempool.
+ */
+static inline void
+eina_mempool_free(Eina_Mempool *mp, void *element)
+{
+   mp->backend.free(mp->backend_data, element);
+}
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_rbtree.x b/tests/suite/ecore/src/include/eina_inline_rbtree.x
new file mode 100644 (file)
index 0000000..954774b
--- /dev/null
@@ -0,0 +1,50 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_RBTREE_INLINE_H_
+#define EINA_RBTREE_INLINE_H_
+
+/**
+ * @addtogroup Eina_Rbtree_Group Red-Black tree
+ *
+ * @brief These functions provide Red-Black trees management.
+ *
+ * @{
+ */
+
+static inline Eina_Rbtree *
+eina_rbtree_inline_lookup(const Eina_Rbtree *root, const void *key, int length, Eina_Rbtree_Cmp_Key_Cb cmp, const void *data)
+{
+   int result;
+
+   while (root)
+     {
+        result = cmp(root, key, length, (void*) data);
+        if (result == 0) return (Eina_Rbtree*) root;
+
+        root = root->son[result < 0 ? 0 : 1];
+     }
+
+   return NULL;
+}
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_rectangle.x b/tests/suite/ecore/src/include/eina_inline_rectangle.x
new file mode 100644 (file)
index 0000000..29ad24b
--- /dev/null
@@ -0,0 +1,254 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_RECTANGLE_H__
+#define EINA_INLINE_RECTANGLE_H__
+
+/**
+ * @addtogroup Eina_Rectangle_Group Rectangle
+ *
+ * @brief These functions provide rectangle management.
+ *
+ * @{
+ */
+
+/**
+ * @brief Check if the given spans intersect.
+ *
+ * @param c1 The column of the first span.
+ * @param l1 The length of the first span.
+ * @param c2 The column of the second span.
+ * @param l2 The length of the second span.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if the  given spans intersect,
+ * #EINA_FALSE otherwise.
+ */
+static inline int
+eina_spans_intersect(int c1, int l1, int c2, int l2)
+{
+       return (!(((c2 + l2) <= c1) || (c2 >= (c1 + l1))));
+}
+
+/**
+ * @brief Check if the given rectangle is empty.
+ *
+ * @param r The rectangle to check.
+ * @return #EINA_TRUE if the rectangle is empty, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if @p r is empty, #EINA_FALSE
+ * otherwise. No check is done on @p r, so it must be a valid
+ * rectangle.
+ */
+static inline Eina_Bool
+eina_rectangle_is_empty(const Eina_Rectangle *r)
+{
+       return ((r->w < 1) || (r->h < 1)) ? EINA_TRUE : EINA_FALSE;
+}
+
+/**
+ * @brief Set the coordinates and size of the given rectangle.
+ *
+ * @param r The rectangle.
+ * @param x The top-left x coordinate of the rectangle.
+ * @param y The top-left y coordinate of the rectangle.
+ * @param w The width of the rectangle.
+ * @param h The height of the rectangle.
+ *
+ * This function sets its top-left x coordinate to @p x, its top-left
+ * y coordinate to @p y, its width to @p w and its height to @p h. No
+ * check is done on @p r, so it must be a valid rectangle.
+ */
+static inline void
+eina_rectangle_coords_from(Eina_Rectangle *r, int x, int y, int w, int h)
+{
+       r->x = x;
+       r->y = y;
+       r->w = w;
+       r->h = h;
+}
+
+/**
+ * @brief Check if the given rectangles intersect.
+ *
+ * @param r1 The first rectangle.
+ * @param r2 The second rectangle.
+ * @return #EINA_TRUE if the rectangles intersect, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if @p r1 and @p r2 intersect,
+ * #EINA_FALSE otherwise. No check is done on @p r1 and @p r2, so they
+ * must be valid rectangles.
+ */
+static inline Eina_Bool
+eina_rectangles_intersect(const Eina_Rectangle *r1, const Eina_Rectangle *r2)
+{
+       return (eina_spans_intersect(r1->x, r1->w, r2->x, r2->w) && eina_spans_intersect(r1->y, r1->h, r2->y, r2->h)) ? EINA_TRUE : EINA_FALSE;
+}
+
+/**
+ * @brief Check if the given x-coordinate is in the rectangle .
+ *
+ * @param r The rectangle.
+ * @param x The x coordinate.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if @p x is in @p r with respect to
+ * the horizontal direction, #EINA_FALSE otherwise. No check is done
+ * on @p r, so it must be a valid rectangle.
+ */
+static inline Eina_Bool
+eina_rectangle_xcoord_inside(const Eina_Rectangle *r, int x)
+{
+       return ((x >= r->x) && (x < (r->x + r->w))) ? EINA_TRUE : EINA_FALSE;
+}
+
+/**
+ * @brief Check if the given y-coordinate is in the rectangle .
+ *
+ * @param r The rectangle.
+ * @param y The y coordinate.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if @p y is in @p r with respect to
+ * the vertical direction, #EINA_FALSE otherwise. No check is done
+ * on @p r, so it must be a valid rectangle.
+ */
+static inline Eina_Bool
+eina_rectangle_ycoord_inside(const Eina_Rectangle *r, int y)
+{
+       return ((y >= r->y) && (y < (r->y + r->h))) ? EINA_TRUE : EINA_FALSE;
+}
+
+/**
+ * @brief Check if the given point is in the rectangle .
+ *
+ * @param r The rectangle.
+ * @param x The x coordinate of the point.
+ * @param y The y coordinate of the point.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if the point of coordinate (@p x,
+ * @p y) is in @p r, #EINA_FALSE otherwise. No check is done on @p r,
+ * so it must be a valid rectangle.
+ */
+static inline Eina_Bool
+eina_rectangle_coords_inside(const Eina_Rectangle *r, int x, int y)
+{
+       return (eina_rectangle_xcoord_inside(r, x) && eina_rectangle_ycoord_inside(r, y)) ? EINA_TRUE : EINA_FALSE;
+}
+
+/**
+ * @brief Get the union of two rectangles.
+ *
+ * @param dst The first rectangle.
+ * @param src The second rectangle.
+ *
+ * This function get the union of the rectangles @p dst and @p src. The
+ * result is stored in @p dst. No check is done on @p dst or @p src,
+ * so they must be valid rectangles.
+ */
+static inline void
+eina_rectangle_union(Eina_Rectangle *dst, const Eina_Rectangle *src)
+{
+       /* left */
+       if (dst->x > src->x)
+       {
+               dst->w += dst->x - src->x;
+               dst->x = src->x;
+       }
+       /* right */
+       if ((dst->x + dst->w) < (src->x + src->w))
+               dst->w = src->x + src->w;
+       /* top */
+       if (dst->y > src->y)
+       {
+               dst->h += dst->y - src->y;
+               dst->y = src->y;
+       }
+       /* bottom */
+       if ((dst->y + dst->h) < (src->y + src->h))
+               dst->h = src->y + src->h;
+}
+
+/**
+ * @brief Get the intersection of two rectangles.
+ *
+ * @param dst The first rectangle.
+ * @param src The second rectangle.
+ * @return #EINA_TRUE if the rectangles intersect, #EINA_FALSE
+ * otherwise.
+ *
+ * This function get the intersection of the rectangles @p dst and
+ * @p src. The result is stored in @p dst. No check is done on @p dst
+ * or @p src, so they must be valid rectangles.
+ */
+static inline Eina_Bool
+eina_rectangle_intersection(Eina_Rectangle *dst, const Eina_Rectangle *src)
+{
+       if (!(eina_rectangles_intersect(dst, src)))
+               return EINA_FALSE;
+
+       /* left */
+       if (dst->x < src->x)
+       {
+               dst->w += dst->x - src->x;
+               dst->x = src->x;
+               if (dst->w < 0)
+                       dst->w = 0;
+       }
+       /* right */
+       if ((dst->x + dst->w) > (src->x + src->w))
+               dst->w = src->x + src->w - dst->x;
+       /* top */
+       if (dst->y < src->y)
+       {
+               dst->h += dst->y - src->y;
+               dst->y = src->y;
+               if (dst->h < 0)
+                       dst->h = 0;
+       }
+       /* bottom */
+       if ((dst->y + dst->h) > (src->y + src->h))
+               dst->h = src->y + src->h - dst->y;
+
+       return EINA_TRUE;
+}
+
+static inline void
+eina_rectangle_rescale_in(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res)
+{
+       res->x = in->x - out->x;
+       res->y = in->y - out->y;
+       res->w = in->w;
+       res->h = in->h;
+}
+
+static inline void
+eina_rectangle_rescale_out(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res)
+{
+       res->x = out->x + in->x;
+       res->y = out->y + in->y;
+       res->w = out->w;
+       res->h = out->h;
+}
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_str.x b/tests/suite/ecore/src/include/eina_inline_str.x
new file mode 100644 (file)
index 0000000..2daeb85
--- /dev/null
@@ -0,0 +1,76 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_STR_INLINE_H_
+#define EINA_STR_INLINE_H_
+
+/**
+ * @addtogroup Eina_String_Group String
+ *
+ * @{
+ */
+
+/**
+ * @brief Count up to a given amount of bytes of the given string.
+ *
+ * @param str The string pointer.
+ * @param maxlen The maximum length to allow.
+ * @return the string size or (size_t)-1 if greater than @a maxlen.
+ *
+ * This function returns the size of @p str, up to @p maxlen
+ * characters. It avoid needless iterations after that size. @p str
+ * must be a valid pointer and MUST not be @c NULL, otherwise this
+ * function will crash. This function returns the string size, or
+ * (size_t)-1 if the size is greater than @a maxlen.
+ */
+static inline size_t
+eina_strlen_bounded(const char *str, size_t maxlen)
+{
+   const char *itr, *str_maxend = str + maxlen;
+   for (itr = str; *itr != '\0'; itr++)
+     if (itr == str_maxend) return (size_t)-1;
+   return itr - str;
+}
+
+/**
+ * @brief Join two strings of known length.
+ *
+ * @param dst The buffer to store the result.
+ * @param size Size (in byte) of the buffer.
+ * @param sep The separator character to use.
+ * @param a First string to use, before @p sep.
+ * @param b Second string to use, after @p sep.
+ * @return The number of characters printed.
+ *
+ * This function is similar to eina_str_join_len(), but will compute
+ * the length of @p a  and @p b using strlen().
+ *
+ * @see eina_str_join_len()
+ * @see eina_str_join_static()
+ */
+static inline size_t
+eina_str_join(char *dst, size_t size, char sep, const char *a, const char *b)
+{
+   return eina_str_join_len(dst, size, sep, a, strlen(a), b, strlen(b));
+}
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STR_INLINE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_inline_stringshare.x b/tests/suite/ecore/src/include/eina_inline_stringshare.x
new file mode 100644 (file)
index 0000000..bfd7677
--- /dev/null
@@ -0,0 +1,91 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_STRINGSHARE_INLINE_H_
+#define EINA_STRINGSHARE_INLINE_H_
+
+#include <string.h>
+#include "eina_stringshare.h"
+/**
+ * @addtogroup Eina_Stringshare_Group Stringshare
+ *
+ * @{
+ */
+
+/**
+ * Replace the previously stringshared pointer with new content.
+ *
+ * The string pointed by @a p_str should be previously stringshared or
+ * @c NULL and it will be eina_stringshare_del(). The new string will
+ * be passed to eina_stringshare_add() and then assigned to @c *p_str.
+ *
+ * @param p_str pointer to the stringhare to be replaced. Must not be
+ *        @c NULL, but @c *p_str may be @c NULL as it is a valid
+ *        stringshare handle.
+ * @param news new string to be stringshared, may be @c NULL.
+ *
+ * @return #EINA_TRUE if the strings were different and thus replaced,
+ *         #EINA_FALSE if the strings were the same after shared.
+ */
+static inline Eina_Bool
+eina_stringshare_replace(const char **p_str, const char *news)
+{
+   if (*p_str == news) return EINA_FALSE;
+
+   news = eina_stringshare_add(news);
+   eina_stringshare_del(*p_str);
+   if (*p_str == news)
+     return EINA_FALSE;
+   *p_str = news;
+   return EINA_TRUE;
+}
+
+/**
+ * Replace the previously stringshared pointer with a new content.
+ *
+ * The string pointed by @a p_str should be previously stringshared or
+ * @c NULL and it will be eina_stringshare_del(). The new string will
+ * be passed to eina_stringshare_add_length() and then assigned to @c *p_str.
+ *
+ * @param p_str pointer to the stringhare to be replaced. Must not be
+ *        @c NULL, but @c *p_str may be @c NULL as it is a valid
+ *        stringshare handle.
+ * @param news new string to be stringshared, may be @c NULL.
+ * @param slen The string size (<= strlen(str)).
+ *
+ * @return #EINA_TRUE if the strings were different and thus replaced,
+ *         #EINA_FALSE if the strings were the same after shared.
+ */
+static inline Eina_Bool
+eina_stringshare_replace_length(const char **p_str, const char *news, unsigned int slen)
+{
+   if (*p_str == news) return EINA_FALSE;
+
+   news = eina_stringshare_add_length(news, slen);
+   eina_stringshare_del(*p_str);
+   if (*p_str == news)
+     return EINA_FALSE;
+   *p_str = news;
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STRINGSHARE_INLINE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_inline_tiler.x b/tests/suite/ecore/src/include/eina_inline_tiler.x
new file mode 100644 (file)
index 0000000..ffc34c1
--- /dev/null
@@ -0,0 +1,182 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2009 Rafael Antognolli
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_TILER_INLINE_H_
+#define EINA_TILER_INLINE_H_
+
+#include "eina_safety_checks.h"
+
+/**
+ * @cond LOCAL
+ * This struct should not be accessed directly, it is used by
+ * eina_tile_grid_slicer functions to maintain context and fill "info"
+ * member with correct values for given iteration.
+ */
+struct _Eina_Tile_Grid_Slicer
+{
+   unsigned long col1, col2, row1, row2; // initial and final col,row
+   int tile_w, tile_h; // tile width, height
+   int x_rel, y_rel; // starting x,y coordinates of the first col,row
+   int w1_rel, h1_rel; // width,height of the first col,row
+   int w2_rel, h2_rel; // width,height of the last col,row
+   struct Eina_Tile_Grid_Info info; // info about the current tile
+   Eina_Bool first;
+};
+
+/**
+ * @endcond
+ */
+
+/**
+ * @brief Iterates over the tiles set by eina_tile_grid_slicer_setup().
+ *
+ * @param   slc Pointer to an Eina_Tile_Grid_Slicer struct.
+ * @param   rect Pointer to a struct Eina_Tile_Grid_Info *.
+ * @return  @c EINA_TRUE if the current rect is valid.
+ *          @c EINA_FALSE if there is no more rects to iterate over (and
+ *            thus the current one isn't valid).
+ *
+ * This functions iterates over each Eina_Tile_Grid_Info *rect of the grid.
+ * eina_tile_grid_slicer_setup() must be called first, and *rect is only valid
+ * if this function returns EINA_TRUE. Its content shouldn't be modified.
+ */
+static inline Eina_Bool
+eina_tile_grid_slicer_next(Eina_Tile_Grid_Slicer *slc, const Eina_Tile_Grid_Info **rect)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(slc, 0);
+
+   if (slc->first)
+     {
+       slc->first = 0;
+       *rect = &slc->info;
+       return EINA_TRUE;
+     }
+
+   slc->info.col++;
+
+   if (slc->info.col > slc->col2)
+     {
+       slc->info.row++;
+       if (slc->info.row > slc->row2)
+         return EINA_FALSE;
+       else if (slc->info.row < slc->row2)
+         slc->info.rect.h = slc->tile_h;
+       else
+         slc->info.rect.h = slc->h2_rel;
+       slc->info.rect.y = 0;
+       slc->info.col = slc->col1;
+       slc->info.rect.x = slc->x_rel;
+       slc->info.rect.w = slc->w1_rel;
+     }
+   else
+     {
+       slc->info.rect.x = 0;
+       if (slc->info.col < slc->col2)
+         slc->info.rect.w = slc->tile_w;
+       else
+         slc->info.rect.w = slc->w2_rel;
+     }
+
+   if (slc->info.rect.w == slc->tile_w && slc->info.rect.h == slc->tile_h)
+     slc->info.full = EINA_TRUE;
+   else
+     slc->info.full = EINA_FALSE;
+
+   *rect = &slc->info;
+
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Setup an Eina_Tile_Grid_Slicer struct.
+ *
+ * @param   slc Pointer to an Eina_Tile_Grid_Slicer struct.
+ * @param   x X axis coordinate.
+ * @param   y Y axis coordinate.
+ * @param   w width.
+ * @param   h height.
+ * @param   tile_w tile width.
+ * @param   tile_h tile height.
+ * @return  A pointer to the Eina_Iterator.
+ *          @c NULL on failure.
+ *
+ * This function splits the rectangle given as argument into tiles of size
+ * tile_w X tile_h, and returns an iterator to them. The geometry of each
+ * tile can be accessed with eina_tile_grid_slicer_next, where rect
+ * will be a pointer to a struct Eina_Tile_Grid_Info.
+ */
+static inline Eina_Bool
+eina_tile_grid_slicer_setup(Eina_Tile_Grid_Slicer *slc, int x, int y, int w, int h, int tile_w, int tile_h)
+{
+   int tx1, tx2, ty1, ty2;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(slc, 0);
+
+   tx1 = x;
+   ty1 = y;
+   tx2 = x + w - 1;
+   ty2 = y + h - 1;
+
+   if (x < 0 || y < 0 || w <= 0 || h <= 0 || tile_w <= 0 || tile_h <= 0)
+     {
+       slc->first = 0;
+       slc->col1 = slc->row1 = 0;
+       slc->col2 = slc->row2 = 0;
+       slc->info.col = slc->col1;
+       slc->info.row = slc->row1;
+       return EINA_TRUE;
+     }
+
+   slc->col1 = tx1 / tile_w;
+   slc->row1 = ty1 / tile_h;
+   slc->col2 = (tx2 - 0) / tile_w;
+   slc->row2 = (ty2 - 0) / tile_h;
+   slc->x_rel = tx1 % tile_w;
+   slc->y_rel = ty1 % tile_h;
+   slc->w1_rel = tile_w - slc->x_rel;
+   slc->h1_rel = tile_h - slc->y_rel;
+   slc->w2_rel = tx2 % tile_w + 1;
+   slc->h2_rel = ty2 % tile_h + 1;
+
+   slc->tile_w = tile_w;
+   slc->tile_h = tile_h;
+
+   slc->first = 1;
+   slc->info.col = slc->col1;
+   slc->info.row = slc->row1;
+   slc->info.rect.x = slc->x_rel;
+   slc->info.rect.y = slc->y_rel;
+
+   if (slc->info.col == slc->col2)
+     slc->w1_rel = slc->w2_rel - slc->x_rel;
+
+   if (slc->info.row == slc->row2)
+     slc->h1_rel = slc->h2_rel - slc->y_rel;
+
+   slc->info.rect.w = slc->w1_rel;
+   slc->info.rect.h = slc->h1_rel;
+
+   if (slc->info.rect.w == slc->tile_w && slc->info.rect.h == slc->tile_h)
+     slc->info.full = EINA_TRUE;
+   else
+     slc->info.full = EINA_FALSE;
+
+   return EINA_TRUE;
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_trash.x b/tests/suite/ecore/src/include/eina_inline_trash.x
new file mode 100644 (file)
index 0000000..4a50611
--- /dev/null
@@ -0,0 +1,90 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLINE_TRASH_X__
+#define EINA_INLINE_TRASH_X__
+
+/**
+ * @brief Initialize a trash before using it.
+ *
+ * @param trash The trash.
+ *
+ * This function just set to zero the trash to correctly
+ * initialize it.
+ *
+ * @note You can just set *trash to NULL and you will have
+ * the same result.
+ */
+static inline void
+eina_trash_init(Eina_Trash **trash)
+{
+   *trash = NULL;
+}
+
+/**
+ * @brief Push an unused pointer in the trash instead of freeing it.
+ *
+ * @param trash A pointer to an Eina_Trash.
+ * @param data An unused pointer big enougth to put a (void*).
+ *
+ * Instead of freeing a pointer and put pressure on malloc/free
+ * you can push it in a trash for a later use. This function just
+ * provide a fast way to push a now unused pointer into a trash.
+ *
+ * @note Do never use the pointer after insertion or bad things will
+ * happens.
+ *
+ * @note This trash will not resize, nor do anything with the size of
+ * the region pointed by @p data, so it's your duty to manage the size.
+ */
+static inline void
+eina_trash_push(Eina_Trash **trash, void *data)
+{
+   Eina_Trash *tmp;
+
+   tmp = (Eina_Trash *)data;
+   tmp->next = *trash;
+   *trash = tmp;
+}
+
+/**
+ * @brief Pop an available pointer from the trash if possible.
+ *
+ * @param trash A pointer to an Eina_Trash.
+ *
+ * Instead of calling malloc, and putting pressure on malloc/free
+ * you can recycle the content of the trash, if it's not empty.
+ *
+ * @note This trash will not resize, nor do anything with the size of
+ * the region pointed by pointer inside the trash, so it's your duty
+ * to manage the size of the returned pointer.
+ */
+static inline void*
+eina_trash_pop(Eina_Trash **trash)
+{
+   void *tmp;
+
+   tmp = *trash;
+
+   if (*trash)
+     *trash = (*trash)->next;
+
+   return tmp;
+}
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_inline_ustringshare.x b/tests/suite/ecore/src/include/eina_inline_ustringshare.x
new file mode 100644 (file)
index 0000000..ace6fdc
--- /dev/null
@@ -0,0 +1,93 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Gustavo Sverzut Barbieri
+                           Tom Hacohen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_USTRINGSHARE_INLINE_H_
+#define EINA_USTRINGSHARE_INLINE_H_
+
+#include "eina_unicode.h"
+#include "eina_ustringshare.h"
+
+/**
+ * @addtogroup Eina_UStringshare_Group Unicode Stringshare
+ *
+ * @{
+ */
+
+/**
+ * Replace the previously stringshared pointer with new content.
+ *
+ * The string pointed by @a p_str should be previously stringshared or
+ * @c NULL and it will be eina_ustringshare_del(). The new string will
+ * be passed to eina_ustringshare_add() and then assigned to @c *p_str.
+ *
+ * @param p_str pointer to the stringhare to be replaced. Must not be
+ *        @c NULL, but @c *p_str may be @c NULL as it is a valid
+ *        stringshare handle.
+ * @param news new string to be stringshared, may be @c NULL.
+ *
+ * @return #EINA_TRUE if the strings were different and thus replaced,
+ *         #EINA_FALSE if the strings were the same after shared.
+ */
+static inline Eina_Bool
+eina_ustringshare_replace(const Eina_Unicode **p_str, const Eina_Unicode *news)
+{
+   if (*p_str == news) return EINA_FALSE;
+
+   news = eina_ustringshare_add(news);
+   eina_ustringshare_del(*p_str);
+   if (*p_str == news)
+     return EINA_FALSE;
+   *p_str = news;
+   return EINA_TRUE;
+}
+
+/**
+ * Replace the previously stringshared pointer with a new content.
+ *
+ * The string pointed by @a p_str should be previously stringshared or
+ * @c NULL and it will be eina_ustringshare_del(). The new string will
+ * be passed to eina_ustringshare_add_length() and then assigned to @c *p_str.
+ *
+ * @param p_str pointer to the stringhare to be replaced. Must not be
+ *        @c NULL, but @c *p_str may be @c NULL as it is a valid
+ *        stringshare handle.
+ * @param news new string to be stringshared, may be @c NULL.
+ * @param slen The string size (<= strlen(str)).
+ *
+ * @return #EINA_TRUE if the strings were different and thus replaced,
+ *         #EINA_FALSE if the strings were the same after shared.
+ */
+static inline Eina_Bool
+eina_ustringshare_replace_length(const Eina_Unicode **p_str, const Eina_Unicode *news, unsigned int slen)
+{
+   if (*p_str == news) return EINA_FALSE;
+
+   news = eina_ustringshare_add_length(news, slen);
+   eina_ustringshare_del(*p_str);
+   if (*p_str == news)
+     return EINA_FALSE;
+   *p_str = news;
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
+
+#endif /* EINA_USTRINGSHARE_INLINE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_inlist.h b/tests/suite/ecore/src/include/eina_inlist.h
new file mode 100644 (file)
index 0000000..34ee0ed
--- /dev/null
@@ -0,0 +1,115 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_INLIST_H_
+#define EINA_INLIST_H_
+
+#include "eina_types.h"
+#include "eina_iterator.h"
+#include "eina_accessor.h"
+#include <stddef.h>
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Inline_List_Group Inline List
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Inlist
+ * Inlined list type.
+ */
+typedef struct _Eina_Inlist Eina_Inlist;
+
+/**
+ * @struct _Eina_Inlist
+ * Inlined list type.
+ */
+struct _Eina_Inlist
+{
+   Eina_Inlist *next; /**< next node */
+   Eina_Inlist *prev; /**< previous node */
+   Eina_Inlist *last; /**< last node */
+};
+
+#define EINA_INLIST Eina_Inlist __in_list
+#define EINA_INLIST_GET(Inlist) (& ((Inlist)->__in_list))
+#define EINA_INLIST_CONTAINER_GET(ptr, \
+                                  type) ((type *)((char *)ptr - \
+                                                  offsetof(type, __in_list)))
+
+EAPI Eina_Inlist *  eina_inlist_append(Eina_Inlist *in_list,
+                                       Eina_Inlist *in_item) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_prepend(Eina_Inlist *in_list,
+                                        Eina_Inlist *in_item) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_append_relative(Eina_Inlist *in_list,
+                                                Eina_Inlist *in_item,
+                                                Eina_Inlist *in_relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_prepend_relative(Eina_Inlist *in_list,
+                                                 Eina_Inlist *in_item,
+                                                 Eina_Inlist *in_relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_remove(Eina_Inlist *in_list,
+                                       Eina_Inlist *in_item) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_find(Eina_Inlist *in_list,
+                                     Eina_Inlist *in_item) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_promote(Eina_Inlist *list,
+                                        Eina_Inlist *item) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Inlist *  eina_inlist_demote(Eina_Inlist *list,
+                                       Eina_Inlist *item) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+EAPI unsigned int   eina_inlist_count(const Eina_Inlist *list) EINA_WARN_UNUSED_RESULT;
+
+EAPI Eina_Iterator *eina_inlist_iterator_new(const Eina_Inlist *in_list) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Accessor *eina_inlist_accessor_new(const Eina_Inlist *in_list) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+/* This two macros are helpers for the _FOREACH ones, don't use them */
+#define _EINA_INLIST_OFFSET(ref) ((char *)&(ref)->__in_list - (char *)(ref))
+#define _EINA_INLIST_CONTAINER(ref, ptr) (void *)((char *)(ptr) - \
+                                                  _EINA_INLIST_OFFSET(ref))
+
+#define EINA_INLIST_FOREACH(list, l) \
+   for (l = NULL, l = (list ? _EINA_INLIST_CONTAINER(l, list) : NULL); l; \
+        l = (EINA_INLIST_GET(l)->next ? _EINA_INLIST_CONTAINER(l, EINA_INLIST_GET(l)->next) : NULL))
+#define EINA_INLIST_REVERSE_FOREACH(list, l) \
+   for (l = NULL, l = (list ? _EINA_INLIST_CONTAINER(l, list->last) : NULL); \
+        l; l = (EINA_INLIST_GET(l)->prev ? _EINA_INLIST_CONTAINER(l, EINA_INLIST_GET(l)->prev) : NULL))
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /*EINA_INLIST_H_*/
diff --git a/tests/suite/ecore/src/include/eina_iterator.h b/tests/suite/ecore/src/include/eina_iterator.h
new file mode 100644 (file)
index 0000000..15188be
--- /dev/null
@@ -0,0 +1,141 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_ITERATOR_H__
+#define EINA_ITERATOR_H__
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_magic.h"
+
+/**
+ * @addtogroup Eina_Content_Access_Group Content Access
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Iterator_Group Iterator Functions
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Iterator
+ * Type for iterators.
+ */
+typedef struct _Eina_Iterator Eina_Iterator;
+
+typedef Eina_Bool (*Eina_Iterator_Next_Callback)(Eina_Iterator *it, void **data);
+typedef void *(*Eina_Iterator_Get_Container_Callback)(Eina_Iterator *it);
+typedef void (*Eina_Iterator_Free_Callback)(Eina_Iterator *it);
+typedef Eina_Bool (*Eina_Iterator_Lock_Callback)(Eina_Iterator *it);
+
+struct _Eina_Iterator
+{
+#define EINA_ITERATOR_VERSION 1
+   int version;
+
+   Eina_Iterator_Next_Callback next                   EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+   Eina_Iterator_Get_Container_Callback get_container EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+   Eina_Iterator_Free_Callback free                   EINA_ARG_NONNULL(1);
+
+   Eina_Iterator_Lock_Callback lock   EINA_WARN_UNUSED_RESULT;
+   Eina_Iterator_Lock_Callback unlock EINA_WARN_UNUSED_RESULT;
+
+#define EINA_MAGIC_ITERATOR 0x98761233
+   EINA_MAGIC
+};
+
+
+#define FUNC_ITERATOR_NEXT(Function) ((Eina_Iterator_Next_Callback)Function)
+#define FUNC_ITERATOR_GET_CONTAINER(Function) (( \
+                                                  Eina_Iterator_Get_Container_Callback) \
+                                               Function)
+#define FUNC_ITERATOR_FREE(Function) ((Eina_Iterator_Free_Callback)Function)
+#define FUNC_ITERATOR_LOCK(Function) ((Eina_Iterator_Lock_Callback)Function)
+
+EAPI void      eina_iterator_free           (Eina_Iterator *iterator) EINA_ARG_NONNULL(1);
+
+EAPI void *    eina_iterator_container_get (Eina_Iterator *iterator) EINA_ARG_NONNULL(1) EINA_PURE;
+EAPI Eina_Bool eina_iterator_next      (Eina_Iterator *iterator,
+                                        void **data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+
+EAPI void      eina_iterator_foreach        (Eina_Iterator *iterator,
+                                             Eina_Each_Cb callback,
+                                             const void *fdata) EINA_ARG_NONNULL(1, 2);
+
+EAPI Eina_Bool eina_iterator_lock(Eina_Iterator *iterator) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool eina_iterator_unlock(Eina_Iterator *iterator) EINA_ARG_NONNULL(1);
+
+/**
+ * @def EINA_ITERATOR_FOREACH
+ * @brief Macro to iterate over all elements easily.
+ *
+ * @param itr The iterator to use.
+ * @param data Where to store * data, must be a pointer support getting
+ *        its address since * eina_iterator_next() requires a pointer
+ *        to pointer!
+ *
+ * This macro is a convenient way to use iterators, very similar to
+ * EINA_LIST_FOREACH().
+ *
+ * This macro can be used for freeing the data of a list, like in the
+ * following example. It has the same goal as the one documented in
+ * EINA_LIST_FOREACH(), but using iterators:
+ *
+ * @code
+ * Eina_List     *list;
+ * Eina_Iterator *itr;
+ * char          *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings
+ *
+ * itr = eina_list_iterator_new(list);
+ * EINA_ITERATOR_FOREACH(itr, data)
+ *   free(data);
+ * eina_iterator_free(itr);
+ * eina_list_free(list);
+ * @endcode
+ *
+ * @note this example is not optimal algorithm to release a list since
+ *    it will walk the list twice, but it serves as an example. For
+ *    optimized version use EINA_LIST_FREE()
+ *
+ * @warning unless explicitly stated in functions returning iterators,
+ *    do not modify the iterated object while you walk it, in this
+ *    example using lists, do not remove list nodes or you might
+ *    crash!  This is not a limitiation of iterators themselves,
+ *    rather in the iterators implementations to keep them as simple
+ *    and fast as possible.
+ */
+#define EINA_ITERATOR_FOREACH(itr, \
+                              data) while (eina_iterator_next((itr), \
+                                                              (void **)&(data)))
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_lalloc.h b/tests/suite/ecore/src/include/eina_lalloc.h
new file mode 100644 (file)
index 0000000..d41a9ed
--- /dev/null
@@ -0,0 +1,60 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_LALLOC_H_
+#define EINA_LALLOC_H_
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Lalloc_Group Lazy allocator
+ *
+ * @{
+ */
+
+typedef Eina_Bool (*Eina_Lalloc_Alloc)(void *user_data, int num);
+#define EINA_LALLOC_ALLOC(function) ((Eina_Lalloc_Alloc)function)
+typedef void (*Eina_Lalloc_Free)(void *user_data);
+#define EINA_LALLOC_FREE(function) ((Eina_Lalloc_Free)function)
+
+typedef struct _Eina_Lalloc Eina_Lalloc;
+
+EAPI Eina_Lalloc *eina_lalloc_new(void *data,
+                                  Eina_Lalloc_Alloc alloc_cb,
+                                  Eina_Lalloc_Free free_cb,
+                                  int num_init) EINA_ARG_NONNULL(2, 3);
+EAPI void         eina_lalloc_free(Eina_Lalloc *a) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool    eina_lalloc_elements_add(Eina_Lalloc *a,
+                                           int num) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool    eina_lalloc_element_add(Eina_Lalloc *a) EINA_ARG_NONNULL(1);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_LALLOC_H_ */
diff --git a/tests/suite/ecore/src/include/eina_list.h b/tests/suite/ecore/src/include/eina_list.h
new file mode 100644 (file)
index 0000000..835422d
--- /dev/null
@@ -0,0 +1,349 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_LIST_H_
+#define EINA_LIST_H_
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_iterator.h"
+#include "eina_accessor.h"
+#include "eina_magic.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_List_Group List
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_List
+ * Type for a generic double linked list.
+ */
+typedef struct _Eina_List Eina_List;
+
+typedef struct _Eina_List_Accounting Eina_List_Accounting;
+
+/**
+ * @struct _Eina_List
+ * Type for a generic double linked list.
+ */
+struct _Eina_List
+{
+   void *data; /**< Pointer to list element payload */
+   Eina_List *next; /**< Next member in the list */
+   Eina_List *prev; /**< Previous member in the list */
+   Eina_List_Accounting *accounting; /**< Private list accounting info - don't touch */
+
+   EINA_MAGIC
+};
+
+struct _Eina_List_Accounting
+{
+   Eina_List *last;
+   unsigned int count;
+   EINA_MAGIC
+};
+
+EAPI Eina_List *               eina_list_append (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_prepend (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_append_relative (Eina_List *list, const void *data, const void *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_append_relative_list (Eina_List *list, const void *data, Eina_List *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_prepend_relative (Eina_List *list, const void *data, const void *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_prepend_relative_list (Eina_List *list, const void *data, Eina_List *relative) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_sorted_insert(Eina_List *list, Eina_Compare_Cb func, const void *data) EINA_ARG_NONNULL(2,                     3) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_remove (Eina_List *list, const void *data) EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_remove_list (Eina_List *list, Eina_List *remove_list) EINA_ARG_NONNULL(   2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_promote_list (Eina_List *list, Eina_List *move_list) EINA_ARG_NONNULL(   2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_demote_list (Eina_List *list, Eina_List *move_list);
+EAPI void *                    eina_list_data_find(const Eina_List *list, const void *data) EINA_PURE EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_data_find_list (const Eina_List *list, const void *data) EINA_PURE EINA_ARG_NONNULL(2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_free (Eina_List *list);
+EAPI void *                    eina_list_nth(const Eina_List *list, unsigned int n) EINA_PURE EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_nth_list (const Eina_List *list, unsigned int n) EINA_PURE EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_reverse (Eina_List *list) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_reverse_clone(const Eina_List *list) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_clone(const Eina_List *list) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_sort (Eina_List *list, unsigned int size, Eina_Compare_Cb func) EINA_ARG_NONNULL(3) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_merge (Eina_List *left, Eina_List *right) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_sorted_merge(Eina_List *left, Eina_List *right, Eina_Compare_Cb func) EINA_ARG_NONNULL(3) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_List *               eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right) EINA_WARN_UNUSED_RESULT;
+
+
+EAPI Eina_List *               eina_list_search_sorted_near_list(const Eina_List *list, Eina_Compare_Cb func, const void *data, int *result_cmp);
+EAPI Eina_List *               eina_list_search_sorted_list(const Eina_List *list, Eina_Compare_Cb func, const void *data);
+EAPI void *                    eina_list_search_sorted(const Eina_List *list, Eina_Compare_Cb func, const void *data);
+EAPI Eina_List *               eina_list_search_unsorted_list(const Eina_List *list, Eina_Compare_Cb func, const void *data);
+EAPI void *                    eina_list_search_unsorted(const Eina_List *list, Eina_Compare_Cb func, const void *data);
+
+static inline Eina_List *               eina_list_last (const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT;
+static inline Eina_List *               eina_list_next (const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT;
+static inline Eina_List *               eina_list_prev (const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT;
+static inline void *                    eina_list_data_get(const Eina_List *list) EINA_PURE EINA_WARN_UNUSED_RESULT;
+static inline unsigned int              eina_list_count(const Eina_List *list) EINA_PURE;
+
+EAPI Eina_Iterator *               eina_list_iterator_new(const Eina_List *list) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Iterator *               eina_list_iterator_reversed_new(const Eina_List *list) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Accessor *               eina_list_accessor_new(const Eina_List *list) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @def EINA_LIST_FOREACH
+ * @brief Macro to iterate over a list.
+ *
+ * @param list The list to iterate over.
+ * @param l A list that is used as an iterator and points to the current node.
+ * @param data Current item's data.
+ *
+ * This macro iterates over @p list from the first element to
+ * the last. @p data is the data related to the current element.
+ * @p l is an #Eina_List used as the list iterator.
+ *
+ * It can be used to free list data, as in the following example:
+ *
+ * @code
+ * Eina_List *list;
+ * Eina_List *l;
+ * char      *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings,
+ * // EINA_LIST_FOREACH will be used to free those strings
+ *
+ * EINA_LIST_FOREACH(list, l, data)
+ *   free(data);
+ * eina_list_free(list);
+ * @endcode
+ *
+ * @note This is not the optimal way to release memory allocated to
+ *       a list, since it iterates over the list twice.
+ *       For an optimized algorithm, use EINA_LIST_FREE().
+ *
+ * @warning Be careful when deleting list nodes.
+ *          If you remove the current node and continue iterating,
+ *          the code will fail because the macro will not be able
+ *          to get the next node. Notice that it's OK to remove any
+ *          node if you stop the loop after that.
+ *          For destructive operations such as this, consider
+ *          using EINA_LIST_FOREACH_SAFE().
+ */
+#define EINA_LIST_FOREACH(list, l, data)       \
+  for (l = list,                               \
+        data = eina_list_data_get(l);          \
+       l;                                      \
+       l = eina_list_next(l),                  \
+        data = eina_list_data_get(l))
+
+/**
+ * @def EINA_LIST_REVERSE_FOREACH
+ * @brief Macro to iterate over a list in the reverse order.
+ *
+ * @param list The list to iterate over.
+ * @param l A list that is used as an iterator and points to the current node.
+ * @param data Current item's data.
+ *
+ * This macro works like EINA_LIST_FOREACH, but iterates from the
+ * last element of a list to the first.
+ * @p data is the data related to the current element, while @p l
+ * is an #Eina_List that is used as the list iterator.
+ *
+ * It can be used to free list data, as in the following example:
+ *
+ * @code
+ * Eina_List *list;
+ * Eina_List *l;
+ * char      *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings,
+ * // EINA_LIST_REVERSE_FOREACH will be used to free those strings
+ *
+ * EINA_LIST_REVERSE_FOREACH(list, l, data)
+ *   free(data);
+ * eina_list_free(list);
+ * @endcode
+ *
+ * @note This is not the optimal way to release memory allocated to
+ *       a list, since it iterates over the list twice.
+ *       For an optimized algorithm, use EINA_LIST_FREE().
+ *
+ * @warning Be careful when deleting list nodes.
+ *          If you remove the current node and continue iterating,
+ *          the code will fail because the macro will not be able
+ *          to get the next node. Notice that it's OK to remove any
+ *          node if you stop the loop after that.
+ *          For destructive operations such as this, consider
+ *          using EINA_LIST_REVERSE_FOREACH_SAFE().
+ */
+#define EINA_LIST_REVERSE_FOREACH(list, l, data)       \
+  for (l = eina_list_last(list),                       \
+        data = eina_list_data_get(l);                  \
+       l;                                              \
+       l = eina_list_prev(l),                          \
+        data = eina_list_data_get(l))
+
+/**
+ * @def EINA_LIST_FOREACH_SAFE
+ * @brief Macro to iterate over a list with support for node deletion.
+ *
+ * @param list The list to iterate over.
+ * @param l A list that is used as an iterator and points to the current node.
+ * @param l_next A list that is used as an iterator and points to the next node.
+ * @param data Current item's data.
+ *
+ * This macro iterates over @p list from the first element to
+ * the last. @p data is the data related to the current element.
+ * @p l is an #Eina_List used as the list iterator.
+ *
+ * Since this macro stores a pointer to the next list node in @p l_next,
+ * deleting the current node and continuing looping is safe.
+ *
+ * This macro can be used to free list nodes, as in the following example:
+ *
+ * @code
+ * Eina_List *list;
+ * Eina_List *l;
+ * Eina_List *l_next;
+ * char      *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings,
+ * // EINA_LIST_FOREACH_SAFE will be used to free elements that match "key".
+ *
+ * EINA_LIST_FOREACH_SAFE(list, l, l_next, data)
+ *   if (strcmp(data, "key") == 0) {
+ *      free(data);
+ *      list = eina_list_remove_list(list, l);
+ *   }
+ * @endcode
+ */
+#define EINA_LIST_FOREACH_SAFE(list, l, l_next, data)  \
+  for (l = list,                                       \
+        l_next = eina_list_next(l),                    \
+        data = eina_list_data_get(l);                  \
+       l;                                              \
+       l = l_next,                                     \
+        l_next = eina_list_next(l),                    \
+        data = eina_list_data_get(l))
+
+/**
+ * @def EINA_LIST_REVERSE_FOREACH_SAFE
+ * @brief Macro to iterate over a list in the reverse order with support
+ *        for deletion.
+ *
+ * @param list The list to iterate over.
+ * @param l A list that is used as an iterator and points to the current node.
+ * @param l_prev A list that is used as an iterator and points to the previous node.
+ * @param data Current item's data.
+ *
+ * This macro works like EINA_LIST_FOREACH_SAFE, but iterates from the
+ * last element of a list to the first.
+ * @p data is the data related to the current element, while @p l
+ * is an #Eina_List that is used as the list iterator.
+ *
+ * Since this macro stores a pointer to the previous list node in @p l_prev,
+ * deleting the current node and continuing looping is safe.
+ *
+ * This macro can be used to free list nodes, as in the following example:
+ *
+ * @code
+ * Eina_List *list;
+ * Eina_List *l;
+ * Eina_List *l_prev;
+ * char       *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings,
+ * // EINA_LIST_REVERSE_FOREACH_SAFE will be used to free elements that match "key".
+ *
+ * EINA_LIST_REVERSE_FOREACH_SAFE(list, l, l_prev, data)
+ *   if (strcmp(data, "key") == 0) {
+ *      free(data);
+ *      list = eina_list_remove_list(list, l);
+ *   }
+ * @endcode
+ */
+#define EINA_LIST_REVERSE_FOREACH_SAFE(list, l, l_prev, data)  \
+  for (l = eina_list_last(list),                               \
+        l_prev = eina_list_prev(l),                            \
+        data = eina_list_data_get(l);                          \
+       l;                                                      \
+       l = l_prev,                                             \
+        l_prev = eina_list_prev(l),                            \
+        data = eina_list_data_get(l))
+
+/**
+ * @def EINA_LIST_FREE
+ * @brief Macro to remove each list node while having access to each node's data.
+ *
+ * @param list The list that will be cleared.
+ * @param data Current node's data.
+ *
+ * This macro will call #eina_list_remove_list for each list node, and store
+ * the data contained in the current node in @p data.
+ *
+ * If you do not need to release node data, it is easier to call #eina_list_free().
+ *
+ * @code
+ * Eina_List *list;
+ * char      *data;
+ *
+ * // list is already filled,
+ * // its elements are just duplicated strings,
+ *
+ * EINA_LIST_FREE(list, data)
+ *   free(data);
+ * @endcode
+ *
+ * @see eina_list_free()
+ */
+#define EINA_LIST_FREE(list, data)                     \
+  for (data = eina_list_data_get(list);                        \
+       list;                                           \
+       list = eina_list_remove_list(list, list),       \
+        data = eina_list_data_get(list))
+
+#include "eina_inline_list.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_LIST_H_ */
diff --git a/tests/suite/ecore/src/include/eina_log.h b/tests/suite/ecore/src/include/eina_log.h
new file mode 100644 (file)
index 0000000..d0fd159
--- /dev/null
@@ -0,0 +1,387 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_LOG_H_
+#define EINA_LOG_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "eina_types.h"
+
+#define EINA_COLOR_LIGHTRED  "\033[31;1m"
+#define EINA_COLOR_RED       "\033[31m"
+#define EINA_COLOR_LIGHTBLUE "\033[34;1m"
+#define EINA_COLOR_BLUE      "\033[34m"
+#define EINA_COLOR_GREEN     "\033[32;1m"
+#define EINA_COLOR_YELLOW    "\033[33;1m"
+#define EINA_COLOR_ORANGE    "\033[0;33m"
+#define EINA_COLOR_WHITE     "\033[37;1m"
+#define EINA_COLOR_LIGHTCYAN "\033[36;1m"
+#define EINA_COLOR_CYAN      "\033[36m"
+#define EINA_COLOR_RESET     "\033[0m"
+#define EINA_COLOR_HIGH      "\033[1m"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Log_Group Log
+ *
+ * @{
+ */
+
+/**
+ * EINA_LOG_DOMAIN_GLOBAL is the general purpose log domain to be
+ * used, it is always registered and available everywhere.
+ */
+EAPI extern int EINA_LOG_DOMAIN_GLOBAL;
+
+#ifndef EINA_LOG_DOMAIN_DEFAULT
+
+/**
+ * @def EINA_LOG_DOMAIN_DEFAULT
+ * This macro defines the domain to use with the macros EINA_LOG_DOM_DBG(),
+ * EINA_LOG_DOM_INFO(), EINA_LOG_DOM_WARN(), EINA_LOG_DOM_ERR() and
+ * EINA_LOG_DOM_CRIT().
+ *
+ * If not defined prior to the inclusion of this header, then it
+ * defaults to #EINA_LOG_DOMAIN_GLOBAL.
+ *
+ * @note One may like to redefine this in its code to avoid typing too
+ *       much. In this case the recommended way is:
+ *
+ * @code
+ * #include <Eina.h>
+ * #undef EINA_LOG_DOMAIN_DEFAULT
+ * #define EINA_LOG_DOMAIN_DEFAULT _log_dom
+ * static int _log_dom = -1;
+ *
+ * int main(void)
+ * {
+ *    eina_init();
+ *    _log_dom = eina_log_domain_register("mydom", EINA_COLOR_CYAN);
+ *    EINA_LOG_ERR("using my own domain");
+ *    return 0;
+ * }
+ * @endcode
+ *
+ * @warning If one defines the domain prior to inclusion of this
+ *          header, the defined log domain symbol must be defined
+ *          prior as well, otherwise the inlined functions defined by
+ *          Eina will fail to find the symbol, causing build failure.
+ *
+ * @code
+ * #define EINA_LOG_DOMAIN_DEFAULT _log_dom
+ * static int _log_dom = -1; // must come before inclusion of Eina.h!
+ * #include <Eina.h>
+ *
+ * int main(void)
+ * {
+ *    eina_init();
+ *    _log_dom = eina_log_domain_register("mydom", EINA_COLOR_CYAN);
+ *    EINA_LOG_ERR("using my own domain");
+ *    return 0;
+ * }
+ * @endcode
+ *
+ */
+# define EINA_LOG_DOMAIN_DEFAULT EINA_LOG_DOMAIN_GLOBAL
+
+#endif /* EINA_LOG_DOMAIN_DEFAULT */
+
+
+/**
+ * @def EINA_LOG(DOM, LEVEL, fmt, ...)
+ * Logs a message on the specified domain, level and format.
+ *
+ * @note if @c EINA_LOG_LEVEL_MAXIMUM is defined, then messages larger
+ *       than this value will be ignored regardless of current domain
+ *       level, the eina_log_print() is not even called! Most
+ *       compilers will just detect the two integers make the branch
+ *       impossible and remove the branch and function call all
+ *       together. Take this as optimization tip and possible remove
+ *       debug messages from binaries to be deployed, saving on hot
+ *       paths. Never define @c EINA_LOG_LEVEL_MAXIMUM on public
+ *       header files.
+ */
+#ifdef EINA_LOG_LEVEL_MAXIMUM
+#define EINA_LOG(DOM, LEVEL, fmt, ...)                                  \
+  do {                                                                  \
+     if (LEVEL <= EINA_LOG_LEVEL_MAXIMUM) {                            \
+       eina_log_print(DOM, LEVEL, __FILE__, __FUNCTION__, __LINE__,    \
+                      fmt, ## __VA_ARGS__); }                          \
+  } while (0)
+#else
+#define EINA_LOG(DOM, LEVEL, fmt, ...)         \
+  eina_log_print(DOM,                          \
+                LEVEL,                         \
+                __FILE__,                      \
+                __FUNCTION__,                  \
+                __LINE__,                      \
+                fmt,                           \
+                ## __VA_ARGS__)
+#endif
+
+/**
+ * @def EINA_LOG_DOM_CRIT(DOM, fmt, ...)
+ * Logs a message with level CRITICAL on the specified domain and format.
+ */
+#define EINA_LOG_DOM_CRIT(DOM, fmt, ...) \
+   EINA_LOG(DOM, EINA_LOG_LEVEL_CRITICAL, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_DOM_ERR(DOM, fmt, ...)
+ * Logs a message with level ERROR on the specified domain and format.
+ */
+#define EINA_LOG_DOM_ERR(DOM, fmt, ...) \
+   EINA_LOG(DOM, EINA_LOG_LEVEL_ERR, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_DOM_INFO(DOM, fmt, ...)
+ * Logs a message with level INFO on the specified domain and format.
+ */
+#define EINA_LOG_DOM_INFO(DOM, fmt, ...) \
+   EINA_LOG(DOM, EINA_LOG_LEVEL_INFO, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_DOM_DBG(DOM, fmt, ...)
+ * Logs a message with level DEBUG on the specified domain and format.
+ */
+#define EINA_LOG_DOM_DBG(DOM, fmt, ...) \
+   EINA_LOG(DOM, EINA_LOG_LEVEL_DBG, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_DOM_WARN(DOM, fmt, ...)
+ * Logs a message with level WARN on the specified domain and format.
+ */
+#define EINA_LOG_DOM_WARN(DOM, fmt, ...) \
+   EINA_LOG(DOM, EINA_LOG_LEVEL_WARN, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_CRIT(fmt, ...)
+ * Logs a message with level CRITICAL on the default domain with the specified
+ * format.
+ */
+#define EINA_LOG_CRIT(fmt, ...)             \
+   EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, \
+            EINA_LOG_LEVEL_CRITICAL, \
+            fmt,                    \
+            ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_ERR(fmt, ...)
+ * Logs a message with level ERROR on the default domain with the specified
+ * format.
+ */
+#define EINA_LOG_ERR(fmt, ...) \
+   EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_ERR, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_INFO(fmt, ...)
+ * Logs a message with level INFO on the default domain with the specified
+ * format.
+ */
+#define EINA_LOG_INFO(fmt, ...) \
+   EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_INFO, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_WARN(fmt, ...)
+ * Logs a message with level WARN on the default domain with the specified
+ * format.
+ */
+#define EINA_LOG_WARN(fmt, ...) \
+   EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_WARN, fmt, ## __VA_ARGS__)
+
+/**
+ * @def EINA_LOG_DBG(fmt, ...)
+ * Logs a message with level DEBUG on the default domain with the specified
+ * format.
+ */
+#define EINA_LOG_DBG(fmt, ...) \
+   EINA_LOG(EINA_LOG_DOMAIN_DEFAULT, EINA_LOG_LEVEL_DBG, fmt, ## __VA_ARGS__)
+
+/**
+ * @typedef Eina_Log_Domain
+ * The domain used for logging.
+ */
+typedef struct _Eina_Log_Domain Eina_Log_Domain;
+
+/**
+ * @struct _Eina_Log_Domain
+ * The domain used for logging.
+ */
+struct _Eina_Log_Domain
+{
+   int level; /**< Max level to log */
+   const char *domain_str; /**< Formatted string with color to print */
+   const char *name; /**< Domain name */
+   size_t namelen; /**< strlen(name) */
+
+   /* Private */
+   Eina_Bool deleted : 1; /**< Flags deletion of domain, a free slot */
+};
+
+EAPI void eina_log_threads_enable(void);
+
+/**
+ * @enum _Eina_Log_Level
+ * List of available logging levels.
+ */
+typedef enum _Eina_Log_Level
+{
+   EINA_LOG_LEVEL_CRITICAL, /**< Critical log level */
+   EINA_LOG_LEVEL_ERR, /**< Error log level */
+   EINA_LOG_LEVEL_WARN, /**< Warning log level */
+   EINA_LOG_LEVEL_INFO, /**< Information log level */
+   EINA_LOG_LEVEL_DBG, /**< Debug log level */
+   EINA_LOG_LEVELS, /**< Count of default log levels */
+   EINA_LOG_LEVEL_UNKNOWN = (-2147483647 - 1) /**< Unknown level */
+} Eina_Log_Level;
+
+/**
+ * @typedef Eina_Log_Print_Cb
+ * Type for print callbacks.
+ */
+typedef void (*Eina_Log_Print_Cb)(const Eina_Log_Domain *d,
+                                  Eina_Log_Level level,
+                                  const char *file, const char *fnc, int line,
+                                  const char *fmt, void *data, va_list args);
+
+/*
+ * Customization
+ */
+EAPI void
+eina_log_print_cb_set(Eina_Log_Print_Cb cb, void *data) EINA_ARG_NONNULL(1);
+
+EAPI void
+eina_log_level_set(int level);
+EAPI int
+eina_log_level_get(void) EINA_WARN_UNUSED_RESULT;
+
+static inline Eina_Bool
+eina_log_level_check(int level);
+
+EAPI Eina_Bool
+eina_log_main_thread_check(void) EINA_CONST EINA_WARN_UNUSED_RESULT;
+
+EAPI void
+eina_log_color_disable_set(Eina_Bool disabled);
+EAPI Eina_Bool
+eina_log_color_disable_get(void) EINA_WARN_UNUSED_RESULT;
+EAPI void
+eina_log_file_disable_set(Eina_Bool disabled);
+EAPI Eina_Bool
+eina_log_file_disable_get(void) EINA_WARN_UNUSED_RESULT;
+EAPI void
+eina_log_function_disable_set(Eina_Bool disabled);
+EAPI Eina_Bool
+eina_log_function_disable_get(void) EINA_WARN_UNUSED_RESULT;
+EAPI void
+eina_log_abort_on_critical_set(Eina_Bool abort_on_critical);
+EAPI Eina_Bool
+eina_log_abort_on_critical_get(void) EINA_WARN_UNUSED_RESULT;
+EAPI void
+eina_log_abort_on_critical_level_set(int critical_level);
+EAPI int
+eina_log_abort_on_critical_level_get(void) EINA_WARN_UNUSED_RESULT;
+
+EAPI void
+eina_log_domain_level_set(const char *domain_name, int level) EINA_ARG_NONNULL(1);
+EAPI int
+eina_log_domain_level_get(const char *domain_name) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI int
+eina_log_domain_registered_level_get(int domain) EINA_WARN_UNUSED_RESULT;
+static inline Eina_Bool
+eina_log_domain_level_check(int domain, int level);
+
+
+/*
+ * Logging domains
+ */
+EAPI int
+eina_log_domain_register(const char *name, const char *color) EINA_ARG_NONNULL(1);
+EAPI void
+eina_log_domain_unregister(int domain);
+
+/*
+ * Logging functions.
+ */
+EAPI void
+eina_log_print(int domain,
+               Eina_Log_Level level,
+               const char *file,
+               const char *function,
+               int line,
+               const char *fmt,
+               ...) EINA_ARG_NONNULL(3, 4, 6) EINA_PRINTF(6, 7) EINA_NOINSTRUMENT;
+EAPI void
+eina_log_vprint(int domain,
+                Eina_Log_Level level,
+                const char *file,
+                const char *fnc,
+                int line,
+                const char *fmt,
+                va_list args) EINA_ARG_NONNULL(3, 4, 6) EINA_NOINSTRUMENT;
+
+
+/*
+ * Logging methods (change how logging is done).
+ */
+EAPI void
+eina_log_print_cb_stdout(const Eina_Log_Domain *d,
+                         Eina_Log_Level level,
+                         const char *file,
+                         const char *fnc,
+                         int line,
+                         const char *fmt,
+                         void *data,
+                         va_list args);
+EAPI void
+eina_log_print_cb_stderr(const Eina_Log_Domain *d,
+                         Eina_Log_Level level,
+                         const char *file,
+                         const char *fnc,
+                         int line,
+                         const char *fmt,
+                         void *data,
+                         va_list args);
+EAPI void
+eina_log_print_cb_file(const Eina_Log_Domain *d,
+                       Eina_Log_Level level,
+                       const char *file,
+                       const char *fnc,
+                       int line,
+                       const char *fmt,
+                       void *data,
+                       va_list args);
+
+#include "eina_inline_log.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_LOG_H_ */
diff --git a/tests/suite/ecore/src/include/eina_magic.h b/tests/suite/ecore/src/include/eina_magic.h
new file mode 100644 (file)
index 0000000..dd1a9f3
--- /dev/null
@@ -0,0 +1,153 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_MAGIC_H_
+#define EINA_MAGIC_H_
+
+
+#include "eina_config.h"
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Magic_Group Magic
+ *
+ * @{
+ */
+
+typedef unsigned int Eina_Magic;
+
+/**
+ * @typedef Eina_Magic
+ * An abstract type for a magic number.
+ */
+EAPI const char *eina_magic_string_get(Eina_Magic magic) EINA_PURE EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Bool   eina_magic_string_set(Eina_Magic magic,
+                                       const char *magic_name) EINA_ARG_NONNULL(2);
+EAPI Eina_Bool   eina_magic_string_static_set(Eina_Magic magic,
+                                              const char *magic_name) EINA_ARG_NONNULL(2);
+
+/**
+ * @def EINA_MAGIC_NONE
+ * Random value for specifying that a structure using the magic
+ * feature has already been freed. It is used by eina_magic_fail().
+ *
+ * If the magic feature of Eina is disabled, #EINA_MAGIC_NONE is just
+ * @c 0.
+ */
+#define EINA_MAGIC_NONE            0x1234fedc
+
+#ifdef EINA_MAGIC_DEBUG
+
+/**
+ * @def EINA_MAGIC
+ * Declaration of a variable of type #Eina_Magic. To put in a structure
+ * when one wants to use the magic feature of Eina with the functions
+ * of that structure, like that:
+ *
+ * @code
+ * struct Foo
+ * {
+ *    int i;
+ *
+ *    EINA_MAGIC
+ * };
+ * @endcode
+ *
+ * If the magic feature of Eina is disabled, #EINA_MAGIC does nothing.
+ */
+#define EINA_MAGIC      Eina_Magic __magic;
+
+/**
+ * @def EINA_MAGIC_SET(d, m)
+ * Set the magic number of @p d to @p m. @p d must be a valid pointer
+ * to a structure holding an Eina magic number declaration. Use
+ * #EINA_MAGIC to add such declaration.
+ *
+ * If the magic feature of Eina is disabled, #EINA_MAGIC_CHECK is just
+ * the value @c 0.
+ */
+#define EINA_MAGIC_SET(d, m)       (d)->__magic = (m)
+
+/**
+ * @def EINA_MAGIC_CHECK(d, m)
+ * Test if @p d is @c NULL or not, and if not @c NULL, if
+ * @p d->__eina_magic is equal to @p m. @p d must be a structure that
+ * holds an Eina magic number declaration. Use #EINA_MAGIC to add such
+ * declaration.
+ *
+ * If the magic feature of Eina is disabled, #EINA_MAGIC_CHECK is just
+ * the value @c 1.
+ */
+#define EINA_MAGIC_CHECK(d, m)     ((d) && ((d)->__magic == (m)))
+
+/**
+ * @def EINA_MAGIC_FAIL(d, m)
+ * Call eina_magic_fail() with the parameters @p d, @p d->__magic, @p
+ * m, __FILE__, __FUNCTION__ and __LINE__. @p d must be a structure that
+ * holds an Eina magic number declaration. Use #EINA_MAGIC to add such
+ * declaration.
+ *
+ * If the magic feature of Eina is disabled, #EINA_MAGIC_FAIL does
+ * nothing.
+ */
+#define EINA_MAGIC_FAIL(d, m)                  \
+  eina_magic_fail((void *)(d),                 \
+                 (d) ? (d)->__magic : 0,       \
+                 (m),                          \
+                 __FILE__,                     \
+                 __FUNCTION__,                 \
+                 __LINE__);
+
+EAPI void eina_magic_fail(void *d, Eina_Magic m, Eina_Magic req_m,
+                          const char *file, const char *fnc,
+                          int line) EINA_ARG_NONNULL(4, 5);
+
+#else
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_MAGIC
+#define EINA_MAGIC_SET(d, m)       ((void)0)
+#define EINA_MAGIC_CHECK(d, m)     (1)
+#define EINA_MAGIC_FAIL(d, m)      ((void)0)
+
+#define eina_magic_fail(d, m, req_m, file, fnx, line) ((void)0)
+
+/**
+ * @endcond
+ */
+
+#endif
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_MAGIC_H_ */
diff --git a/tests/suite/ecore/src/include/eina_main.h b/tests/suite/ecore/src/include/eina_main.h
new file mode 100644 (file)
index 0000000..db80042
--- /dev/null
@@ -0,0 +1,75 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_MAIN_H_
+#define EINA_MAIN_H_
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Core_Group Core
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Main_Group Main
+ *
+ * @{
+ */
+
+/**
+ * @def EINA_VERSION_MAJOR
+ * @brief Major version of Eina
+ */
+#define EINA_VERSION_MAJOR 1
+
+/**
+ * @def EINA_VERSION_MINOR
+ * @brief Minor version of Eina
+ */
+#define EINA_VERSION_MINOR 0
+
+/**
+ * @typedef Eina_Version
+ * The version of Eina.
+ */
+typedef struct _Eina_Version
+{
+   int major;    /**< Major component of the version */
+   int minor;    /**< Minor component of the version */
+   int micro;    /**< Micro component of the version */
+   int revision; /**< Revision component of the version */
+} Eina_Version;
+
+EAPI extern Eina_Version *eina_version;
+
+EAPI int eina_init(void);
+EAPI int eina_shutdown(void);
+EAPI int eina_threads_init(void);
+EAPI int eina_threads_shutdown(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_MAIN_H_ */
diff --git a/tests/suite/ecore/src/include/eina_matrixsparse.h b/tests/suite/ecore/src/include/eina_matrixsparse.h
new file mode 100644 (file)
index 0000000..cbf29cc
--- /dev/null
@@ -0,0 +1,120 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2009 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_MATRIXSPARSE_H_
+#define EINA_MATRIXSPARSE_H_
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_iterator.h"
+#include "eina_accessor.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Matrixsparse_Group Sparse Matrix
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Matrixsparse
+ * Type for a generic sparse matrix.
+ */
+typedef struct _Eina_Matrixsparse Eina_Matrixsparse;
+
+/**
+ * @typedef Eina_Matrixsparse_Row
+ * Type for a generic sparse matrix row, opaque for users.
+ */
+typedef struct _Eina_Matrixsparse_Row Eina_Matrixsparse_Row;
+
+/**
+ * @typedef Eina_Matrixsparse_Cell
+ * Type for a generic sparse matrix cell, opaque for users.
+ */
+typedef struct _Eina_Matrixsparse_Cell Eina_Matrixsparse_Cell;
+
+typedef struct _Eina_Matrixsparse_Item_Cell Eina_Matrixsparse_Item_Cell;
+typedef struct _Eina_Matrixsparse_Item_Row Eina_Matrixsparse_Item_Row;
+
+
+/* constructors and destructors */
+EAPI Eina_Matrixsparse *eina_matrixsparse_new(unsigned long rows,
+                                              unsigned long cols,
+                                              void (*free_func)(void *user_data,
+                                                                void *cell_data),
+                                              const void *user_data);
+EAPI void               eina_matrixsparse_free(Eina_Matrixsparse *m);
+
+/* size manipulation */
+EAPI void               eina_matrixsparse_size_get(const Eina_Matrixsparse *m,
+                                                   unsigned long *rows,
+                                                   unsigned long *cols);
+EAPI Eina_Bool          eina_matrixsparse_size_set(Eina_Matrixsparse *m,
+                                                   unsigned long rows,
+                                                   unsigned long cols);
+
+/* data getting */
+EAPI Eina_Bool          eina_matrixsparse_cell_idx_get(const Eina_Matrixsparse *m, unsigned long row, unsigned long col, Eina_Matrixsparse_Cell **cell);
+EAPI void *             eina_matrixsparse_cell_data_get(const Eina_Matrixsparse_Cell *cell);
+EAPI void *             eina_matrixsparse_data_idx_get(const Eina_Matrixsparse *m, unsigned long row, unsigned long col);
+EAPI Eina_Bool          eina_matrixsparse_cell_position_get(const Eina_Matrixsparse_Cell *cell, unsigned long *row, unsigned long *col);
+
+/* data setting */
+EAPI Eina_Bool eina_matrixsparse_cell_data_replace(Eina_Matrixsparse_Cell *cell, const void *data, void **p_old);
+EAPI Eina_Bool eina_matrixsparse_cell_data_set(Eina_Matrixsparse_Cell *cell, const void *data);
+EAPI Eina_Bool eina_matrixsparse_data_idx_replace(Eina_Matrixsparse *m, unsigned long row, unsigned long col, const void *data, void **p_old);
+EAPI Eina_Bool eina_matrixsparse_data_idx_set(Eina_Matrixsparse *m, unsigned long row, unsigned long col, const void *data);
+
+/* data deleting */
+EAPI Eina_Bool      eina_matrixsparse_row_idx_clear(Eina_Matrixsparse *m, unsigned long row);
+EAPI Eina_Bool      eina_matrixsparse_column_idx_clear(Eina_Matrixsparse *m, unsigned long col);
+EAPI Eina_Bool      eina_matrixsparse_cell_idx_clear(Eina_Matrixsparse *m, unsigned long row, unsigned long col);
+EAPI Eina_Bool      eina_matrixsparse_cell_clear(Eina_Matrixsparse_Cell *cell);
+
+/* iterators */
+EAPI Eina_Iterator *eina_matrixsparse_iterator_new(const Eina_Matrixsparse *m);
+EAPI Eina_Iterator *eina_matrixsparse_iterator_complete_new(const Eina_Matrixsparse *m);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_MATRIXSPARSE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_mempool.h b/tests/suite/ecore/src/include/eina_mempool.h
new file mode 100644 (file)
index 0000000..cc19e94
--- /dev/null
@@ -0,0 +1,77 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_MEMPOOL_H_
+#define EINA_MEMPOOL_H_
+
+#include "eina_types.h"
+#include "eina_error.h"
+#include "eina_module.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Memory_Pool_Group Memory Pool
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Mempool
+ * Mempool type.
+ */
+typedef struct _Eina_Mempool Eina_Mempool;
+
+/**
+ * @typedef Eina_Mempool_Backend
+ * Mempool backend type.
+ */
+typedef struct _Eina_Mempool_Backend Eina_Mempool_Backend;
+
+EAPI extern Eina_Error EINA_ERROR_NOT_MEMPOOL_MODULE;
+
+EAPI Eina_Mempool *                     eina_mempool_add(const char *module,const char *context,const char *options,...) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void                               eina_mempool_del(Eina_Mempool *mp) EINA_ARG_NONNULL(1);
+
+static inline void *                    eina_mempool_realloc(Eina_Mempool *mp,void *element,unsigned int size) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline void *                    eina_mempool_malloc(Eina_Mempool *mp,unsigned int size) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline void                      eina_mempool_free(Eina_Mempool *mp,void *element) EINA_ARG_NONNULL(1);
+
+EAPI void                               eina_mempool_gc(Eina_Mempool *mp) EINA_ARG_NONNULL(1);
+EAPI void                               eina_mempool_statistics(Eina_Mempool *mp) EINA_ARG_NONNULL(1);
+
+EAPI Eina_Bool                          eina_mempool_register(Eina_Mempool_Backend *be) EINA_ARG_NONNULL(1);
+EAPI void                               eina_mempool_unregister(Eina_Mempool_Backend *be) EINA_ARG_NONNULL(1);
+
+EAPI unsigned int                       eina_mempool_alignof(unsigned int size);
+
+#include "eina_inline_mempool.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_MEMPOOL_H_ */
diff --git a/tests/suite/ecore/src/include/eina_module.h b/tests/suite/ecore/src/include/eina_module.h
new file mode 100644 (file)
index 0000000..6618381
--- /dev/null
@@ -0,0 +1,150 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_MODULE_H_
+#define EINA_MODULE_H_
+
+#include "eina_types.h"
+#include "eina_array.h"
+#include "eina_error.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Module_Group Module
+ *
+ * Eina module provides some helpers over POSIX dlopen(). It is not
+ * meant to replace, abstract or make a "portable" version of the
+ * POSIX, but enhance its usage by defining some good practices.
+ *
+ * Modules are created with eina_module_new() and later loaded with
+ * eina_module_load(). Loads are reference counted and there must be
+ * the same number of eina_module_unload() in order to have it to call
+ * dlclose(). This makes simple to have different users for the same
+ * module.
+ *
+ * The loaded shared objects may have two visible functions that will
+ * be called and might provide initialization and shutdown
+ * proceedures. The symbols are @c __eina_module_init and
+ * @c __eina_module_shutdown and will be defined by the macros
+ * EINA_MODULE_INIT() and EINA_MODULE_SHUTDOWN().
+ *
+ * There are some helpers to automatically create modules based on
+ * directory listing. See eina_module_arch_list_get(),
+ * eina_module_list_get() and eina_module_find().
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Module
+ * Dynamic module loader handle.
+ */
+typedef struct _Eina_Module Eina_Module;
+
+typedef Eina_Bool (*Eina_Module_Cb)(Eina_Module *m, void *data);
+
+/**
+ * @typedef Eina_Module_Init
+ * If a function with such signature is exported by module as
+ * __eina_module_init, it will be called on the first load after
+ * dlopen() and if #EINA_FALSE is returned, load will fail, #EINA_TRUE
+ * means the module was successfully initialized.
+ * @see Eina_Module_Shutdown
+ */
+typedef Eina_Bool (*Eina_Module_Init)(void);
+
+/**
+ * @typedef Eina_Module_Shutdown
+ * If a function with such signature is exported by module as
+ * __eina_module_shutdown, it will be called before calling dlclose()
+ * @see Eina_Module_Init
+ */
+typedef void (*Eina_Module_Shutdown)(void);
+
+/**
+ * @def EINA_MODULE_INIT
+ * declares the given function as the module initializer (__eina_module_init).
+ * It must be of signature #Eina_Module_Init
+ */
+#define EINA_MODULE_INIT(f) EAPI Eina_Module_Init __eina_module_init = &f
+
+/**
+ * @def EINA_MODULE_SHUTDOWN
+ * declares the given function as the module shutdownializer
+ * (__eina_module_shutdown).  It must be of signature
+ * #Eina_Module_Shutdown
+ */
+#define EINA_MODULE_SHUTDOWN(f) EAPI Eina_Module_Shutdown __eina_module_shutdown = &f
+
+/**
+ * @var EINA_ERROR_WRONG_MODULE
+ * Error identifier corresponding to a wrong module.
+ */
+extern EAPI Eina_Error EINA_ERROR_WRONG_MODULE;
+
+/**
+ * @var EINA_ERROR_MODULE_INIT_FAILED
+ * Error identifier corresponding to a failure during the initialisation of a module.
+ */
+extern EAPI Eina_Error EINA_ERROR_MODULE_INIT_FAILED;
+
+EAPI Eina_Module *
+eina_module_new(const char *file) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI Eina_Bool
+eina_module_free(Eina_Module *m) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool
+eina_module_load(Eina_Module *module) EINA_ARG_NONNULL(1);
+EAPI Eina_Bool
+eina_module_unload(Eina_Module *m) EINA_ARG_NONNULL(1);
+EAPI void *
+eina_module_symbol_get(const Eina_Module *module, const char *symbol) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+EAPI const char *
+eina_module_file_get(const Eina_Module *m) EINA_PURE EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+EAPI char *
+eina_module_symbol_path_get(const void *symbol, const char *sub_dir) EINA_PURE EINA_MALLOC EINA_ARG_NONNULL(1, 2);
+EAPI char *
+eina_module_environment_path_get(const char *env, const char *sub_dir) EINA_PURE EINA_MALLOC EINA_ARG_NONNULL(1, 2);
+
+EAPI Eina_Array *
+eina_module_arch_list_get(Eina_Array *array, const char *path, const char *arch);
+EAPI Eina_Array *
+eina_module_list_get(Eina_Array *array, const char *path, Eina_Bool recursive, Eina_Module_Cb cb, void *data) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI void
+eina_module_list_load(Eina_Array *list) EINA_ARG_NONNULL(1);
+EAPI void
+eina_module_list_unload(Eina_Array *list) EINA_ARG_NONNULL(1);
+EAPI void
+eina_module_list_free(Eina_Array *list) EINA_ARG_NONNULL(1);
+EAPI Eina_Module *
+eina_module_find(const Eina_Array *array, const char *module) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /*EINA_MODULE_H_*/
diff --git a/tests/suite/ecore/src/include/eina_quadtree.h b/tests/suite/ecore/src/include/eina_quadtree.h
new file mode 100644 (file)
index 0000000..ad24c3e
--- /dev/null
@@ -0,0 +1,53 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_QUADTREE_H_
+#define EINA_QUADTREE_H_
+
+#include "eina_config.h"
+
+#include "eina_inlist.h"
+
+typedef struct _Eina_QuadTree Eina_QuadTree;
+typedef struct _Eina_QuadTree_Item Eina_QuadTree_Item;
+
+typedef enum {
+   EINA_QUAD_LEFT,
+   EINA_QUAD_RIGHT,
+   EINA_QUAD_BOTH
+} Eina_Quad_Direction;
+
+typedef Eina_Quad_Direction (*Eina_Quad_Callback)(const void *object, size_t middle);
+
+EAPI Eina_QuadTree *     eina_quadtree_new(size_t w, size_t h, Eina_Quad_Callback vertical, Eina_Quad_Callback horizontal);
+EAPI void                eina_quadtree_free(Eina_QuadTree *q);
+EAPI void                eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h);
+
+EAPI void                eina_quadtree_cycle(Eina_QuadTree *q);
+EAPI void                eina_quadtree_increase(Eina_QuadTree_Item *object);
+
+EAPI Eina_QuadTree_Item *eina_quadtree_add(Eina_QuadTree *q, const void *object);
+EAPI Eina_Bool           eina_quadtree_del(Eina_QuadTree_Item *object);
+EAPI Eina_Bool           eina_quadtree_change(Eina_QuadTree_Item *object);
+EAPI Eina_Bool           eina_quadtree_hide(Eina_QuadTree_Item *object);
+EAPI Eina_Bool           eina_quadtree_show(Eina_QuadTree_Item *object);
+
+EAPI Eina_Inlist *       eina_quadtree_collide(Eina_QuadTree *q, int x, int y, int w, int h);
+EAPI void *              eina_quadtree_object(Eina_Inlist *list);
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_rbtree.h b/tests/suite/ecore/src/include/eina_rbtree.h
new file mode 100644 (file)
index 0000000..026db41
--- /dev/null
@@ -0,0 +1,157 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_RBTREE_H__
+#define EINA_RBTREE_H__
+
+#include <stdlib.h>
+
+#include "eina_types.h"
+#include "eina_error.h"
+#include "eina_iterator.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Rbtree_Group Red-Black tree
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Rbtree_Color
+ * node color.
+ */
+typedef enum {
+   EINA_RBTREE_RED,
+   EINA_RBTREE_BLACK
+} Eina_Rbtree_Color;
+
+/**
+ * @typedef Eina_Rbtree_Direction
+ * walk direction.
+ */
+typedef enum {
+   EINA_RBTREE_LEFT = 0,
+   EINA_RBTREE_RIGHT = 1
+} Eina_Rbtree_Direction;
+
+/**
+ * @typedef Eina_Rbtree
+ * Type for a Red-Black tree node. It should be inlined into user's type.
+ */
+typedef struct _Eina_Rbtree Eina_Rbtree;
+struct _Eina_Rbtree
+{
+   Eina_Rbtree *son[2];
+
+   Eina_Rbtree_Color color : 1;
+};
+
+/**
+ * @def EINA_RBTREE
+ * recommended way to declare the inlined Eina_Rbtree in your type.
+ *
+ * @code
+ * struct my_type {
+ *    EINA_RBTREE;
+ *    int my_value;
+ *    char *my_name;
+ * };
+ * @endcode
+ *
+ * @see EINA_RBTREE_GET()
+ */
+#define EINA_RBTREE Eina_Rbtree __rbtree
+
+/**
+ * @def EINA_RBTREE_GET
+ * access the inlined node if it was created with #EINA_RBTREE.
+ */
+#define EINA_RBTREE_GET(Rbtree) & ((Rbtree)->__rbtree)
+
+/**
+ * @typedef Eina_Rbtree_Cmp_Node_Cb
+ * Function used compare two nodes and see which direction to navigate.
+ */
+typedef Eina_Rbtree_Direction (*Eina_Rbtree_Cmp_Node_Cb)(const Eina_Rbtree *left, const Eina_Rbtree *right, void *data);
+
+/**
+ * @def EINA_RBTREE_CMP_NODE_CB
+ * Cast using #Eina_Rbtree_Cmp_Node_Cb
+ */
+#define EINA_RBTREE_CMP_NODE_CB(Function) ((Eina_Rbtree_Cmp_Node_Cb)Function)
+
+/**
+ * @typedef Eina_Rbtree_Cmp_Key_Cb
+ * Function used compare node with a given key of specified length.
+ */
+typedef int (*Eina_Rbtree_Cmp_Key_Cb)(const Eina_Rbtree *node, const void *key, int length, void *data);
+/**
+ * @def EINA_RBTREE_CMP_KEY_CB
+ * Cast using #Eina_Rbtree_Cmp_Key_Cb
+ */
+#define EINA_RBTREE_CMP_KEY_CB(Function) ((Eina_Rbtree_Cmp_Key_Cb)Function)
+
+/**
+ * @typedef Eina_Rbtree_Free_Cb
+ * Function used free a node.
+ */
+typedef void (*Eina_Rbtree_Free_Cb)(Eina_Rbtree *node, void *data);
+/**
+ * @def EINA_RBTREE_FREE_CB
+ * Cast using #Eina_Rbtree_Free_Cb
+ */
+#define EINA_RBTREE_FREE_CB(Function) ((Eina_Rbtree_Free_Cb)Function)
+
+EAPI Eina_Rbtree *              eina_rbtree_inline_insert(Eina_Rbtree *root,Eina_Rbtree *node,Eina_Rbtree_Cmp_Node_Cb cmp,const void *data) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Rbtree *              eina_rbtree_inline_remove(Eina_Rbtree *root,Eina_Rbtree *node,Eina_Rbtree_Cmp_Node_Cb cmp,const void *data) EINA_ARG_NONNULL(2, 3) EINA_WARN_UNUSED_RESULT;
+EAPI void                       eina_rbtree_delete(Eina_Rbtree *root,Eina_Rbtree_Free_Cb func,void *data) EINA_ARG_NONNULL(2);
+
+static inline Eina_Rbtree *     eina_rbtree_inline_lookup(const Eina_Rbtree *root,const void *key,int length,Eina_Rbtree_Cmp_Key_Cb cmp,const void *data) EINA_PURE EINA_ARG_NONNULL(2, 4) EINA_WARN_UNUSED_RESULT;
+
+EAPI Eina_Iterator *            eina_rbtree_iterator_prefix(const Eina_Rbtree *root) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Iterator *            eina_rbtree_iterator_infix(const Eina_Rbtree *root) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Iterator *            eina_rbtree_iterator_postfix(const Eina_Rbtree *root) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+#include "eina_inline_rbtree.x"
+
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_rectangle.h b/tests/suite/ecore/src/include/eina_rectangle.h
new file mode 100644 (file)
index 0000000..a9daf66
--- /dev/null
@@ -0,0 +1,109 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_RECTANGLE_H_
+#define EINA_RECTANGLE_H_
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Rectangle_Group Rectangle
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Rectangle
+ * Simple rectangle structure.
+ */
+typedef struct _Eina_Rectangle
+{
+  int x; /**< top-left x co-ordinate of rectangle */
+  int y; /**< top-left y co-ordinate of rectangle */
+  int w; /**< width of rectangle */
+  int h; /**< height of rectangle */
+} Eina_Rectangle;
+
+/**
+ * @typedef Eina_Rectangle_Pool
+ * Type for an opaque pool of rectangle.
+ */
+typedef struct _Eina_Rectangle_Pool Eina_Rectangle_Pool;
+
+static inline int eina_spans_intersect(int c1, int l1, int c2, int l2) EINA_WARN_UNUSED_RESULT;
+static inline Eina_Bool eina_rectangle_is_empty(const Eina_Rectangle *r) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline void eina_rectangle_coords_from(Eina_Rectangle *r, int x, int y, int w, int h) EINA_ARG_NONNULL(1);
+static inline Eina_Bool eina_rectangles_intersect(const Eina_Rectangle *r1, const Eina_Rectangle *r2) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+static inline Eina_Bool eina_rectangle_xcoord_inside(const Eina_Rectangle *r, int x) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline Eina_Bool eina_rectangle_ycoord_inside(const Eina_Rectangle *r, int y) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline Eina_Bool eina_rectangle_coords_inside(const Eina_Rectangle *r, int x, int y) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+static inline void eina_rectangle_union(Eina_Rectangle *dst, const Eina_Rectangle *src) EINA_ARG_NONNULL(1, 2);
+static inline Eina_Bool eina_rectangle_intersection(Eina_Rectangle *dst, const Eina_Rectangle *src) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+static inline void eina_rectangle_rescale_in(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res) EINA_ARG_NONNULL(1, 2, 3);
+static inline void eina_rectangle_rescale_out(const Eina_Rectangle *out, const Eina_Rectangle *in, Eina_Rectangle *res) EINA_ARG_NONNULL(1, 2, 3);
+
+EAPI Eina_Rectangle_Pool * eina_rectangle_pool_new(int w, int h) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Rectangle_Pool * eina_rectangle_pool_get(Eina_Rectangle *rect) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI Eina_Bool eina_rectangle_pool_geometry_get(Eina_Rectangle_Pool *pool, int *w, int *h) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI void * eina_rectangle_pool_data_get(Eina_Rectangle_Pool *pool) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void eina_rectangle_pool_data_set(Eina_Rectangle_Pool *pool, const void *data) EINA_ARG_NONNULL(1);
+EAPI void eina_rectangle_pool_free(Eina_Rectangle_Pool *pool) EINA_ARG_NONNULL(1);
+EAPI int eina_rectangle_pool_count(Eina_Rectangle_Pool *pool) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Rectangle * eina_rectangle_pool_request(Eina_Rectangle_Pool *pool, int w, int h) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+EAPI void eina_rectangle_pool_release(Eina_Rectangle *rect) EINA_ARG_NONNULL(1);
+
+/**
+ * @def EINA_RECTANGLE_SET
+ * @brief Macro to set the values of a #Eina_Rectangle.
+ *
+ * @param Rectangle The rectangle to set the values.
+ * @param X The X coordinate of the top left corner of the rectangle.
+ * @param Y The Y coordinate of the top left corner of the rectangle.
+ * @param W The width of the rectangle.
+ * @param H The height of the rectangle.
+ *
+ * This macro set the values of @p Rectangle. (@p X, @p Y) is the
+ * coordinates of the top left corner of @p Rectangle, @p W is its
+ * width and @p H is its height.
+ */
+#define EINA_RECTANGLE_SET(Rectangle, X, Y, W, H) \
+                          (Rectangle)->x = X;     \
+                          (Rectangle)->y = Y;     \
+                          (Rectangle)->w = W;     \
+                          (Rectangle)->h = H;
+
+EAPI Eina_Rectangle *eina_rectangle_new(int x, int y, int w, int h) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI void            eina_rectangle_free(Eina_Rectangle *rect) EINA_ARG_NONNULL(1);
+
+#include "eina_inline_rectangle.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /*_EINA_RECTANGLE_H_*/
diff --git a/tests/suite/ecore/src/include/eina_safety_checks.h b/tests/suite/ecore/src/include/eina_safety_checks.h
new file mode 100644 (file)
index 0000000..29ebba0
--- /dev/null
@@ -0,0 +1,234 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_SAFETY_CHECKS_H_
+#define EINA_SAFETY_CHECKS_H_
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Safety_Checks_Group Safety Checks
+ *
+ * @warning @c eina_safety_checks.h should only be included by source
+ *          files, after all other includes and before the source file
+ *          specific includes. By source file specific includes we
+ *          mean those that define the functions that are being
+ *          checked. The reason for such complexity is the trick to
+ *          avoid compiler optimizations. If compilers are told that
+ *          some given function will never receive @c NULL
+ *          (EINA_ARG_NONNULL(), then compiler will emit a warning if
+ *          it detects so (good!) but will remove any checks for that
+ *          condition as it believes it will never happen, removing
+ *          all safety checks! By including @c eina_safety_checks.h it
+ *          will redefine EINA_ARG_NONNULL() to void and compiler
+ *          warning will not be emitted, but checks will be there. The
+ *          files already processed with the old macro
+ *          EINA_ARG_NONNULL() will still work and emit the warnings.
+ *
+ *
+ * @code
+ *
+ * // all these files will emit warning from EINA_ARG_NONNULL()
+ * #include <Evas.h>  // third party headers
+ * #include <Ecore.h>
+ * #include <eina_error.h> // eina own header
+ *
+ * #include <eina_safety_checks.h>
+ * // all these files below will NOT emit warning from EINA_ARG_NONNULL(),
+ * // but this is required to have the functions defined there to be checked
+ * // for NULL pointers
+ * #include "my_functions1.h"
+ * #include "my_functions2.h"
+ *
+ * @endcode
+ *
+ * @{
+ */
+
+
+#include "eina_config.h"
+#include "eina_error.h"
+
+/**
+ * @var EINA_ERROR_SAFETY_FAILED
+ * Error identifier corresponding to safety check failure.
+ */
+EAPI extern Eina_Error EINA_ERROR_SAFETY_FAILED;
+
+#ifdef EINA_SAFETY_CHECKS
+
+#include "eina_log.h"
+
+#define EINA_SAFETY_ON_NULL_RETURN(exp)                                 \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY((exp) == NULL))                                \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " == NULL"); \
+           return;                                                     \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_NULL_RETURN_VAL(exp, val)                        \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY((exp) == NULL))                                \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " == NULL"); \
+           return (val);                                               \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_NULL_GOTO(exp, label)                            \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY((exp) == NULL))                                \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " == NULL"); \
+           goto label;                                                 \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_TRUE_RETURN(exp)                                 \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY(exp))                                          \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " is true"); \
+           return;                                                     \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_TRUE_RETURN_VAL(exp, val)                        \
+  do                                                                    \
+    {                                                                  \
+       if (EINA_UNLIKELY(exp))                                         \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " is true"); \
+           return val;                                                 \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_TRUE_GOTO(exp, label)                            \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY(exp))                                          \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " is true"); \
+           goto label;                                                 \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_FALSE_RETURN(exp)                                \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY(!(exp)))                                       \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " is false"); \
+           return;                                                     \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_FALSE_RETURN_VAL(exp, val)                       \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY(!(exp)))                                       \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " is false"); \
+           return val;                                                 \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#define EINA_SAFETY_ON_FALSE_GOTO(exp, label)                           \
+  do                                                                    \
+    {                                                                   \
+       if (EINA_UNLIKELY(!(exp)))                                       \
+        {                                                              \
+           eina_error_set(EINA_ERROR_SAFETY_FAILED);                   \
+           EINA_LOG_ERR("%s", "safety check failed: " # exp " is false"); \
+           goto label;                                                 \
+        }                                                              \
+    }                                                                   \
+  while (0)
+
+#ifdef EINA_ARG_NONNULL
+/* make EINA_ARG_NONNULL void so GCC does not optimize safety checks */
+#undef EINA_ARG_NONNULL
+#define EINA_ARG_NONNULL(idx, ...)
+#endif
+
+
+#else /* no safety checks */
+
+#define EINA_SAFETY_ON_NULL_RETURN(exp)                \
+  do { (void)(!(exp)); } while (0)
+
+#define EINA_SAFETY_ON_NULL_RETURN_VAL(exp, val)               \
+  do { if (0 && !(exp)) { (void)val; } } while (0)
+
+#define EINA_SAFETY_ON_NULL_GOTO(exp, label)                   \
+  do { if (0 && (exp) == NULL) { goto label; } } while (0)
+
+#define EINA_SAFETY_ON_TRUE_RETURN(exp)                \
+  do { (void)(exp); } while (0)
+
+#define EINA_SAFETY_ON_TRUE_RETURN_VAL(exp, val)       \
+  do { if (0 && (exp)) { (void)val; } } while (0)
+
+#define EINA_SAFETY_ON_TRUE_GOTO(exp, label)           \
+  do { if (0 && (exp)) { goto label; } } while (0)
+
+#define EINA_SAFETY_ON_FALSE_RETURN(exp)       \
+  do { (void)(!(exp)); } while (0)
+
+#define EINA_SAFETY_ON_FALSE_RETURN_VAL(exp, val)      \
+  do { if (0 && !(exp)) { (void)val; } } while (0)
+
+#define EINA_SAFETY_ON_FALSE_GOTO(exp, label)          \
+  do { if (0 && !(exp)) { goto label; } } while (0)
+
+#endif /* safety checks macros */
+#endif /* EINA_SAFETY_CHECKS_H_ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/include/eina_sched.h b/tests/suite/ecore/src/include/eina_sched.h
new file mode 100644 (file)
index 0000000..607b9f5
--- /dev/null
@@ -0,0 +1,26 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2010 ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_SCHED_H_
+#define EINA_SCHED_H_
+
+#include "eina_types.h"
+
+EAPI void eina_sched_prio_drop(void);
+
+#endif /* EINA_SCHED_H_ */
diff --git a/tests/suite/ecore/src/include/eina_str.h b/tests/suite/ecore/src/include/eina_str.h
new file mode 100644 (file)
index 0000000..1d12615
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _EINA_STR_H
+#define _EINA_STR_H
+
+#include <stddef.h>
+#include <string.h>
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_String_Group String
+ *
+ * @{
+ */
+
+/* strlcpy implementation for libc's lacking it */
+EAPI size_t      eina_strlcpy(char *dst, const char *src, size_t siz) EINA_ARG_NONNULL(1, 2);
+EAPI size_t      eina_strlcat(char *dst, const char *src, size_t siz) EINA_ARG_NONNULL(1, 2);
+
+EAPI Eina_Bool   eina_str_has_prefix(const char *str, const char *prefix) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Bool   eina_str_has_suffix(const char *str, const char *suffix) EINA_PURE EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+EAPI Eina_Bool   eina_str_has_extension(const char *str, const char *ext) EINA_PURE    EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+
+EAPI char **     eina_str_split(const char *string, const char *delimiter, int max_tokens) EINA_ARG_NONNULL(1, 2) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+EAPI char **     eina_str_split_full(const char *string, const char *delimiter, int max_tokens, unsigned int *elements) EINA_ARG_NONNULL(1, 2, 4) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+EAPI size_t      eina_str_join_len(char *dst, size_t size, char sep, const char *a, size_t a_len, const char *b, size_t b_len) EINA_ARG_NONNULL(1, 4, 6);
+
+EAPI char *      eina_str_convert(const char *enc_from, const char *enc_to, const char *text) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_ARG_NONNULL(1, 2, 3);
+
+EAPI char *      eina_str_escape(const char *str) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_ARG_NONNULL(1);
+
+EAPI void        eina_str_tolower(char **str);
+EAPI void        eina_str_toupper(char **str);
+
+static inline size_t eina_str_join(char *dst, size_t size, char sep, const char *a, const char *b) EINA_ARG_NONNULL(1, 4, 5);
+
+/**
+ * @def eina_str_join_static(dst, sep, a, b)
+ * @brief Join two static strings and store the result in a static buffer.
+ *
+ * @param dst The buffer to store the result.
+ * @param sep The separator character to use.
+ * @param a First string to use, before @p sep.
+ * @param b Second string to use, after @p sep.
+ * @return The number of characters printed.
+ *
+ * This function is similar to eina_str_join_len(), but will assume
+ * string sizes are know using sizeof(X).
+ *
+ * @see eina_str_join()
+ * @see eina_str_join_static()
+ */
+#define eina_str_join_static(dst, sep, a, b) eina_str_join_len(dst, sizeof(dst), sep, a, (sizeof(a) > 0) ? sizeof(a) - 1 : 0, b, (sizeof(b) > 0) ? sizeof(b) - 1 : 0)
+
+static inline size_t eina_strlen_bounded(const char *str, size_t maxlen) EINA_PURE EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+#include "eina_inline_str.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STR_H */
diff --git a/tests/suite/ecore/src/include/eina_strbuf.h b/tests/suite/ecore/src/include/eina_strbuf.h
new file mode 100644 (file)
index 0000000..4424eb4
--- /dev/null
@@ -0,0 +1,477 @@
+#ifndef EINA_STRBUF_H
+#define EINA_STRBUF_H
+
+#include <stddef.h>
+#include <stdarg.h>
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_String_Buffer_Group String Buffer
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Strbuf
+ * Type for a string buffer.
+ */
+typedef struct _Eina_Strbuf Eina_Strbuf;
+
+/**
+ * @brief Create a new string buffer.
+ *
+ * @return Newly allocated string buffer instance.
+ *
+ * This function creates a new string buffer. On error, @c NULL is
+ * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To
+ * free the resources, use eina_strbuf_free().
+ *
+ * @see eina_strbuf_free()
+ * @see eina_strbuf_append()
+ * @see eina_strbuf_string_get()
+ */
+EAPI Eina_Strbuf *eina_strbuf_new(void) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Free a string buffer.
+ *
+ * @param buf The string buffer to free.
+ *
+ * This function frees the memory of @p buf. @p buf must have been
+ * created by eina_strbuf_new().
+ */
+EAPI void         eina_strbuf_free(Eina_Strbuf *buf) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Reset a string buffer.
+ *
+ * @param buf The string buffer to reset.
+ *
+ * This function reset @p buf: the buffer len is set to 0, and the
+ * string is set to '\\0'. No memory is free'd.
+ */
+EAPI void         eina_strbuf_reset(Eina_Strbuf *buf) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends @p str to @p buf. It computes the length of
+ * @p str, so is slightly slower than eina_strbuf_append_length(). If
+ * the length is known beforehand, consider using that variant. If
+ * @p buf can't append it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ *
+ * @see eina_strbuf_append()
+ * @see eina_strbuf_append_length()
+ */
+EAPI Eina_Bool    eina_strbuf_append(Eina_Strbuf *buf, const char *str) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append an escaped string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends the escaped string @p str to @p buf. If @p
+ * str can not be appended, #EINA_FALSE is returned, otherwise,
+ * #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool    eina_strbuf_append_escaped(Eina_Strbuf *buf, const char *str) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary,
+ * limited by the given length.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @param maxlen The maximum number of characters to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends at most @p maxlen characters of @p str to
+ * @p buf. It can't appends more than the length of @p str. It
+ * computes the length of @p str, so is slightly slower than
+ * eina_strbuf_append_length(). If the length is known beforehand,
+ * consider using that variant (@p maxlen should then be checked so
+ * that it is greater than the size of @p str). If @p str can not be
+ * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_strbuf_append()
+ * @see eina_strbuf_append_length()
+ */
+EAPI Eina_Bool    eina_strbuf_append_n(Eina_Strbuf *buf, const char *str, size_t maxlen) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append a string of exact length to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @param length The exact length to use.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends @p str to @p buf. @p str must be of size at
+ * most @p length. It is slightly faster than eina_strbuf_append() as
+ * it does not compute the size of @p str. It is useful when dealing
+ * with strings of known size, such as eina_strngshare. If @p buf
+ * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_stringshare_length()
+ * @see eina_strbuf_append()
+ * @see eina_strbuf_append_n()
+ */
+EAPI Eina_Bool    eina_strbuf_append_length(Eina_Strbuf *buf, const char *str, size_t length) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append a character to a string buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param c The char to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p c to @p buf. If it can not insert it,
+ * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool    eina_strbuf_append_char(Eina_Strbuf *buf, char c) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param fmt The string to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * @see eina_strbuf_append()
+ */
+EAPI Eina_Bool    eina_strbuf_append_printf(Eina_Strbuf *buf, const char *fmt, ...) EINA_ARG_NONNULL(1, 2) EINA_PRINTF(2, 3);
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param fmt The string to append.
+ * @param args The variable arguments.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * @see eina_strbuf_append()
+ */
+EAPI Eina_Bool    eina_strbuf_append_vprintf(Eina_Strbuf *buf, const char *fmt, va_list args) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert.
+ * @param str The string to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str to @p buf at position @p pos. It
+ * computes the length of @p str, so is slightly slower than
+ * eina_strbuf_insert_length(). If  the length is known beforehand,
+ * consider using that variant. If @p buf can't insert it, #EINA_FALSE
+ * is returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool    eina_strbuf_insert(Eina_Strbuf *buf, const char *str, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert an escaped string to a buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts the escaped string @p str to @p buf at
+ * position @p pos. If @p buf can't insert @p str, #EINA_FALSE is
+ * returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool    eina_strbuf_insert_escaped(Eina_Strbuf *buf, const char *str, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param maxlen The maximum number of chars to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str ot @p buf at position @p pos, with at
+ * most @p maxlen bytes. The number of inserted characters can not be
+ * greater than the length of @p str. It computes the length of
+ * @p str, so is slightly slower than eina_strbuf_insert_length(). If the
+ * length is known beforehand, consider using that variant (@p maxlen
+ * should then be checked so that it is greater than the size of
+ * @p str). If @p str can not be inserted, #EINA_FALSE is returned,
+ * otherwise, #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool    eina_strbuf_insert_n(Eina_Strbuf *buf, const char *str, size_t maxlen, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a string of exact length to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param length The exact length to use.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str to @p buf. @p str must be of size at
+ * most @p length. It is slightly faster than eina_strbuf_insert() as
+ * it does not compute the size of @p str. It is useful when dealing
+ * with strings of known size, such as eina_strngshare. If @p buf
+ * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_stringshare_length()
+ * @see eina_strbuf_insert()
+ * @see eina_strbuf_insert_n()
+ */
+EAPI Eina_Bool    eina_strbuf_insert_length(Eina_Strbuf *buf, const char *str, size_t length, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a character to a string buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param c The char to insert.
+ * @param pos The position to insert the char.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p c to @p buf at position @p pos. If @p buf
+ * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+EAPI Eina_Bool    eina_strbuf_insert_char(Eina_Strbuf *buf, char c, size_t pos) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert.
+ * @param fmt The string to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ */
+EAPI Eina_Bool    eina_strbuf_insert_printf(Eina_Strbuf *buf, const char *fmt, size_t pos, ...) EINA_ARG_NONNULL(1, 2) EINA_PRINTF(2, 4);
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert.
+ * @param fmt The string to insert.
+ * @param pos The position to insert the string.
+ * @param args The variable arguments.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ */
+EAPI Eina_Bool    eina_strbuf_insert_vprintf(Eina_Strbuf *buf, const char *fmt, size_t pos, va_list args) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @def eina_strbuf_prepend(buf, str)
+ * @brief Prepend the given string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert() at position 0.If @p buf
+ * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+#define eina_strbuf_prepend(buf, str) eina_strbuf_insert(buf, str, 0)
+
+/**
+ * @def eina_strbuf_prepend_escaped(buf, str)
+ * @brief Prepend the given escaped string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert_escaped() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_strbuf_prepend_escaped(buf, str) eina_strbuf_insert_escaped(buf, str, 0)
+
+/**
+ * @def eina_strbuf_prepend_n(buf, str)
+ * @brief Prepend the given escaped string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @param maxlen The maximum number of chars to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert_n() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_strbuf_prepend_n(buf, str, maxlen) eina_strbuf_insert_n(buf, str, maxlen, 0)
+
+/**
+ * @def eina_strbuf_prepend_length(buf, str)
+ * @brief Prepend the given escaped string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @param length The exact length to use.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert_length() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_strbuf_prepend_length(buf, str, length) eina_strbuf_insert_length( buf, str, length, 0)
+
+/**
+ * @def eina_strbuf_prepend_char(buf, str)
+ * @brief Prepend the given character to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param c The character to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert_char() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_strbuf_prepend_char(buf, c) eina_strbuf_insert_char(buf, c, 0)
+
+/**
+ * @def eina_strbuf_prepend_printf(buf, fmt, ...)
+ * @brief Prepend the given string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param fmt The string to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert_printf() at position 0.If @p buf
+ * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+#define eina_strbuf_prepend_printf(buf, fmt, ...) eina_strbuf_insert_printf(buf, fmt, 0, ## __VA_ARGS__)
+
+/**
+ * @def eina_strbuf_prepend_vprintf(buf, fmt, args)
+ * @brief Prepend the given string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param fmt The string to prepend.
+ * @param args The variable arguments.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_insert_vprintf() at position 0.If @p buf
+ * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+#define eina_strbuf_prepend_vprintf(buf, fmt, args) eina_strbuf_insert_vprintf(buf, fmt, 0, args)
+
+/**
+ * @brief Remove a slice of the given string buffer.
+ *
+ * @param buf The string buffer to remove a slice.
+ * @param start The initial (inclusive) slice position to start
+ *        removing, in bytes.
+ * @param end The final (non-inclusive) slice position to finish
+ *        removing, in bytes.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function removes a slice of @p buf, starting at @p start
+ * (inclusive) and ending at @p end (non-inclusive). Both values are
+ * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise.
+ */
+
+EAPI Eina_Bool eina_strbuf_remove(Eina_Strbuf *buf, size_t start, size_t end) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Retrieve a pointer to the contents of a string buffer
+ *
+ * @param buf The string buffer.
+ * @return The current string in the string buffer.
+ *
+ * This function returns the string contained in @p buf. The returned
+ * value must not be modified and will no longer be valid if @p buf is
+ * modified. In other words, any eina_strbuf_append() or similar will
+ * make that pointer invalid.
+ *
+ * @see eina_strbuf_string_steal()
+ */
+EAPI const char *eina_strbuf_string_get(const Eina_Strbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Steal the contents of a string buffer.
+ *
+ * @param buf The string buffer to steal.
+ * @return The current string in the string buffer.
+ *
+ * This function returns the string contained in @p buf. @p buf is
+ * then initialized and does not own the returned string anymore. The
+ * caller must release the memory of the returned string by calling
+ * free().
+ *
+ * @see eina_strbuf_string_get()
+ */
+EAPI char *eina_strbuf_string_steal(Eina_Strbuf *buf) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Free the contents of a string buffer but not the buffer.
+ *
+ * @param buf The string buffer to free the string of.
+ *
+ * This function frees the string contained in @p buf without freeing
+ * @p buf.
+ */
+EAPI void eina_strbuf_string_free(Eina_Strbuf *buf) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Retrieve the length of the string buffer content.
+ *
+ * @param buf The string buffer.
+ * @return The current length of the string, in bytes.
+ *
+ * This function returns the length of @p buf.
+ */
+EAPI size_t eina_strbuf_length_get(const Eina_Strbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+EAPI Eina_Bool eina_strbuf_replace(Eina_Strbuf *buf, const char *str, const char *with, unsigned int n) EINA_ARG_NONNULL(1, 2, 3);
+
+/**
+ * @def eina_strbuf_replace_first(buf, str, with)
+ * @brief Prepend the given character to the given buffer
+ *
+ * @param buf The string buffer to work with.
+ * @param str The string to replace.
+ * @param with The replaceing string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_strbuf_replace() with the n-th occurrence
+ * equal to @c 1. If @p buf can't replace it, #EINA_FALSE is returned,
+ * otherwise #EINA_TRUE is returned.
+ */
+#define eina_strbuf_replace_first(buf, str, with) eina_strbuf_replace(buf, str, with, 1)
+
+EAPI int eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with) EINA_ARG_NONNULL(1, 2, 3);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STRBUF_H */
diff --git a/tests/suite/ecore/src/include/eina_stringshare.h b/tests/suite/ecore/src/include/eina_stringshare.h
new file mode 100644 (file)
index 0000000..4df5b40
--- /dev/null
@@ -0,0 +1,93 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2008 Peter Wehrfritz
+ *
+ *  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 and its Copyright notices. In addition publicly
+ *  documented acknowledgment must be given that this software has been used if no
+ *  source code of this software is made available publicly. This includes
+ *  acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+ *  documents or any documentation provided with any product containing this
+ *  software. This License does not apply to any software that links to the
+ *  libraries provided by this software (statically or dynamically), but only to
+ *  the software provided.
+ *
+ *  Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice
+ *  and it's intent.
+ *
+ *  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.
+ */
+
+#ifndef EINA_STRINGSHARE_H_
+#define EINA_STRINGSHARE_H_
+
+#include <stdarg.h>
+
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Stringshare_Group Stringshare
+ *
+ * @{
+ */
+
+EAPI const char *       eina_stringshare_add_length(const char *str, unsigned int slen) EINA_WARN_UNUSED_RESULT;
+EAPI const char *       eina_stringshare_add(const char *str) EINA_WARN_UNUSED_RESULT;
+EAPI const char *       eina_stringshare_printf(const char *fmt, ...) EINA_WARN_UNUSED_RESULT EINA_PRINTF(1, 2);
+EAPI const char *       eina_stringshare_vprintf(const char *fmt, va_list args) EINA_WARN_UNUSED_RESULT;
+EAPI const char *       eina_stringshare_nprintf(unsigned int len, const char *fmt, ...) EINA_WARN_UNUSED_RESULT EINA_PRINTF(2, 3);
+EAPI const char *       eina_stringshare_ref(const char *str);
+EAPI void               eina_stringshare_del(const char *str);
+EAPI int                eina_stringshare_strlen(const char *str) EINA_PURE EINA_WARN_UNUSED_RESULT;
+EAPI void               eina_stringshare_dump(void);
+
+static inline Eina_Bool eina_stringshare_replace(const char **p_str, const char *news) EINA_ARG_NONNULL(1);
+static inline Eina_Bool eina_stringshare_replace_length(const char **p_str, const char *news, unsigned int slen) EINA_ARG_NONNULL(1);
+
+#include "eina_inline_stringshare.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STRINGSHARE_H_ */
diff --git a/tests/suite/ecore/src/include/eina_tiler.h b/tests/suite/ecore/src/include/eina_tiler.h
new file mode 100644 (file)
index 0000000..19762a5
--- /dev/null
@@ -0,0 +1,86 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_TILER_H_
+#define EINA_TILER_H_
+
+#include "eina_types.h"
+#include "eina_iterator.h"
+#include "eina_rectangle.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Tiler_Group Tiler
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Tiler
+ * Tiler type.
+ */
+typedef struct _Eina_Tiler Eina_Tiler;
+
+/**
+ * @typedef Eina_Tile_Grid_Info
+ * Grid type of a tiler.
+ */
+typedef struct Eina_Tile_Grid_Info Eina_Tile_Grid_Info;
+
+/**
+ * @struct Eina_Tile_Grid_Info
+ * Grid type of a tiler.
+ */
+struct Eina_Tile_Grid_Info
+{
+   unsigned long col; /**< column of the tiler grid */
+   unsigned long row; /**< row of the tiler grid*/
+   Eina_Rectangle rect; /**< rectangle of the tiler grid*/
+   Eina_Bool full; /**< whether the grid is full or not */
+};
+
+typedef struct _Eina_Tile_Grid_Slicer Eina_Tile_Grid_Slicer;
+
+EAPI Eina_Tiler *   eina_tiler_new(int w, int h);
+EAPI void           eina_tiler_free(Eina_Tiler *t);
+EAPI void           eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h);
+EAPI Eina_Bool      eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r);
+EAPI void           eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r);
+EAPI void           eina_tiler_clear(Eina_Tiler *t);
+EAPI Eina_Iterator *eina_tiler_iterator_new(const Eina_Tiler *t);
+EAPI Eina_Iterator *eina_tile_grid_slicer_iterator_new(int x, int y, int w, int h, int tile_w, int tile_h);
+static inline Eina_Bool eina_tile_grid_slicer_next(Eina_Tile_Grid_Slicer *slc, const Eina_Tile_Grid_Info **rect);
+static inline Eina_Bool eina_tile_grid_slicer_setup(Eina_Tile_Grid_Slicer *slc, int x, int y, int w, int h, int tile_w, int tile_h);
+
+#include "eina_inline_tiler.x"
+
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_TILER_H_ */
diff --git a/tests/suite/ecore/src/include/eina_trash.h b/tests/suite/ecore/src/include/eina_trash.h
new file mode 100644 (file)
index 0000000..0d165f1
--- /dev/null
@@ -0,0 +1,100 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_TRASH_H__
+#define EINA_TRASH_H__
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @addtogroup Eina_Containers_Group Containers
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Trash_Group Trash
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Trash
+ * Type for a generic container of unused allocated pointer.
+ */
+typedef struct _Eina_Trash Eina_Trash;
+
+/**
+ * @struct _Eina_Trash
+ * Type for a generic container of unused allocated pointer.
+ */
+struct _Eina_Trash
+{
+   Eina_Trash *next; /**< next item in trash. */
+};
+
+static inline void  eina_trash_init(Eina_Trash **trash) EINA_ARG_NONNULL(1);
+static inline void  eina_trash_push(Eina_Trash **trash, void *data) EINA_ARG_NONNULL(1);
+static inline void *eina_trash_pop(Eina_Trash **trash) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @def EINA_TRASH_CLEAN
+ * @brief Macro to remove all pointer from the trash.
+ *
+ * @param trash The trash to clean.
+ * @param data The pointer extracted from the trash.
+ *
+ * This macro allow the cleaning of @p trash in an easy way. It will
+ * remove all pointers from @p trash until it's empty.
+ *
+ * This macro can be used for freeing the data in the trash, like in
+ * the following example:
+ *
+ * @code
+ * Eina_Trash *trash = NULL;
+ * char *data;
+ *
+ * // trash is filled with pointer to some duped strings.
+ *
+ * EINA_TRASH_CLEAN(&trash, data)
+ *   free(data);
+ * @endcode
+ *
+ * @note this macro is useful when you implement some memory pool.
+ */
+#define EINA_TRASH_CLEAN(trash, data) while ((data = eina_trash_pop(trash))
+
+#include "eina_inline_trash.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_TRASH_H_ */
diff --git a/tests/suite/ecore/src/include/eina_types.h b/tests/suite/ecore/src/include/eina_types.h
new file mode 100644 (file)
index 0000000..c94983f
--- /dev/null
@@ -0,0 +1,284 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_TYPES_H_
+#define EINA_TYPES_H_
+
+/**
+ * @addtogroup Eina_Core_Group Core
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Types_Group Types
+ *
+ * @{
+ */
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EINA_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_EINA_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif
+
+#include "eina_config.h"
+
+#ifdef EINA_WARN_UNUSED_RESULT
+# undef EINA_WARN_UNUSED_RESULT
+#endif
+#ifdef EINA_ARG_NONNULL
+# undef EINA_ARG_NONNULL
+#endif
+#ifdef EINA_DEPRECATED
+# undef EINA_DEPRECATED
+#endif
+#ifdef EINA_MALLOC
+# undef EINA_MALLOC
+#endif
+#ifdef EINA_PURE
+# undef EINA_PURE
+#endif
+#ifdef EINA_PRINTF
+# undef EINA_PRINTF
+#endif
+#ifdef EINA_SCANF
+# undef EINA_SCANF
+#endif
+#ifdef EINA_FORMAT
+# undef EINA_FORMAT
+#endif
+#ifdef EINA_CONST
+# undef EINA_CONST
+#endif
+#ifdef EINA_NOINSTRUMENT
+# undef EINA_NOINSTRUMENT
+#endif
+#ifdef EINA_UNLIKELY
+# undef EINA_UNLIKELY
+#endif
+#ifdef EINA_LIKELY
+# undef EINA_LIKELY
+#endif
+
+
+#ifdef __GNUC__
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#  define EINA_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
+# else
+#  define EINA_WARN_UNUSED_RESULT
+# endif
+
+# if (!defined(EINA_SAFETY_CHECKS)) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
+#  define EINA_ARG_NONNULL(idx, ...) __attribute__ ((nonnull(idx, ## __VA_ARGS__)))
+# else
+#  define EINA_ARG_NONNULL(idx, ...)
+# endif
+
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#  define EINA_DEPRECATED __attribute__ ((__deprecated__))
+# else
+#  define EINA_DEPRECATED
+# endif
+
+# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+#  define EINA_MALLOC __attribute__ ((malloc))
+#  define EINA_PURE __attribute__ ((pure))
+# else
+#  define EINA_MALLOC
+#  define EINA_PURE
+# endif
+
+# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#  define EINA_PRINTF(fmt, arg)  __attribute__((format (printf, fmt, arg)))
+#  define EINA_SCANF(fmt, arg)  __attribute__((format (scanf, fmt, arg)))
+#  define EINA_FORMAT(fmt) __attribute__((format_arg(fmt)))
+#  define EINA_CONST __attribute__((const))
+#  define EINA_NOINSTRUMENT __attribute__((no_instrument_function))
+#  define EINA_UNLIKELY(exp) __builtin_expect((exp), 0)
+#  define EINA_LIKELY(exp) __builtin_expect((exp), 1)
+# else
+#  define EINA_PRINTF(fmt, arg)
+#  define EINA_SCANF(fmt, arg)
+#  define EINA_FORMAT(fmt)
+#  define EINA_CONST
+#  define EINA_NOINSTRUMENT
+#  define EINA_UNLIKELY(exp) exp
+#  define EINA_LIKELY(exp) exp
+# endif
+
+#elif defined(_WIN32)
+# define EINA_WARN_UNUSED_RESULT
+# define EINA_ARG_NONNULL(idx, ...)
+# if defined(_MSC_VER) && _MSC_VER >= 1300
+#  define EINA_DEPRECATED __declspec(deprecated)
+# else
+#  define EINA_DEPRECATED
+# endif
+# define EINA_MALLOC
+# define EINA_PURE
+# define EINA_PRINTF(fmt, arg)
+# define EINA_SCANF(fmt, arg)
+# define EINA_FORMAT(fmt)
+# define EINA_CONST
+# define EINA_NOINSTRUMENT
+# define EINA_UNLIKELY(exp) exp
+# define EINA_LIKELY(exp) exp
+
+#elif defined(__SUNPRO_C)
+# define EINA_WARN_UNUSED_RESULT
+# define EINA_ARG_NONNULL(...)
+# define EINA_DEPRECATED
+# if __SUNPRO_C >= 0x590
+#  define EINA_MALLOC __attribute__ ((malloc))
+#  define EINA_PURE __attribute__ ((pure))
+# else
+#  define EINA_MALLOC
+#  define EINA_PURE
+# endif
+# define EINA_PRINTF(fmt, arg)
+# define EINA_SCANF(fmt, arg)
+# define EINA_FORMAT(fmt)
+# if __SUNPRO_C >= 0x590
+#  define EINA_CONST __attribute__ ((const))
+# else
+#  define EINA_CONST
+# endif
+# define EINA_NOINSTRUMENT
+# define EINA_UNLIKELY(exp) exp
+# define EINA_LIKELY(exp) exp
+
+#else /* ! __GNUC__ && ! _WIN32 && ! __SUNPRO_C */
+
+/**
+ * @def EINA_WARN_UNUSED_RESULT
+ * Used to warn when the returned value of the function is not used.
+ */
+# define EINA_WARN_UNUSED_RESULT
+
+/**
+ * @def EINA_ARG_NONNULL
+ * Used to warn when the specified arguments of the function are @c NULL.
+ */
+# define EINA_ARG_NONNULL(idx, ...)
+
+/**
+ * @def EINA_DEPRECATED
+ * Used to warn when the function is considered as deprecated.
+ */
+# define EINA_DEPRECATED
+# define EINA_MALLOC
+# define EINA_PURE
+# define EINA_PRINTF(fmt, arg)
+# define EINA_SCANF(fmt, arg)
+# define EINA_FORMAT(fmt)
+# define EINA_CONST
+# define EINA_NOINSTRUMENT
+# define EINA_UNLIKELY(exp) exp
+# define EINA_LIKELY(exp) exp
+#endif /* ! __GNUC__ && ! _WIN32 && ! __SUNPRO_C */
+
+
+/**
+ * @typedef Eina_Bool
+ * Type to mimic a boolean.
+ *
+ * @note it differs from stdbool.h as this is defined as an unsigned
+ *       char to make it usable by bitfields (Eina_Bool name:1) and
+ *       also take as few bytes as possible.
+ */
+typedef unsigned char Eina_Bool;
+
+/**
+ * @def EINA_FALSE
+ * boolean value FALSE (numerical value 0)
+ */
+#define EINA_FALSE ((Eina_Bool)0)
+
+/**
+ * @def EINA_TRUE
+ * boolean value TRUE (numerical value 1)
+ */
+#define EINA_TRUE ((Eina_Bool)1)
+
+EAPI extern const unsigned int eina_prime_table[];
+
+/**
+ * @typedef Eina_Compare_Cb
+ * Function used in functions using sorting. It compares @p data1 and
+ * @p data2. If @p data1 is 'less' than @p data2, -1 must be returned,
+ * if it is 'greater', 1 must be returned, and if they are equal, 0
+ * must be returned.
+ */
+typedef int (*Eina_Compare_Cb)(const void *data1, const void *data2);
+
+/**
+ * @def EINA_COMPARE_CB
+ * Macro to cast to Eina_Compare_Cb.
+ */
+#define EINA_COMPARE_CB(function) ((Eina_Compare_Cb)function)
+
+typedef Eina_Bool (*Eina_Each_Cb)(const void *container, void *data, void *fdata);
+
+/**
+ * @def EINA_EACH_CB
+ * Macro to cast to Eina_Each.
+ */
+#define EINA_EACH_CB(Function) ((Eina_Each_Cb)Function)
+
+/**
+ * @typedef Eina_Free_Cb
+ * A callback type used to free data when iterating over a container.
+ */
+typedef void (*Eina_Free_Cb)(void *data);
+
+/**
+ * @def EINA_FREE_CB
+ * Macro to cast to Eina_Free_Cb.
+ */
+#define EINA_FREE_CB(Function) ((Eina_Free_Cb)Function)
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_TYPES_H_ */
diff --git a/tests/suite/ecore/src/include/eina_unicode.h b/tests/suite/ecore/src/include/eina_unicode.h
new file mode 100644 (file)
index 0000000..06a2af1
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef EINA_UNICODE_H
+#define EINA_UNICODE_H
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+#include "eina_types.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+/**
+ * @addtogroup Eina_Unicode_String Unicode String
+ *
+ * @brief These functions provide basic unicode string handling
+ *
+ * Eina_Unicode is a type that holds unicode codepoints.
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_Unicode
+ * A type that holds Unicode codepoints.
+ */
+#if EINA_SIZEOF_WCHAR_T >= 4
+# include <wchar.h>
+typedef wchar_t Eina_Unicode;
+#elif defined(EINA_HAVE_INTTYPES_H)
+# include <inttypes.h>
+typedef uint32_t Eina_Unicode;
+#elif defined(EINA_HAVE_STDINT_H)
+# include <stdint.h>
+typedef uint32_t Eina_Unicode;
+#else
+/* Hope that int is big enough */
+typedef unsigned int Eina_Unicode;
+#endif
+
+EAPI extern const Eina_Unicode *EINA_UNICODE_EMPTY_STRING;
+
+EAPI size_t eina_unicode_strlen(const Eina_Unicode *ustr) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE;
+EAPI size_t eina_unicode_strnlen(const Eina_Unicode *ustr, int n) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE;
+
+
+EAPI Eina_Unicode *eina_unicode_strdup(const Eina_Unicode *text) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC;
+
+EAPI int           eina_unicode_strcmp(const Eina_Unicode *a, const Eina_Unicode *b) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2) EINA_PURE;
+
+EAPI Eina_Unicode *eina_unicode_strcpy(Eina_Unicode *dest, const Eina_Unicode *source) EINA_ARG_NONNULL(1, 2);
+
+EAPI Eina_Unicode *eina_unicode_strstr(const Eina_Unicode *haystack, const Eina_Unicode *needle) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2) EINA_PURE;
+
+EAPI Eina_Unicode *eina_unicode_strncpy(Eina_Unicode *dest, const Eina_Unicode *source, size_t n) EINA_ARG_NONNULL(1, 2);
+
+EAPI Eina_Unicode *eina_unicode_escape(const Eina_Unicode *str) EINA_ARG_NONNULL(1) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @}
+ */
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/include/eina_ustrbuf.h b/tests/suite/ecore/src/include/eina_ustrbuf.h
new file mode 100644 (file)
index 0000000..e042b5d
--- /dev/null
@@ -0,0 +1,418 @@
+#ifndef EINA_USTRBUF_H
+#define EINA_USTRBUF_H
+
+#include <stddef.h>
+
+#include "eina_types.h"
+#include "eina_unicode.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Unicode_String_Buffer_Group Unicode String Buffer
+ *
+ * @{
+ */
+
+/**
+ * @typedef Eina_UStrbuf
+ * Type for a string buffer.
+ */
+typedef struct _Eina_Strbuf Eina_UStrbuf;
+
+/**
+ * @brief Create a new string buffer.
+ *
+ * @return Newly allocated string buffer instance.
+ *
+ * This function creates a new string buffer. On error, @c NULL is
+ * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To
+ * free the resources, use eina_ustrbuf_free().
+ *
+ * @see eina_ustrbuf_free()
+ * @see eina_ustrbuf_append()
+ * @see eina_ustrbuf_string_get()
+ */
+EAPI Eina_UStrbuf *eina_ustrbuf_new(void) EINA_MALLOC EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Free a string buffer.
+ *
+ * @param buf The string buffer to free.
+ *
+ * This function frees the memory of @p buf. @p buf must have been
+ * created by eina_ustrbuf_new().
+ */
+EAPI void          eina_ustrbuf_free(Eina_UStrbuf *buf) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Reset a string buffer.
+ *
+ * @param buf The string buffer to reset.
+ *
+ * This function reset @p buf: the buffer len is set to 0, and the
+ * string is set to '\\0'. No memory is free'd.
+ */
+EAPI void          eina_ustrbuf_reset(Eina_UStrbuf *buf) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends @p str to @p buf. It computes the length of
+ * @p str, so is slightly slower than eina_ustrbuf_append_length(). If
+ * the length is known beforehand, consider using that variant. If
+ * @p buf can't append it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ *
+ * @see eina_ustrbuf_append()
+ * @see eina_ustrbuf_append_length()
+ */
+EAPI Eina_Bool     eina_ustrbuf_append(Eina_UStrbuf *buf, const Eina_Unicode *str) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append an escaped string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends the escaped string @p str to @p buf. If @p
+ * str can not be appended, #EINA_FALSE is returned, otherwise,
+ * #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool     eina_ustrbuf_append_escaped(Eina_UStrbuf *buf, const Eina_Unicode *str) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary,
+ * limited by the given length.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @param maxlen The maximum number of characters to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends at most @p maxlen characters of @p str to
+ * @p buf. It can't appends more than the length of @p str. It
+ * computes the length of @p str, so is slightly slower than
+ * eina_ustrbuf_append_length(). If the length is known beforehand,
+ * consider using that variant (@p maxlen should then be checked so
+ * that it is greater than the size of @p str). If @p str can not be
+ * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_ustrbuf_append()
+ * @see eina_ustrbuf_append_length()
+ */
+EAPI Eina_Bool     eina_ustrbuf_append_n(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t maxlen) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append a string of exact length to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @param length The exact length to use.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends @p str to @p buf. @p str must be of size at
+ * most @p length. It is slightly faster than eina_ustrbuf_append() as
+ * it does not compute the size of @p str. It is useful when dealing
+ * with strings of known size, such as eina_strngshare. If @p buf
+ * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_stringshare_length()
+ * @see eina_ustrbuf_append()
+ * @see eina_ustrbuf_append_n()
+ */
+EAPI Eina_Bool     eina_ustrbuf_append_length(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t length) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Append a character to a string buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param c The char to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p c to @p buf. If it can not insert it,
+ * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool     eina_ustrbuf_append_char(Eina_UStrbuf *buf, Eina_Unicode c) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert.
+ * @param str The string to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str to @p buf at position @p pos. It
+ * computes the length of @p str, so is slightly slower than
+ * eina_ustrbuf_insert_length(). If  the length is known beforehand,
+ * consider using that variant. If @p buf can't insert it, #EINA_FALSE
+ * is returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool     eina_ustrbuf_insert(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert an escaped string to a buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts the escaped string @p str to @p buf at
+ * position @p pos. If @p buf can't insert @p str, #EINA_FALSE is
+ * returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool     eina_ustrbuf_insert_escaped(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param maxlen The maximum number of chars to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str ot @p buf at position @p pos, with at
+ * most @p maxlen bytes. The number of inserted characters can not be
+ * greater than the length of @p str. It computes the length of
+ * @p str, so is slightly slower than eina_ustrbuf_insert_length(). If the
+ * length is known beforehand, consider using that variant (@p maxlen
+ * should then be checked so that it is greater than the size of
+ * @p str). If @p str can not be inserted, #EINA_FALSE is returned,
+ * otherwise, #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool     eina_ustrbuf_insert_n(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t maxlen, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a string of exact length to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param length The exact length to use.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str to @p buf. @p str must be of size at
+ * most @p length. It is slightly faster than eina_ustrbuf_insert() as
+ * it does not compute the size of @p str. It is useful when dealing
+ * with strings of known size, such as eina_strngshare. If @p buf
+ * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_stringshare_length()
+ * @see eina_ustrbuf_insert()
+ * @see eina_ustrbuf_insert_n()
+ */
+EAPI Eina_Bool     eina_ustrbuf_insert_length(Eina_UStrbuf *buf, const Eina_Unicode *str, size_t length, size_t pos) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * @brief Insert a character to a string buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param c The char to insert.
+ * @param pos The position to insert the char.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p c to @p buf at position @p pos. If @p buf
+ * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+EAPI Eina_Bool     eina_ustrbuf_insert_char(Eina_UStrbuf *buf, Eina_Unicode c, size_t pos) EINA_ARG_NONNULL(1);
+
+/**
+ * @def eina_ustrbuf_prepend(buf, str)
+ * @brief Prepend the given string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert() at position 0.If @p buf
+ * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+#define eina_ustrbuf_prepend(buf, str) eina_ustrbuf_insert(buf, str, 0)
+
+/**
+ * @def eina_ustrbuf_prepend_escaped(buf, str)
+ * @brief Prepend the given escaped string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert_escaped() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_ustrbuf_prepend_escaped(buf, str) eina_ustrbuf_insert_escaped(buf, str, 0)
+
+/**
+ * @def eina_ustrbuf_prepend_n(buf, str)
+ * @brief Prepend the given escaped string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @param maxlen The maximum number of Eina_Unicode *s to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert_n() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_ustrbuf_prepend_n(buf, str, maxlen) eina_ustrbuf_insert_n(buf, str, maxlen, 0)
+
+/**
+ * @def eina_ustrbuf_prepend_length(buf, str)
+ * @brief Prepend the given escaped string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param str The string to prepend.
+ * @param length The exact length to use.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert_length() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_ustrbuf_prepend_length(buf, str, length) eina_ustrbuf_insert_length(buf, str, length, 0)
+
+/**
+ * @def eina_ustrbuf_prepend_Eina_Unicode *(buf, str)
+ * @brief Prepend the given Eina_Unicode *acter to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param c The Eina_Unicode *acter to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert_Eina_Unicode *() at position 0. If
+ * @p buf can't prepend it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ */
+#define eina_ustrbuf_prepend_Eina_Unicode *(buf, c)eina_ustrbuf_insert_Eina_Unicode * (buf, c, 0)
+
+/**
+ * @def eina_ustrbuf_prepend_printf(buf, fmt, ...)
+ * @brief Prepend the given string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param fmt The string to prepend.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert_printf() at position 0.If @p buf
+ * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+#define eina_ustrbuf_prepend_printf(buf, fmt, ...) eina_ustrbuf_insert_printf(buf, fmt, 0, ## __VA_ARGS__)
+
+/**
+ * @def eina_ustrbuf_prepend_vprintf(buf, fmt, args)
+ * @brief Prepend the given string to the given buffer
+ *
+ * @param buf The string buffer to prepend to.
+ * @param fmt The string to prepend.
+ * @param args The variable arguments.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This macro is calling eina_ustrbuf_insert_vprintf() at position 0.If @p buf
+ * can't prepend it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+#define eina_ustrbuf_prepend_vprintf(buf, fmt, args) eina_ustrbuf_insert_vprintf(buf, fmt, 0, args)
+
+/**
+ * @brief Remove a slice of the given string buffer.
+ *
+ * @param buf The string buffer to remove a slice.
+ * @param start The initial (inclusive) slice position to start
+ *        removing, in bytes.
+ * @param end The final (non-inclusive) slice position to finish
+ *        removing, in bytes.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function removes a slice of @p buf, starting at @p start
+ * (inclusive) and ending at @p end (non-inclusive). Both values are
+ * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise.
+ */
+EAPI Eina_Bool
+eina_ustrbuf_remove(Eina_UStrbuf *buf, size_t start, size_t end) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Retrieve a pointer to the contents of a string buffer
+ *
+ * @param buf The string buffer.
+ * @return The current string in the string buffer.
+ *
+ * This function returns the string contained in @p buf. The returned
+ * value must not be modified and will no longer be valid if @p buf is
+ * modified. In other words, any eina_ustrbuf_append() or similar will
+ * make that pointer invalid.
+ *
+ * @see eina_ustrbuf_string_steal()
+ */
+EAPI const Eina_Unicode *
+eina_ustrbuf_string_get(const Eina_UStrbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @brief Steal the contents of a string buffer.
+ *
+ * @param buf The string buffer to steal.
+ * @return The current string in the string buffer.
+ *
+ * This function returns the string contained in @p buf. @p buf is
+ * then initialized and does not own the returned string anymore. The
+ * caller must release the memory of the returned string by calling
+ * free().
+ *
+ * @see eina_ustrbuf_string_get()
+ */
+EAPI Eina_Unicode *
+eina_ustrbuf_string_steal(Eina_UStrbuf *buf) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Free the contents of a string buffer but not the buffer.
+ *
+ * @param buf The string buffer to free the string of.
+ *
+ * This function frees the string contained in @p buf without freeing
+ * @p buf.
+ */
+EAPI void
+eina_ustrbuf_string_free(Eina_UStrbuf *buf) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Retrieve the length of the string buffer content.
+ *
+ * @param buf The string buffer.
+ * @return The current length of the string, in bytes.
+ *
+ * This function returns the length of @p buf.
+ */
+EAPI size_t
+eina_ustrbuf_length_get(const Eina_UStrbuf *buf) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STRBUF_H */
diff --git a/tests/suite/ecore/src/include/eina_ustringshare.h b/tests/suite/ecore/src/include/eina_ustringshare.h
new file mode 100644 (file)
index 0000000..ac8c889
--- /dev/null
@@ -0,0 +1,89 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2008 Peter Wehrfritz
+ *
+ *  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 and its Copyright notices. In addition publicly
+ *  documented acknowledgment must be given that this software has been used if no
+ *  source code of this software is made available publicly. This includes
+ *  acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+ *  documents or any documentation provided with any product containing this
+ *  software. This License does not apply to any software that links to the
+ *  libraries provided by this software (statically or dynamically), but only to
+ *  the software provided.
+ *
+ *  Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice
+ *  and it's intent.
+ *
+ *  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.
+ */
+
+#ifndef EINA_USTRINGSHARE_H_
+#define EINA_USTRINGSHARE_H_
+
+#include "eina_types.h"
+#include "eina_unicode.h"
+
+/**
+ * @addtogroup Eina_Data_Types_Group Data Types
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_UStringshare_Group Unicode Stringshare
+ *
+ * @{
+ */
+
+EAPI const Eina_Unicode *eina_ustringshare_add_length(const Eina_Unicode *str, unsigned int slen) EINA_WARN_UNUSED_RESULT;
+EAPI const Eina_Unicode *eina_ustringshare_add(const Eina_Unicode *str) EINA_WARN_UNUSED_RESULT;
+EAPI const Eina_Unicode *eina_ustringshare_ref(const Eina_Unicode *str);
+EAPI void                eina_ustringshare_del(const Eina_Unicode *str);
+EAPI int                 eina_ustringshare_strlen(const Eina_Unicode *str) EINA_PURE EINA_WARN_UNUSED_RESULT;
+EAPI void                eina_ustringshare_dump(void);
+
+static inline Eina_Bool  eina_ustringshare_replace(const Eina_Unicode **p_str, const Eina_Unicode *news) EINA_ARG_NONNULL(1);
+static inline Eina_Bool  eina_ustringshare_replace_length(const Eina_Unicode **p_str, const Eina_Unicode *news, unsigned int slen) EINA_ARG_NONNULL(1);
+
+#include "eina_inline_ustringshare.x"
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EINA_STRINGSHARE_H_ */
diff --git a/tests/suite/ecore/src/lib/Ecore.h b/tests/suite/ecore/src/lib/Ecore.h
new file mode 100644 (file)
index 0000000..a70fa21
--- /dev/null
@@ -0,0 +1,484 @@
+#ifndef _ECORE_H
+#define _ECORE_H
+
+#ifdef _MSC_VER
+# include <Evil.h>
+#endif
+
+#include <Eina.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ECORE_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_ECORE_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+/**
+ * @file Ecore.h
+ * @brief The file that provides the program utility, main loop and timer
+ *        functions.
+ *
+ * This header provides the Ecore event handling loop.  For more
+ * details, see @ref Ecore_Main_Loop_Group.
+ *
+ * For the main loop to be of any use, you need to be able to add events
+ * and event handlers.  Events for file descriptor events are covered in
+ * @ref Ecore_FD_Handler_Group.
+ *
+ * Time functions are covered in @ref Ecore_Time_Group.
+ *
+ * There is also provision for callbacks for when the loop enters or
+ * exits an idle state. See @ref Idle_Group for more information.
+ *
+ * Functions are also provided for spawning child processes using fork.
+ * See @ref Ecore_Exe_Basic_Group and @ref Ecore_Exe_Signal_Group for
+ * more details.
+ */
+
+#ifdef _WIN32
+# include <winsock2.h>
+#elif (defined (__FreeBSD__) && (__FreeBSD_version >= 420001)) || defined (__OpenBSD__)
+# include <sys/select.h>
+# include <signal.h>
+#else
+# include <sys/time.h>
+# include <signal.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ECORE_VERSION_MAJOR 1
+#define ECORE_VERSION_MINOR 0
+
+   typedef struct _Ecore_Version
+     {
+        int major;
+        int minor;
+        int micro;
+        int revision;
+     } Ecore_Version;
+   
+   EAPI extern Ecore_Version *ecore_version;
+
+#define ECORE_CALLBACK_CANCEL EINA_FALSE /**< Return value to remove a callback */
+#define ECORE_CALLBACK_RENEW EINA_TRUE  /**< Return value to keep a callback */
+
+#define ECORE_CALLBACK_PASS_ON EINA_TRUE /**< Return value to pass event to next handler */
+#define ECORE_CALLBACK_DONE EINA_FALSE /**< Return value to stop event handling */
+
+#define ECORE_EVENT_NONE            0
+#define ECORE_EVENT_SIGNAL_USER     1 /**< User signal event */
+#define ECORE_EVENT_SIGNAL_HUP      2 /**< Hup signal event */
+#define ECORE_EVENT_SIGNAL_EXIT     3 /**< Exit signal event */
+#define ECORE_EVENT_SIGNAL_POWER    4 /**< Power signal event */
+#define ECORE_EVENT_SIGNAL_REALTIME 5 /**< Realtime signal event */
+#define ECORE_EVENT_COUNT           6
+
+#define ECORE_EXE_PRIORITY_INHERIT 9999
+   
+   EAPI extern int ECORE_EXE_EVENT_ADD; /**< A child process has been added */
+   EAPI extern int ECORE_EXE_EVENT_DEL; /**< A child process has been deleted (it exited, naming consistent with the rest of ecore). */
+   EAPI extern int ECORE_EXE_EVENT_DATA; /**< Data from a child process. */
+   EAPI extern int ECORE_EXE_EVENT_ERROR; /**< Errors from a child process. */
+
+   enum _Ecore_Fd_Handler_Flags
+     {
+        ECORE_FD_READ = 1, /**< Fd Read mask */
+        ECORE_FD_WRITE = 2, /**< Fd Write mask */
+        ECORE_FD_ERROR = 4 /**< Fd Error mask */
+     };
+   typedef enum _Ecore_Fd_Handler_Flags Ecore_Fd_Handler_Flags;
+
+   enum _Ecore_Exe_Flags /* flags for executing a child with its stdin and/or stdout piped back */
+     {
+        ECORE_EXE_PIPE_READ = 1, /**< Exe Pipe Read mask */
+        ECORE_EXE_PIPE_WRITE = 2, /**< Exe Pipe Write mask */
+        ECORE_EXE_PIPE_ERROR = 4, /**< Exe Pipe error mask */
+        ECORE_EXE_PIPE_READ_LINE_BUFFERED = 8, /**< Reads are buffered until a newline and delivered 1 event per line */
+        ECORE_EXE_PIPE_ERROR_LINE_BUFFERED = 16, /**< Errors are buffered until a newline and delivered 1 event per line */
+        ECORE_EXE_PIPE_AUTO = 32, /**< stdout and stderr are buffered automatically */
+        ECORE_EXE_RESPAWN = 64, /**< FIXME: Exe is restarted if it dies */
+        ECORE_EXE_USE_SH = 128, /**< Use /bin/sh to run the command. */
+        ECORE_EXE_NOT_LEADER = 256 /**< Do not use setsid() to have the executed process be its own session leader */
+     };
+   typedef enum _Ecore_Exe_Flags Ecore_Exe_Flags;
+
+   enum _Ecore_Exe_Win32_Priority
+     {
+       ECORE_EXE_WIN32_PRIORITY_IDLE, /**< Idle priority, for monitoring the system */
+       ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL, /**< Below default priority */
+       ECORE_EXE_WIN32_PRIORITY_NORMAL, /**< Default priority */
+       ECORE_EXE_WIN32_PRIORITY_ABOVE_NORMAL, /**< Above default priority */
+       ECORE_EXE_WIN32_PRIORITY_HIGH, /**< High priority, use with care as other threads in the system will not get processor time */
+       ECORE_EXE_WIN32_PRIORITY_REALTIME /**< Realtime priority, should be almost never used as it can interrupt system threads that manage mouse input, keyboard input, and background disk flushing */
+     };
+   typedef enum _Ecore_Exe_Win32_Priority Ecore_Exe_Win32_Priority;
+
+   enum _Ecore_Poller_Type /* Poller types */
+     {
+        ECORE_POLLER_CORE = 0 /**< The core poller interval */
+     };
+   typedef enum _Ecore_Poller_Type Ecore_Poller_Type;
+
+   typedef struct _Ecore_Exe                   Ecore_Exe; /**< A handle for spawned processes */
+   typedef struct _Ecore_Timer                 Ecore_Timer; /**< A handle for timers */
+   typedef struct _Ecore_Idler                 Ecore_Idler; /**< A handle for idlers */
+   typedef struct _Ecore_Idle_Enterer          Ecore_Idle_Enterer; /**< A handle for idle enterers */
+   typedef struct _Ecore_Idle_Exiter           Ecore_Idle_Exiter; /**< A handle for idle exiters */
+   typedef struct _Ecore_Fd_Handler            Ecore_Fd_Handler; /**< A handle for Fd handlers */
+   typedef struct _Ecore_Win32_Handler         Ecore_Win32_Handler; /**< A handle for HANDLE handlers on Windows */
+   typedef struct _Ecore_Event_Handler         Ecore_Event_Handler; /**< A handle for an event handler */
+   typedef struct _Ecore_Event_Filter          Ecore_Event_Filter; /**< A handle for an event filter */
+   typedef struct _Ecore_Event                 Ecore_Event; /**< A handle for an event */
+   typedef struct _Ecore_Animator              Ecore_Animator; /**< A handle for animators */
+   typedef struct _Ecore_Pipe                  Ecore_Pipe; /**< A handle for pipes */
+   typedef struct _Ecore_Poller                Ecore_Poller; /**< A handle for pollers */
+   typedef struct _Ecore_Event_Signal_User     Ecore_Event_Signal_User; /**< User signal event */
+   typedef struct _Ecore_Event_Signal_Hup      Ecore_Event_Signal_Hup; /**< Hup signal event */
+   typedef struct _Ecore_Event_Signal_Exit     Ecore_Event_Signal_Exit; /**< Exit signal event */
+   typedef struct _Ecore_Event_Signal_Power    Ecore_Event_Signal_Power; /**< Power signal event */
+   typedef struct _Ecore_Event_Signal_Realtime Ecore_Event_Signal_Realtime; /**< Realtime signal event */
+   typedef struct _Ecore_Exe_Event_Add         Ecore_Exe_Event_Add; /**< Spawned Exe add event */
+   typedef struct _Ecore_Exe_Event_Del         Ecore_Exe_Event_Del; /**< Spawned Exe exit event */
+   typedef struct _Ecore_Exe_Event_Data_Line   Ecore_Exe_Event_Data_Line; /**< Lines from a child process */
+   typedef struct _Ecore_Exe_Event_Data        Ecore_Exe_Event_Data; /**< Data from a child process */
+   typedef struct _Ecore_Thread                Ecore_Thread;
+
+   /**
+    * @typedef Ecore_Data_Cb Ecore_Data_Cb
+    * A callback which is used to return data to the main function
+    */
+   typedef void *(*Ecore_Data_Cb) (void *data);
+   /**
+    * @typedef Ecore_Filter_Cb
+    * A callback used for filtering events from the main loop.
+    */
+   typedef Eina_Bool (*Ecore_Filter_Cb) (void *data, void *loop_data, int type, void *event);
+   /**
+    * @typedef Ecore_Eselect_Function Ecore_Eselect_Function
+    * A function which can be used to replace select() in the main loop
+    */
+   typedef int (*Ecore_Select_Function)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+   /**
+    * @typedef Ecore_End_Cb Ecore_End_Cb
+    * This is the callback which is called at the end of a function, usually for cleanup purposes.
+    */
+   typedef void (*Ecore_End_Cb) (void *user_data, void *func_data);
+   /**
+    * @typedef Ecore_Pipe_Cb Ecore_Pipe_Cb
+    * The callback that data written to the pipe is sent to.
+    */
+   typedef void (*Ecore_Pipe_Cb) (void *data, void *buffer, unsigned int nbyte);
+   /**
+    * @typedef Ecore_Exe_Cb Ecore_Exe_Cb
+    * A callback to run with the associated @ref Ecore_Exe, usually for cleanup purposes.
+    */
+   typedef void (*Ecore_Exe_Cb)(void *data, const Ecore_Exe *exe);
+   /**
+    * @typedef Ecore_Event_Handler_Cb Ecore_Event_Handler_Cb
+    * A callback used by the main loop to handle events of a specified type.
+    */
+   typedef Eina_Bool (*Ecore_Event_Handler_Cb) (void *data, int type, void *event);
+   /**
+    * @typedef Ecore_Thread_Heavy_Cb Ecore_Thread_Heavy_Cb
+    * A callback used to run cpu intensive or blocking I/O operations.
+    */
+   typedef void (*Ecore_Thread_Heavy_Cb) (Ecore_Thread *thread, void *data);
+   /**
+    * @typedef Ecore_Thread_Notify_Cb Ecore_Thread_Notify_Cb
+    * A callback used by the main loop to receive data sent by an @ref Ecore_Thread.
+    */
+   typedef void (*Ecore_Thread_Notify_Cb) (Ecore_Thread *thread, void *msg_data, void *data);
+   /**
+    * @typedef Ecore_Task_Cb Ecore_Task_Cb
+    * A callback run for a task (timer, idler, poller, animater, etc)
+    */
+   typedef Eina_Bool (*Ecore_Task_Cb) (void *data);
+   /**
+    * @typedef Ecore_Cb Ecore_Cb
+    * A generic callback called as a hook when a certain point in execution is reached.
+    */
+   typedef void (*Ecore_Cb) (void *data);
+   /**
+    * @typedef Ecore_Fd_Cb Ecore_Fd_Cb
+    * A callback used by an @ref Ecore_Fd_Handler.
+    */
+   typedef Eina_Bool (*Ecore_Fd_Cb) (void *data, Ecore_Fd_Handler *fd_handler);
+   /**
+    * @typedef Ecore_Fd_Prep_Cb Ecore_Fd_Prep_Cb
+    * A callback used by an @ref Ecore_Fd_Handler.
+    */
+   typedef void (*Ecore_Fd_Prep_Cb) (void *data, Ecore_Fd_Handler *fd_handler);
+   /**
+    * @typedef Ecore_Fd_Win32_Cb Ecore_Fd_Win32_Cb
+    * A callback used by an @ref Ecore_Win32_Handler.
+    */
+   typedef Eina_Bool (*Ecore_Fd_Win32_Cb) (void *data, Ecore_Win32_Handler *wh);
+
+
+   typedef struct _Ecore_Job Ecore_Job; /**< A job handle */
+
+   struct _Ecore_Event_Signal_User /** User signal event */
+     {
+        int   number; /**< The signal number. Either 1 or 2 */
+        void *ext_data; /**< Extension data - not used */
+
+#ifndef _WIN32
+        siginfo_t data; /**< Signal info */
+#endif
+     };
+
+   struct _Ecore_Event_Signal_Hup /** Hup signal event */
+     {
+        void *ext_data; /**< Extension data - not used */
+
+#ifndef _WIN32
+        siginfo_t data; /**< Signal info */
+#endif
+     };
+
+   struct _Ecore_Event_Signal_Exit /** Exit request event */
+     {
+        unsigned int   interrupt : 1; /**< Set if the exit request was an interrupt  signal*/
+        unsigned int   quit      : 1; /**< set if the exit request was a quit signal */
+        unsigned int   terminate : 1; /**< Set if the exit request was a terminate singal */
+        void          *ext_data; /**< Extension data - not used */
+
+#ifndef _WIN32
+        siginfo_t data; /**< Signal info */
+#endif
+     };
+
+   struct _Ecore_Event_Signal_Power /** Power event */
+     {
+        void *ext_data; /**< Extension data - not used */
+
+#ifndef _WIN32
+        siginfo_t data; /**< Signal info */
+#endif
+     };
+
+   struct _Ecore_Event_Signal_Realtime /** Realtime event */
+     {
+        int num; /**< The realtime signal's number */
+
+#ifndef _WIN32
+        siginfo_t data; /**< Signal info */
+#endif
+     };
+
+   struct _Ecore_Exe_Event_Add /** Process add event */
+     {
+        Ecore_Exe *exe; /**< The handle to the added process */
+        void      *ext_data; /**< Extension data - not used */
+     };
+
+   struct _Ecore_Exe_Event_Del /** Process exit event */
+     {
+        pid_t         pid; /**< The process ID of the process that exited */
+        int           exit_code; /**< The exit code of the process */
+        Ecore_Exe    *exe; /**< The handle to the exited process, or NULL if not found */
+        int           exit_signal; /** < The signal that caused the process to exit */
+        unsigned int  exited    : 1; /** < set to 1 if the process exited of its own accord */
+        unsigned int  signalled : 1; /** < set to 1 id the process exited due to uncaught signal */
+        void         *ext_data; /**< Extension data - not used */
+#ifndef _WIN32
+        siginfo_t     data; /**< Signal info */
+#endif
+     };
+
+   struct _Ecore_Exe_Event_Data_Line /**< Lines from a child process */
+      {
+         char *line;
+         int   size;
+      };
+
+   struct _Ecore_Exe_Event_Data /** Data from a child process event */
+     {
+        Ecore_Exe *exe; /**< The handle to the process */
+        void *data; /**< the raw binary data from the child process that was received */
+        int   size; /**< the size of this data in bytes */
+        Ecore_Exe_Event_Data_Line *lines; /**< an array of line data if line buffered, the last one has it's line member set to NULL */
+     };
+
+   EAPI int  ecore_init(void);
+   EAPI int  ecore_shutdown(void);
+
+   EAPI void ecore_app_args_set(int argc, const char **argv);
+   EAPI void ecore_app_args_get(int *argc, char ***argv);
+   EAPI void ecore_app_restart(void);
+
+   EAPI Ecore_Event_Handler *ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data);
+   EAPI void                *ecore_event_handler_del(Ecore_Event_Handler *event_handler);
+   EAPI Ecore_Event         *ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data);
+   EAPI void                *ecore_event_del(Ecore_Event *event);
+   EAPI int                  ecore_event_type_new(void);
+   EAPI Ecore_Event_Filter  *ecore_event_filter_add(Ecore_Data_Cb func_start, Ecore_Filter_Cb func_filter, Ecore_End_Cb func_end, const void *data);
+   EAPI void                *ecore_event_filter_del(Ecore_Event_Filter *ef);
+   EAPI int                  ecore_event_current_type_get(void);
+   EAPI void                *ecore_event_current_event_get(void);
+
+
+   EAPI void        ecore_exe_run_priority_set(int pri);
+   EAPI int         ecore_exe_run_priority_get(void);
+   EAPI Ecore_Exe  *ecore_exe_run(const char *exe_cmd, const void *data);
+   EAPI Ecore_Exe  *ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data);
+   EAPI void        ecore_exe_callback_pre_free_set(Ecore_Exe *exe, Ecore_Exe_Cb func);
+   EAPI Eina_Bool   ecore_exe_send(Ecore_Exe *exe, const void *data, int size);
+   EAPI void        ecore_exe_close_stdin(Ecore_Exe *exe);
+   EAPI void        ecore_exe_auto_limits_set(Ecore_Exe *exe, int start_bytes, int end_bytes, int start_lines, int end_lines);
+   EAPI Ecore_Exe_Event_Data *ecore_exe_event_data_get(Ecore_Exe *exe, Ecore_Exe_Flags flags);
+   EAPI void        ecore_exe_event_data_free(Ecore_Exe_Event_Data *data);
+   EAPI void       *ecore_exe_free(Ecore_Exe *exe);
+   EAPI pid_t       ecore_exe_pid_get(const Ecore_Exe *exe);
+   EAPI void        ecore_exe_tag_set(Ecore_Exe *exe, const char *tag);
+   EAPI const char *ecore_exe_tag_get(const Ecore_Exe *exe);
+   EAPI const char *ecore_exe_cmd_get(const Ecore_Exe *exe);
+   EAPI void       *ecore_exe_data_get(const Ecore_Exe *exe);
+   EAPI Ecore_Exe_Flags ecore_exe_flags_get(const Ecore_Exe *exe);
+   EAPI void        ecore_exe_pause(Ecore_Exe *exe);
+   EAPI void        ecore_exe_continue(Ecore_Exe *exe);
+   EAPI void        ecore_exe_interrupt(Ecore_Exe *exe);
+   EAPI void        ecore_exe_quit(Ecore_Exe *exe);
+   EAPI void        ecore_exe_terminate(Ecore_Exe *exe);
+   EAPI void        ecore_exe_kill(Ecore_Exe *exe);
+   EAPI void        ecore_exe_signal(Ecore_Exe *exe, int num);
+   EAPI void        ecore_exe_hup(Ecore_Exe *exe);
+
+   EAPI Ecore_Idler *ecore_idler_add(Ecore_Task_Cb func, const void *data);
+   EAPI void        *ecore_idler_del(Ecore_Idler *idler);
+
+   EAPI Ecore_Idle_Enterer *ecore_idle_enterer_add(Ecore_Task_Cb func, const void *data);
+   EAPI Ecore_Idle_Enterer *ecore_idle_enterer_before_add(Ecore_Task_Cb func, const void *data);
+   EAPI void               *ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer);
+
+   EAPI Ecore_Idle_Exiter *ecore_idle_exiter_add(Ecore_Task_Cb func, const void *data);
+   EAPI void              *ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter);
+
+   EAPI void              ecore_main_loop_iterate(void);
+
+   EAPI void              ecore_main_loop_select_func_set(Ecore_Select_Function func);
+   EAPI void             *ecore_main_loop_select_func_get(void);
+
+   EAPI Eina_Bool         ecore_main_loop_glib_integrate(void);
+   EAPI void              ecore_main_loop_glib_always_integrate_disable(void);
+       
+   EAPI void              ecore_main_loop_begin(void);
+   EAPI void              ecore_main_loop_quit(void);
+   EAPI Ecore_Fd_Handler *ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data,
+                                                    Ecore_Fd_Cb buf_func, const void *buf_data);
+   EAPI void              ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data);
+   EAPI void             *ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler);
+   EAPI int               ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler);
+   EAPI Eina_Bool         ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags);
+   EAPI void              ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags);
+
+   EAPI Ecore_Win32_Handler *ecore_main_win32_handler_add(void *h, Ecore_Fd_Win32_Cb func, const void *data);
+   EAPI void                *ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler);
+
+   EAPI Ecore_Pipe  *ecore_pipe_add(Ecore_Pipe_Cb handler, const void *data);
+   EAPI void        *ecore_pipe_del(Ecore_Pipe *p);
+   EAPI Eina_Bool    ecore_pipe_write(Ecore_Pipe *p, const void *buffer, unsigned int nbytes);
+   EAPI void         ecore_pipe_write_close(Ecore_Pipe *p);
+   EAPI void         ecore_pipe_read_close(Ecore_Pipe *p);
+
+
+
+   EAPI Ecore_Thread *ecore_thread_run(Ecore_Cb,
+                                       Ecore_Cb,
+                                       Ecore_Cb,
+                                       const void *data);
+   EAPI Ecore_Thread *ecore_thread_feedback_run(Ecore_Thread_Heavy_Cb,
+                                                Ecore_Thread_Notify_Cb,
+                                                Ecore_Cb,
+                                                Ecore_Cb,
+                                                const void *data,
+                                                Eina_Bool try_no_queue);
+   EAPI Eina_Bool     ecore_thread_cancel(Ecore_Thread *thread);
+   EAPI Eina_Bool     ecore_thread_check(Ecore_Thread *thread);
+   EAPI Eina_Bool     ecore_thread_feedback(Ecore_Thread *thread, const void *msg_data);
+   EAPI int           ecore_thread_active_get(void);
+   EAPI int           ecore_thread_pending_get(void);
+   EAPI int           ecore_thread_pending_feedback_get(void);
+   EAPI int           ecore_thread_pending_total_get(void);
+   EAPI int           ecore_thread_max_get(void);
+   EAPI void          ecore_thread_max_set(int num);
+   EAPI void          ecore_thread_max_reset(void);
+   EAPI int           ecore_thread_available_get(void);
+
+   EAPI Eina_Bool     ecore_thread_local_data_add(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct);
+   EAPI void         *ecore_thread_local_data_set(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb);
+   EAPI void   *ecore_thread_local_data_find(Ecore_Thread *thread, const char *key);
+   EAPI Eina_Bool     ecore_thread_local_data_del(Ecore_Thread *thread, const char *key);
+
+   EAPI Eina_Bool     ecore_thread_global_data_add(const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct);
+   EAPI void         *ecore_thread_global_data_set(const char *key, void *value, Eina_Free_Cb cb);
+   EAPI void   *ecore_thread_global_data_find(const char *key);
+   EAPI Eina_Bool     ecore_thread_global_data_del(const char *key);
+   EAPI void   *ecore_thread_global_data_wait(const char *key, double seconds);
+
+
+
+
+   EAPI double ecore_time_get(void);
+   EAPI double ecore_time_unix_get(void);
+   EAPI double ecore_loop_time_get(void);
+
+   EAPI Ecore_Timer *ecore_timer_add(double in, Ecore_Task_Cb func, const void *data);
+   EAPI Ecore_Timer *ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data);
+   EAPI void        *ecore_timer_del(Ecore_Timer *timer);
+   EAPI void         ecore_timer_interval_set(Ecore_Timer *timer, double in);
+   EAPI double       ecore_timer_interval_get(Ecore_Timer *timer);
+   EAPI void         ecore_timer_freeze(Ecore_Timer *timer);
+   EAPI void         ecore_timer_thaw(Ecore_Timer *timer);
+   EAPI void         ecore_timer_delay(Ecore_Timer *timer, double add);
+   EAPI double       ecore_timer_pending_get(Ecore_Timer *timer);
+
+   EAPI double       ecore_timer_precision_get(void);
+   EAPI void         ecore_timer_precision_set(double precision);
+
+   EAPI Ecore_Animator *ecore_animator_add(Ecore_Task_Cb func, const void *data);
+   EAPI void           *ecore_animator_del(Ecore_Animator *animator);
+   EAPI void            ecore_animator_freeze(Ecore_Animator *animator);
+   EAPI void            ecore_animator_thaw(Ecore_Animator *animator);
+   EAPI void            ecore_animator_frametime_set(double frametime);
+   EAPI double          ecore_animator_frametime_get(void);
+
+   EAPI void          ecore_poller_poll_interval_set(Ecore_Poller_Type type, double poll_time);
+   EAPI double        ecore_poller_poll_interval_get(Ecore_Poller_Type type);
+   EAPI Eina_Bool     ecore_poller_poller_interval_set(Ecore_Poller *poller, int interval);
+   EAPI int           ecore_poller_poller_interval_get(Ecore_Poller *poller);
+   EAPI Ecore_Poller *ecore_poller_add(Ecore_Poller_Type type, int interval, Ecore_Task_Cb func, const void *data);
+   EAPI void         *ecore_poller_del(Ecore_Poller *poller);
+
+   EAPI Ecore_Job *ecore_job_add(Ecore_Cb func, const void *data);
+   EAPI void      *ecore_job_del(Ecore_Job *job);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/tests/suite/ecore/src/lib/Ecore_Getopt.h b/tests/suite/ecore/src/lib/Ecore_Getopt.h
new file mode 100644 (file)
index 0000000..18a8459
--- /dev/null
@@ -0,0 +1,403 @@
+#ifndef _ECORE_GETOPT_H
+#define _ECORE_GETOPT_H
+
+#include <stdio.h>
+#include <Eina.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ECORE_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_ECORE_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+/**
+ * @file Ecore_Getopt.h
+ * @brief Contains powerful getopt replacement.
+ *
+ * This replacement handles both short (-X) or long options (--ABC)
+ * options, with various actions supported, like storing one value and
+ * already converting to required type, counting number of
+ * occurrences, setting true or false values, show help, license,
+ * copyright and even support user-defined callbacks.
+ *
+ * It is provided a set of C Pre Processor macros so definition is
+ * straightforward.
+ *
+ * Values will be stored elsewhere indicated by an array of pointers
+ * to values, it is given in separate to parser description so you can
+ * use multiple values with the same parser.
+ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  typedef enum {
+    ECORE_GETOPT_ACTION_STORE,
+    ECORE_GETOPT_ACTION_STORE_CONST,
+    ECORE_GETOPT_ACTION_STORE_TRUE,
+    ECORE_GETOPT_ACTION_STORE_FALSE,
+    ECORE_GETOPT_ACTION_CHOICE,
+    ECORE_GETOPT_ACTION_APPEND,
+    ECORE_GETOPT_ACTION_COUNT,
+    ECORE_GETOPT_ACTION_CALLBACK,
+    ECORE_GETOPT_ACTION_HELP,
+    ECORE_GETOPT_ACTION_VERSION,
+    ECORE_GETOPT_ACTION_COPYRIGHT,
+    ECORE_GETOPT_ACTION_LICENSE
+  } Ecore_Getopt_Action;
+
+  typedef enum {
+    ECORE_GETOPT_TYPE_STR,
+    ECORE_GETOPT_TYPE_BOOL,
+    ECORE_GETOPT_TYPE_SHORT,
+    ECORE_GETOPT_TYPE_INT,
+    ECORE_GETOPT_TYPE_LONG,
+    ECORE_GETOPT_TYPE_USHORT,
+    ECORE_GETOPT_TYPE_UINT,
+    ECORE_GETOPT_TYPE_ULONG,
+    ECORE_GETOPT_TYPE_DOUBLE
+  } Ecore_Getopt_Type;
+
+  typedef enum {
+    ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO = 0,
+    ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES = 1,
+    ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL = 3
+  } Ecore_Getopt_Desc_Arg_Requirement;
+
+  typedef union _Ecore_Getopt_Value             Ecore_Getopt_Value;
+
+  typedef struct _Ecore_Getopt_Desc_Store       Ecore_Getopt_Desc_Store;
+  typedef struct _Ecore_Getopt_Desc_Callback    Ecore_Getopt_Desc_Callback;
+  typedef struct _Ecore_Getopt_Desc             Ecore_Getopt_Desc;
+  typedef struct _Ecore_Getopt                  Ecore_Getopt;
+
+  union _Ecore_Getopt_Value
+  {
+     char **strp;
+     unsigned char *boolp;
+     short *shortp;
+     int *intp;
+     long *longp;
+     unsigned short *ushortp;
+     unsigned int *uintp;
+     unsigned long *ulongp;
+     double *doublep;
+     Eina_List **listp;
+     void **ptrp;
+  };
+
+  struct _Ecore_Getopt_Desc_Store
+  {
+     Ecore_Getopt_Type type;           /**< type of data being handled */
+     Ecore_Getopt_Desc_Arg_Requirement arg_req;
+     union
+     {
+        const char *strv;
+        unsigned char boolv;
+        short shortv;
+        int intv;
+        long longv;
+        unsigned short ushortv;
+        unsigned int uintv;
+        unsigned long ulongv;
+        double doublev;
+     } def;
+  };
+
+  struct _Ecore_Getopt_Desc_Callback
+  {
+     unsigned char (*func)(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage);
+     const void *data;
+     Ecore_Getopt_Desc_Arg_Requirement arg_req;
+     const char *def;
+  };
+
+  struct _Ecore_Getopt_Desc
+  {
+     char shortname;       /**< used with a single dash */
+     const char *longname; /**< used with double dashes */
+     const char *help;     /**< used by --help/ecore_getopt_help() */
+     const char *metavar;  /**< used by ecore_getopt_help() with nargs > 0 */
+
+     Ecore_Getopt_Action action; /**< define how to handle it */
+     union
+     {
+        const Ecore_Getopt_Desc_Store store;
+        const void *store_const;
+        const char *const *choices; /* NULL terminated. */
+        const Ecore_Getopt_Type append_type;
+        const Ecore_Getopt_Desc_Callback callback;
+        const void *dummy;
+     } action_param;
+  };
+
+  struct _Ecore_Getopt
+  {
+     const char *prog; /**< to be used when ecore_app_args_get() fails */
+     const char *usage; /**< usage example, %prog is replaced */
+     const char *version; /**< if exists, --version will work */
+     const char *copyright; /**< if exists, --copyright will work */
+     const char *license; /**< if exists, --license will work */
+     const char *description; /**< long description, possible multiline */
+     unsigned char strict : 1; /**< fail on errors */
+     const Ecore_Getopt_Desc descs[]; /* NULL terminated. */
+  };
+
+#define ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, arg_requirement, default_value) \
+  {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_STORE,        \
+       {.store = {type, arg_requirement, default_value}}}
+
+#define ECORE_GETOPT_STORE(shortname, longname, help, type)             \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type,        \
+                          ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {})
+
+#define ECORE_GETOPT_STORE_STR(shortname, longname, help)               \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_STR)
+#define ECORE_GETOPT_STORE_BOOL(shortname, longname, help)              \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_BOOL)
+#define ECORE_GETOPT_STORE_SHORT(shortname, longname, help)             \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_SHORT)
+#define ECORE_GETOPT_STORE_INT(shortname, longname, help)               \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_INT)
+#define ECORE_GETOPT_STORE_LONG(shortname, longname, help)              \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_LONG)
+#define ECORE_GETOPT_STORE_USHORT(shortname, longname, help)            \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_USHORT)
+#define ECORE_GETOPT_STORE_UINT(shortname, longname, help)              \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_UINT)
+#define ECORE_GETOPT_STORE_ULONG(shortname, longname, help)             \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_ULONG)
+#define ECORE_GETOPT_STORE_DOUBLE(shortname, longname, help)            \
+  ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_DOUBLE)
+
+
+#define ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, type) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type,        \
+                          ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {})
+
+#define ECORE_GETOPT_STORE_METAVAR_STR(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_STR)
+#define ECORE_GETOPT_STORE_METAVAR_BOOL(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_BOOL)
+#define ECORE_GETOPT_STORE_METAVAR_SHORT(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_SHORT)
+#define ECORE_GETOPT_STORE_METAVAR_INT(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_INT)
+#define ECORE_GETOPT_STORE_METAVAR_LONG(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_LONG)
+#define ECORE_GETOPT_STORE_METAVAR_USHORT(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_USHORT)
+#define ECORE_GETOPT_STORE_METAVAR_UINT(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_UINT)
+#define ECORE_GETOPT_STORE_METAVAR_ULONG(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_ULONG)
+#define ECORE_GETOPT_STORE_METAVAR_DOUBLE(shortname, longname, help, metavar) \
+  ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_DOUBLE)
+
+
+#define ECORE_GETOPT_STORE_DEF(shortname, longname, help, type, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type,               \
+                          ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL,          \
+                          default_value)
+
+#define ECORE_GETOPT_STORE_DEF_STR(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                          \
+                         ECORE_GETOPT_TYPE_STR,                              \
+                         {.strv = default_value})
+#define ECORE_GETOPT_STORE_DEF_BOOL(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                           \
+                         ECORE_GETOPT_TYPE_BOOL,                              \
+                         {.boolv = default_value})
+#define ECORE_GETOPT_STORE_DEF_SHORT(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                            \
+                         ECORE_GETOPT_TYPE_SHORT,                              \
+                         {.shortv = default_value})
+#define ECORE_GETOPT_STORE_DEF_INT(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                          \
+                         ECORE_GETOPT_TYPE_INT,                              \
+                         {.intv = default_value})
+#define ECORE_GETOPT_STORE_DEF_LONG(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                           \
+                         ECORE_GETOPT_TYPE_LONG,                              \
+                         {.longv = default_value})
+#define ECORE_GETOPT_STORE_DEF_USHORT(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                             \
+                         ECORE_GETOPT_TYPE_USHORT,                              \
+                         {.ushortv = default_value})
+#define ECORE_GETOPT_STORE_DEF_UINT(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                           \
+                         ECORE_GETOPT_TYPE_UINT,                              \
+                         {.uintv = default_value})
+#define ECORE_GETOPT_STORE_DEF_ULONG(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                            \
+                         ECORE_GETOPT_TYPE_ULONG,                              \
+                         {.ulongv = default_value})
+#define ECORE_GETOPT_STORE_DEF_DOUBLE(shortname, longname, help, default_value) \
+  ECORE_GETOPT_STORE_DEF(shortname, longname, help,                             \
+                         ECORE_GETOPT_TYPE_DOUBLE,                              \
+                         {.doublev = default_value})
+
+#define ECORE_GETOPT_STORE_FULL_STR(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                           \
+                          ECORE_GETOPT_TYPE_STR,                                                        \
+                          arg_requirement,                                                               \
+                          {.strv = default_value})
+#define ECORE_GETOPT_STORE_FULL_BOOL(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                            \
+                          ECORE_GETOPT_TYPE_BOOL,                                                        \
+                          arg_requirement,                                                               \
+                          {.boolv = default_value})
+#define ECORE_GETOPT_STORE_FULL_SHORT(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                             \
+                          ECORE_GETOPT_TYPE_SHORT,                                                        \
+                          arg_requirement,                                                                \
+                          {.shortv = default_value})
+#define ECORE_GETOPT_STORE_FULL_INT(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                           \
+                          ECORE_GETOPT_TYPE_INT,                                                        \
+                          arg_requirement,                                                              \
+                          {.intv = default_value})
+#define ECORE_GETOPT_STORE_FULL_LONG(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                            \
+                          ECORE_GETOPT_TYPE_LONG,                                                        \
+                          arg_requirement,                                                               \
+                          {.longv = default_value})
+#define ECORE_GETOPT_STORE_FULL_USHORT(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                              \
+                          ECORE_GETOPT_TYPE_USHORT,                                                        \
+                          arg_requirement,                                                                 \
+                          {.ushortv = default_value})
+#define ECORE_GETOPT_STORE_FULL_UINT(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                            \
+                          ECORE_GETOPT_TYPE_UINT,                                                        \
+                          arg_requirement,                                                               \
+                          {.uintv = default_value})
+#define ECORE_GETOPT_STORE_FULL_ULONG(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                             \
+                          ECORE_GETOPT_TYPE_ULONG,                                                        \
+                          arg_requirement,                                                                \
+                          {.ulongv = default_value})
+#define ECORE_GETOPT_STORE_FULL_DOUBLE(shortname, longname, help, metavar, arg_requirement, default_value) \
+  ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar,                                              \
+                          ECORE_GETOPT_TYPE_DOUBLE,                                                        \
+                          arg_requirement,                                                                 \
+                          {.doublev = default_value})
+
+#define ECORE_GETOPT_STORE_CONST(shortname, longname, help, value)   \
+  {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_CONST, \
+       {.store_const = value}}
+#define ECORE_GETOPT_STORE_TRUE(shortname, longname, help)          \
+  {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_TRUE, \
+       {.dummy = NULL}}
+#define ECORE_GETOPT_STORE_FALSE(shortname, longname, help)          \
+  {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_FALSE, \
+       {.dummy = NULL}}
+
+#define ECORE_GETOPT_CHOICE(shortname, longname, help, choices_array) \
+  {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_CHOICE,       \
+       {.choices = choices_array}}
+#define ECORE_GETOPT_CHOICE_METAVAR(shortname, longname, help, metavar, choices_array) \
+  {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CHOICE,                     \
+       {.choices = choices_array}}
+
+
+#define ECORE_GETOPT_APPEND(shortname, longname, help, sub_type) \
+  {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_APPEND,  \
+       {.append_type = sub_type}}
+#define ECORE_GETOPT_APPEND_METAVAR(shortname, longname, help, metavar, type) \
+  {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_APPEND,            \
+       {.append_type = type}}
+
+#define ECORE_GETOPT_COUNT(shortname, longname, help)          \
+  {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_COUNT, \
+       {.dummy = NULL}}
+
+#define ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, callback_func, callback_data, argument_requirement, default_value) \
+  {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CALLBACK,                                                                      \
+       {.callback = {callback_func, callback_data,                                                                                        \
+                     argument_requirement, default_value}}}
+#define ECORE_GETOPT_CALLBACK_NOARGS(shortname, longname, help, callback_func, callback_data) \
+  ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, NULL,                                 \
+                             callback_func, callback_data,                                    \
+                             ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO,                            \
+                             NULL)
+#define ECORE_GETOPT_CALLBACK_ARGS(shortname, longname, help, metavar, callback_func, callback_data) \
+  ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar,                                     \
+                             callback_func, callback_data,                                           \
+                             ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES,                                  \
+                             NULL)
+
+#define ECORE_GETOPT_HELP(shortname, longname)      \
+  {shortname, longname, "show this message.", NULL, \
+       ECORE_GETOPT_ACTION_HELP,                    \
+       {.dummy = NULL}}
+
+#define ECORE_GETOPT_VERSION(shortname, longname)      \
+  {shortname, longname, "show program version.", NULL, \
+       ECORE_GETOPT_ACTION_VERSION,                    \
+       {.dummy = NULL}}
+
+#define ECORE_GETOPT_COPYRIGHT(shortname, longname) \
+  {shortname, longname, "show copyright.", NULL,    \
+       ECORE_GETOPT_ACTION_COPYRIGHT,               \
+       {.dummy = NULL}}
+
+#define ECORE_GETOPT_LICENSE(shortname, longname) \
+  {shortname, longname, "show license.", NULL,    \
+       ECORE_GETOPT_ACTION_LICENSE,               \
+       {.dummy = NULL}}
+
+#define ECORE_GETOPT_SENTINEL {0, NULL, NULL, NULL, 0, {.dummy = NULL}}
+
+#define ECORE_GETOPT_VALUE_STR(val)      {.strp = &(val)}
+#define ECORE_GETOPT_VALUE_BOOL(val)     {.boolp = &(val)}
+#define ECORE_GETOPT_VALUE_SHORT(val)    {.shortp = &(val)}
+#define ECORE_GETOPT_VALUE_INT(val)      {.intp = &(val)}
+#define ECORE_GETOPT_VALUE_LONG(val)     {.longp = &(val)}
+#define ECORE_GETOPT_VALUE_USHORT(val)   {.ushortp = &(val)}
+#define ECORE_GETOPT_VALUE_UINT(val)     {.uintp = &(val)}
+#define ECORE_GETOPT_VALUE_ULONG(val)    {.ulongp = &(val)}
+#define ECORE_GETOPT_VALUE_DOUBLE(val)   {.doublep = &(val)}
+#define ECORE_GETOPT_VALUE_PTR(val)      {.ptrp = &(val)}
+#define ECORE_GETOPT_VALUE_PTR_CAST(val) {.ptrp = (void **)&(val)}
+#define ECORE_GETOPT_VALUE_LIST(val)     {.listp = &(val)}
+#define ECORE_GETOPT_VALUE_NONE          {.ptrp = NULL}
+
+  EAPI void ecore_getopt_help(FILE *fp, const Ecore_Getopt *info);
+
+  EAPI unsigned char ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser);
+  EAPI int ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv);
+
+  EAPI Eina_List *ecore_getopt_list_free(Eina_List *list);
+
+  /* helper functions to be used with ECORE_GETOPT_CALLBACK_*() */
+  EAPI unsigned char ecore_getopt_callback_geometry_parse(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage);
+  EAPI unsigned char ecore_getopt_callback_size_parse(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _ECORE_GETOPT_H */
diff --git a/tests/suite/ecore/src/lib/ecore.c b/tests/suite/ecore/src/lib/ecore.c
new file mode 100644 (file)
index 0000000..5b0789c
--- /dev/null
@@ -0,0 +1,418 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+#include <Eina.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#if HAVE_MALLINFO
+#include <malloc.h>
+
+static Ecore_Version _version = { VERS_MAJ, VERS_MIN, VERS_MIC, VERS_REV };
+EAPI Ecore_Version *ecore_version = &_version;
+
+#define KEEP_MAX(Global, Local)                        \
+   if (Global < (Local))                        \
+     Global = Local;
+
+static Eina_Bool _ecore_memory_statistic(void *data);
+static int _ecore_memory_max_total = 0;
+static int _ecore_memory_max_free = 0;
+static pid_t _ecore_memory_pid = 0;
+#endif
+
+static const char *_ecore_magic_string_get(Ecore_Magic m);
+static int _ecore_init_count = 0;
+int _ecore_log_dom = -1;
+int _ecore_fps_debug = 0;
+
+/** OpenBSD does not define CODESET
+ * FIXME ??
+ */
+
+#ifndef CODESET
+# define CODESET "INVALID"
+#endif
+
+/**
+ * Set up connections, signal handlers, sockets etc.
+ * @return 1 or greater on success, 0 otherwise
+ *
+ * This function sets up all singal handlers and the basic event loop. If it
+ * succeeds, 1 will be returned, otherwise 0 will be returned.
+ *
+ * @code
+ * #include <Ecore.h>
+ *
+ * int main(int argc, char **argv)
+ * {
+ *   if (!ecore_init())
+ *   {
+ *     printf("ERROR: Cannot init Ecore!\n");
+ *     return -1;
+ *   }
+ *   ecore_main_loop_begin();
+ *   ecore_shutdown();
+ * }
+ * @endcode
+ */
+EAPI int
+ecore_init(void)
+{
+   if (++_ecore_init_count != 1)
+     return _ecore_init_count;
+
+#ifdef HAVE_LOCALE_H
+   setlocale(LC_CTYPE, "");
+#endif
+   /*
+     if (strcmp(nl_langinfo(CODESET), "UTF-8"))
+     {
+        WRN("Not a utf8 locale!");
+     }
+   */
+#ifdef HAVE_EVIL
+   if (!evil_init())
+     return --_ecore_init_count;
+#endif
+   if (!eina_init())
+     goto shutdown_evil;
+   _ecore_log_dom = eina_log_domain_register("Ecore",ECORE_DEFAULT_LOG_COLOR);
+   if (_ecore_log_dom < 0) {
+     EINA_LOG_ERR("Ecore was unable to create a log domain.");
+     goto shutdown_log_dom;
+   }
+   if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
+   if (_ecore_fps_debug) _ecore_fps_debug_init();
+   _ecore_main_loop_init();
+   _ecore_signal_init();
+   _ecore_exe_init();
+   _ecore_thread_init();
+   _ecore_glib_init();
+   _ecore_job_init();
+   _ecore_time_init();
+
+#if HAVE_MALLINFO
+   if (getenv("ECORE_MEM_STAT"))
+     {
+        _ecore_memory_pid = getpid();
+        ecore_animator_add(_ecore_memory_statistic, NULL);
+     }
+#endif
+
+#if defined(GLIB_INTEGRATION_ALWAYS)
+   if (_ecore_glib_always_integrate) ecore_main_loop_glib_integrate();
+#endif
+   
+   return _ecore_init_count;
+
+ shutdown_log_dom:
+   eina_shutdown();
+ shutdown_evil:
+#ifdef HAVE_EVIL
+   evil_shutdown();
+#endif
+   return --_ecore_init_count;
+}
+
+/**
+ * Shut down connections, signal handlers sockets etc.
+ *
+ * This function shuts down all things set up in ecore_init() and cleans up all
+ * event queues, handlers, filters, timers, idlers, idle enterers/exiters
+ * etc. set up after ecore_init() was called.
+ *
+ * Do not call this function from any callback that may be called from the main
+ * loop, as the main loop will then fall over and not function properly.
+ */
+EAPI int
+ecore_shutdown(void)
+{
+   if (--_ecore_init_count != 0)
+     return _ecore_init_count;
+
+   if (_ecore_fps_debug) _ecore_fps_debug_shutdown();
+   _ecore_poller_shutdown();
+   _ecore_animator_shutdown();
+   _ecore_glib_shutdown();
+   _ecore_job_shutdown();
+   _ecore_thread_shutdown();
+   _ecore_exe_shutdown();
+   _ecore_idle_enterer_shutdown();
+   _ecore_idle_exiter_shutdown();
+   _ecore_idler_shutdown();
+   _ecore_timer_shutdown();
+   _ecore_event_shutdown();
+   _ecore_main_shutdown();
+   _ecore_signal_shutdown();
+   _ecore_main_loop_shutdown();
+
+#if HAVE_MALLINFO
+   if (getenv("ECORE_MEM_STAT"))
+     {
+        _ecore_memory_statistic(NULL);
+
+        ERR("[%i] Memory MAX total: %i, free: %i",
+            _ecore_memory_pid,
+            _ecore_memory_max_total,
+            _ecore_memory_max_free);
+     }
+#endif
+
+   eina_log_domain_unregister(_ecore_log_dom);
+   _ecore_log_dom = -1;
+   eina_shutdown();
+#ifdef HAVE_EVIL
+   evil_shutdown();
+#endif
+
+   return _ecore_init_count;
+}
+
+EAPI void
+ecore_print_warning(const char *function, const char *sparam)
+{
+   WRN("***** Developer Warning ***** :\n"
+       "\tThis program is calling:\n\n"
+       "\t%s();\n\n"
+       "\tWith the parameter:\n\n"
+       "\t%s\n\n"
+       "\tbeing NULL. Please fix your program.", function, sparam);
+   if (getenv("ECORE_ERROR_ABORT")) abort();
+}
+
+EAPI void
+_ecore_magic_fail(const void *d, Ecore_Magic m, Ecore_Magic req_m, const char *fname)
+{
+   ERR("\n"
+       "*** ECORE ERROR: Ecore Magic Check Failed!!!\n"
+       "*** IN FUNCTION: %s()", fname);
+   if (!d)
+     ERR("  Input handle pointer is NULL!");
+   else if (m == ECORE_MAGIC_NONE)
+     ERR("  Input handle has already been freed!");
+   else if (m != req_m)
+     ERR("  Input handle is wrong type\n"
+         "    Expected: %08x - %s\n"
+         "    Supplied: %08x - %s",
+         (unsigned int)req_m, _ecore_magic_string_get(req_m),
+         (unsigned int)m, _ecore_magic_string_get(m));
+     ERR("*** NAUGHTY PROGRAMMER!!!\n"
+         "*** SPANK SPANK SPANK!!!\n"
+         "*** Now go fix your code. Tut tut tut!");
+   if (getenv("ECORE_ERROR_ABORT")) abort();
+}
+
+static const char *
+_ecore_magic_string_get(Ecore_Magic m)
+{
+   switch (m)
+     {
+      case ECORE_MAGIC_NONE:
+        return "None (Freed Object)";
+        break;
+      case ECORE_MAGIC_EXE:
+        return "Ecore_Exe (Executable)";
+        break;
+      case ECORE_MAGIC_TIMER:
+        return "Ecore_Timer (Timer)";
+        break;
+      case ECORE_MAGIC_IDLER:
+        return "Ecore_Idler (Idler)";
+        break;
+      case ECORE_MAGIC_IDLE_ENTERER:
+        return "Ecore_Idle_Enterer (Idler Enterer)";
+        break;
+      case ECORE_MAGIC_IDLE_EXITER:
+        return "Ecore_Idle_Exiter (Idler Exiter)";
+        break;
+      case ECORE_MAGIC_FD_HANDLER:
+        return "Ecore_Fd_Handler (Fd Handler)";
+        break;
+      case ECORE_MAGIC_WIN32_HANDLER:
+        return "Ecore_Win32_Handler (Win32 Handler)";
+        break;
+      case ECORE_MAGIC_EVENT_HANDLER:
+        return "Ecore_Event_Handler (Event Handler)";
+        break;
+      case ECORE_MAGIC_EVENT:
+        return "Ecore_Event (Event)";
+        break;
+      default:
+        return "<UNKNOWN>";
+     };
+}
+
+/* fps debug calls - for debugging how much time your app actually spends */
+/* "running" (and the inverse being time spent running)... this does not */
+/* account for other apps and multitasking... */
+
+static int _ecore_fps_debug_init_count = 0;
+static int _ecore_fps_debug_fd = -1;
+unsigned int *_ecore_fps_runtime_mmap = NULL;
+
+void
+_ecore_fps_debug_init(void)
+{
+   char  buf[4096];
+   const char *tmp;
+   int   pid;
+
+   _ecore_fps_debug_init_count++;
+   if (_ecore_fps_debug_init_count > 1) return;
+
+#ifndef HAVE_EVIL
+   tmp = "/tmp";
+#else
+   tmp = (char *)evil_tmpdir_get ();
+#endif /* HAVE_EVIL */
+   pid = (int)getpid();
+   snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid);
+   _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644);
+   if (_ecore_fps_debug_fd < 0)
+     {
+        unlink(buf);
+        _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644);
+     }
+   if (_ecore_fps_debug_fd >= 0)
+     {
+        unsigned int zero = 0;
+        char *buf = (char *)&zero;
+        ssize_t todo = sizeof(unsigned int);
+
+        while (todo > 0)
+          {
+             ssize_t r = write(_ecore_fps_debug_fd, buf, todo);
+             if (r > 0)
+               {
+                  todo -= r;
+                  buf += r;
+               }
+             else if ((r < 0) && (errno == EINTR))
+               continue;
+             else
+               {
+                  ERR("could not write to file '%s' fd %d: %s",
+                      tmp, _ecore_fps_debug_fd, strerror(errno));
+                  close(_ecore_fps_debug_fd);
+                  _ecore_fps_debug_fd = -1;
+                  return;
+               }
+          }
+        _ecore_fps_runtime_mmap = mmap(NULL, sizeof(unsigned int),
+                                       PROT_READ | PROT_WRITE,
+                                       MAP_SHARED,
+                                       _ecore_fps_debug_fd, 0);
+        if (_ecore_fps_runtime_mmap == MAP_FAILED)
+          _ecore_fps_runtime_mmap = NULL;
+     }
+}
+
+void
+_ecore_fps_debug_shutdown(void)
+{
+   _ecore_fps_debug_init_count--;
+   if (_ecore_fps_debug_init_count > 0) return;
+   if (_ecore_fps_debug_fd >= 0)
+     {
+        char buf[4096];
+        const char *tmp;
+        int   pid;
+
+#ifndef HAVE_EVIL
+   tmp = "/tmp";
+#else
+   tmp = (char *)evil_tmpdir_get ();
+#endif /* HAVE_EVIL */
+   pid = (int)getpid();
+        snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid);
+        unlink(buf);
+        if (_ecore_fps_runtime_mmap)
+          {
+             munmap(_ecore_fps_runtime_mmap, sizeof(int));
+             _ecore_fps_runtime_mmap = NULL;
+          }
+        close(_ecore_fps_debug_fd);
+        _ecore_fps_debug_fd = -1;
+     }
+}
+
+void
+_ecore_fps_debug_runtime_add(double t)
+{
+   if ((_ecore_fps_debug_fd >= 0) &&
+       (_ecore_fps_runtime_mmap))
+     {
+        unsigned int tm;
+
+        tm = (unsigned int)(t * 1000000.0);
+        /* i know its not 100% theoretically guaranteed, but i'd say a write */
+        /* of an int could be considered atomic for all practical purposes */
+        /* oh and since this is cumulative, 1 second = 1,000,000 ticks, so */
+        /* this can run for about 4294 seconds becore looping. if you are */
+        /* doing performance testing in one run for over an hour... well */
+        /* time to restart or handle a loop condition :) */
+        *(_ecore_fps_runtime_mmap) += tm;
+     }
+}
+
+#if HAVE_MALLINFO
+static Eina_Bool
+_ecore_memory_statistic(__UNUSED__ void *data)
+{
+   struct mallinfo mi;
+   static int uordblks = 0;
+   static int fordblks = 0;
+   Eina_Bool changed = EINA_FALSE;
+
+   mi = mallinfo();
+
+#define HAS_CHANGED(Global, Local)                \
+   if (Global != Local)                                \
+     {                                                \
+        Global = Local;                                \
+        changed = EINA_TRUE;                        \
+     }
+
+   HAS_CHANGED(uordblks, mi.uordblks);
+   HAS_CHANGED(fordblks, mi.fordblks);
+
+   if (changed)
+     ERR("[%i] Memory total: %i, free: %i",
+         _ecore_memory_pid,
+         mi.uordblks,
+         mi.fordblks);
+
+   KEEP_MAX(_ecore_memory_max_total, mi.uordblks);
+   KEEP_MAX(_ecore_memory_max_free, mi.fordblks);
+
+   return ECORE_CALLBACK_RENEW;
+}
+#endif
diff --git a/tests/suite/ecore/src/lib/ecore_anim.c b/tests/suite/ecore/src/lib/ecore_anim.c
new file mode 100644 (file)
index 0000000..921071e
--- /dev/null
@@ -0,0 +1,233 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+struct _Ecore_Animator
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+
+   Ecore_Task_Cb func;
+   void          *data;
+
+   Eina_Bool     delete_me : 1;
+   Eina_Bool     suspended : 1;
+};
+
+
+static Eina_Bool _ecore_animator(void *data);
+
+static Ecore_Timer    *timer = NULL;
+static int             animators_delete_me = 0;
+static Ecore_Animator *animators = NULL;
+static double          animators_frametime = 1.0 / 30.0;
+
+/**
+ * Add a animator to tick off at every animaton tick during main loop execution.
+ * @param func The function to call when it ticks off
+ * @param data The data to pass to the function
+ * @return A handle to the new animator
+ * @ingroup Ecore_Animator_Group
+ *
+ * This function adds a animator and returns its handle on success and NULL on
+ * failure. The function @p func will be called every N seconds where N is the
+ * frametime interval set by ecore_animator_frametime_set(). The function will
+ * be passed the @p data pointer as its parameter.
+ *
+ * When the animator @p func is called, it must return a value of either 1 or 0.
+ * If it returns 1 (or ECORE_CALLBACK_RENEW), it will be called again at the
+ * next tick, or if it returns 0 (or ECORE_CALLBACK_CANCEL) it will be deleted
+ * automatically making any references/handles for it invalid.
+ */
+EAPI Ecore_Animator *
+ecore_animator_add(Ecore_Task_Cb func, const void *data)
+{
+   Ecore_Animator *animator;
+
+   if (!func) return NULL;
+   animator = calloc(1, sizeof(Ecore_Animator));
+   if (!animator) return NULL;
+   ECORE_MAGIC_SET(animator, ECORE_MAGIC_ANIMATOR);
+   animator->func = func;
+   animator->data = (void *)data;
+   animators = (Ecore_Animator *)eina_inlist_append(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator));
+   if (!timer)
+     {
+        double t_loop = ecore_loop_time_get();
+        double sync_0 = 0.0;
+        double d = -fmod(t_loop - sync_0, animators_frametime);
+
+        timer = ecore_timer_loop_add(animators_frametime, _ecore_animator, NULL);
+        ecore_timer_delay(timer, d);
+     }
+   return animator;
+}
+
+/**
+ * Delete the specified animator from the animator list.
+ * @param animator The animator to delete
+ * @return The data pointer set for the animator
+ * @ingroup Ecore_Animator_Group
+ *
+ * Delete the specified @p aqnimator from the set of animators that are executed
+ * during main loop execution. This function returns the data parameter that
+ * was being passed to the callback on success, or NULL on failure. After this
+ * call returns the specified animator object @p animator is invalid and should not
+ * be used again. It will not get called again after deletion.
+ */
+EAPI void *
+ecore_animator_del(Ecore_Animator *animator)
+{
+   if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR))
+     {
+        ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR,
+                         "ecore_animator_del");
+        return NULL;
+     }
+   if (animator->delete_me) return animator->data;
+   animator->delete_me = EINA_TRUE;
+   animators_delete_me++;
+   return animator->data;
+}
+
+/**
+ * Set the animator call interval in seconds.
+ * @param frametime The time in seconds in between animator ticks.
+ *
+ * This function sets the time interval (in seconds) between animator ticks.
+ */
+EAPI void
+ecore_animator_frametime_set(double frametime)
+{
+   if (frametime < 0.0) frametime = 0.0;
+   if (animators_frametime == frametime) return;
+   animators_frametime = frametime;
+   if (timer)
+     {
+        ecore_timer_del(timer);
+        timer = NULL;
+     }
+   if (animators)
+     timer = ecore_timer_add(animators_frametime, _ecore_animator, NULL);
+}
+
+/**
+ * Get the animator call interval in seconds.
+ * @return The time in second in between animator ticks.
+ *
+ * this function retrieves the time between animator ticks, in seconds.
+ */
+EAPI double
+ecore_animator_frametime_get(void)
+{
+   return animators_frametime;
+}
+
+/**
+ * Suspend the specified animator.
+ * @param animator The animator to delete
+ * @ingroup Ecore_Animator_Group
+ *
+ * The specified @p animator will be temporarly removed from the set of animators
+ * that are executed during main loop execution.
+ */
+EAPI void
+ecore_animator_freeze(Ecore_Animator *animator)
+{
+   if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR))
+     {
+        ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR,
+                         "ecore_animator_del");
+        return;
+     }
+   if (animator->delete_me) return;
+   animator->suspended = EINA_TRUE;
+}
+
+/**
+ * Restore execution of the specified animator.
+ * @param animator The animator to delete
+ * @ingroup Ecore_Animator_Group
+ *
+ * The specified @p animator will be put back in the set of animators
+ * that are executed during main loop execution.
+ */
+EAPI void
+ecore_animator_thaw(Ecore_Animator *animator)
+{
+   if (!ECORE_MAGIC_CHECK(animator, ECORE_MAGIC_ANIMATOR))
+     {
+        ECORE_MAGIC_FAIL(animator, ECORE_MAGIC_ANIMATOR,
+                         "ecore_animator_del");
+        return;
+     }
+   if (animator->delete_me) return;
+   animator->suspended = EINA_FALSE;
+}
+
+void
+_ecore_animator_shutdown(void)
+{
+   if (timer)
+     {
+        ecore_timer_del(timer);
+        timer = NULL;
+     }
+   while (animators)
+     {
+        Ecore_Animator *animator;
+
+        animator = animators;
+        animators = (Ecore_Animator *) eina_inlist_remove(EINA_INLIST_GET(animators), EINA_INLIST_GET(animators));
+        ECORE_MAGIC_SET(animator, ECORE_MAGIC_NONE);
+        free(animator);
+     }
+}
+
+static Eina_Bool
+_ecore_animator(void *data __UNUSED__)
+{
+   Ecore_Animator *animator;
+
+   EINA_INLIST_FOREACH(animators, animator)
+     {
+        if (!animator->delete_me && !animator->suspended)
+          {
+             if (!animator->func(animator->data))
+               {
+                  animator->delete_me = EINA_TRUE;
+                  animators_delete_me++;
+               }
+          }
+     }
+   if (animators_delete_me)
+     {
+        Ecore_Animator *l;
+        for(l = animators; l;)
+          {
+             animator = l;
+             l = (Ecore_Animator *) EINA_INLIST_GET(l)->next;
+             if (animator->delete_me)
+               {
+                  animators = (Ecore_Animator *) eina_inlist_remove(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator));
+                  ECORE_MAGIC_SET(animator, ECORE_MAGIC_NONE);
+                  free(animator);
+                  animators_delete_me--;
+                  if (animators_delete_me == 0) break;
+               }
+          }
+     }
+   if (!animators)
+     {
+        timer = NULL;
+        return ECORE_CALLBACK_CANCEL;
+     }
+   return ECORE_CALLBACK_RENEW;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_app.c b/tests/suite/ecore/src/lib/ecore_app.c
new file mode 100644 (file)
index 0000000..f9663a5
--- /dev/null
@@ -0,0 +1,77 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#else
+# include <process.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+static int app_argc = 0;
+static char **app_argv = NULL;
+
+/**
+ * Set up the programs command-line arguments.
+ * @param argc The same as passed as argc to the programs main() function
+ * @param argv The same as passed as argv to the programs main() function
+ *
+ * A call to this function will store the programs command-line arguments
+ * for later use by ecore_app_restart() or ecore_app_args_get().
+ */
+EAPI void
+ecore_app_args_set(int argc, const char **argv)
+{
+   if ((argc < 1) ||
+       (!argv)) return;
+   app_argc = argc;
+   app_argv = (char **)argv;
+}
+
+/**
+ * Return the programs stored command-line arguments.
+ * @param argc A pointer to the return value to hold argc
+ * @param argv A pointer to the return value to hold argv
+ *
+ * When called, this funciton returns the arguments for the program stored by
+ * ecore_app_args_set(). The integer pointed to by @p argc will be filled, if
+ * the pointer is not NULL, and the string array pointer @p argv will be filled
+ * also if the pointer is not NULL. The values they are filled with will be the
+ * same set by ecore_app_args_set().
+ */
+EAPI void
+ecore_app_args_get(int *argc, char ***argv)
+{
+   if (argc) *argc = app_argc;
+   if (argv) *argv = app_argv;
+}
+
+/**
+ * Restart the program executable with the command-line arguments stored.
+ *
+ * This function will restart & re-execute this program in place of itself
+ * using the command-line arguments stored by ecore_app_args_set(). This is
+ * an easy way for a program to restart itself for cleanup purposes,
+ * configuration reasons or in the event of a crash.
+ */
+EAPI void
+ecore_app_restart(void)
+{
+   char *args[4096];
+   int i;
+
+   if ((app_argc < 1) || (!app_argv)) return;
+   if (app_argc >= 4096) return;
+   for (i = 0; i < app_argc; i++) args[i] = app_argv[i];
+   args[i] = NULL;
+   execvp(app_argv[0], args);
+}
diff --git a/tests/suite/ecore/src/lib/ecore_events.c b/tests/suite/ecore/src/lib/ecore_events.c
new file mode 100644 (file)
index 0000000..470838c
--- /dev/null
@@ -0,0 +1,659 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+static int inpurge = 0;
+
+struct _Ecore_Event_Handler
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   int type;
+   Ecore_Event_Handler_Cb func;
+   void *data;
+   int       references;
+   Eina_Bool delete_me : 1;
+};
+
+struct _Ecore_Event_Filter
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   Ecore_Data_Cb func_start;
+   Ecore_Filter_Cb func_filter;
+   Ecore_End_Cb func_end;
+   void *loop_data;
+   void *data;
+   int       references;
+   Eina_Bool delete_me : 1;
+};
+
+struct _Ecore_Event
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   int type;
+   void *event;
+   Ecore_End_Cb func_free;
+   void *data;
+   int       references;
+   Eina_Bool delete_me : 1;
+};
+
+
+static int events_num = 0;
+static Ecore_Event *events = NULL;
+static Ecore_Event *event_current = NULL;
+static Ecore_Event *purge_events = NULL;
+
+static Ecore_Event_Handler **event_handlers = NULL;
+static Ecore_Event_Handler *event_handler_current = NULL;
+static int event_handlers_num = 0;
+static int event_handlers_alloc_num = 0;
+static Eina_List *event_handlers_delete_list = NULL;
+
+static Ecore_Event_Filter *event_filters = NULL;
+static Ecore_Event_Filter *event_filter_current = NULL;
+static Ecore_Event *event_filter_event_current = NULL;
+static int event_filters_delete_me = 0;
+static int event_id_max = ECORE_EVENT_COUNT;
+static int ecore_raw_event_type = ECORE_EVENT_NONE;
+static void *ecore_raw_event_event =  NULL;
+
+
+static void _ecore_event_purge_deleted(void);
+static void *_ecore_event_del(Ecore_Event *event);
+
+
+/**
+ * Add an event handler.
+ * @param type The type of the event this handler will get called for
+ * @param func The function to call when the event is found in the queue
+ * @param data A data pointer to pass to the called function @p func
+ * @return A new Event handler, or NULL on failure
+ *
+ * Add an event handler to the list of handlers. This will, on success, return
+ * a handle to the event handler object that was created, that can be used
+ * later to remove the handler using ecore_event_handler_del(). The @p type
+ * parameter is the integer of the event type that will trigger this callback
+ * to be called. The callback @p func is called when this event is processed
+ * and will be passed the event type, a pointer to the private event
+ * structure that is specific to that event type, and a data pointer that is
+ * provided in this call as the @p data parameter.
+ *
+ * When the callback @p func is called, it must return 1 or 0. If it returns
+ * 1 (or ECORE_CALLBACK_RENEW), It will keep being called as per normal, for
+ * each handler set up for that event type. If it returns 0 (or
+ * ECORE_CALLBACK_CANCEL), it will cease processing handlers for that particular
+ * event, so all handler set to handle that event type that have not already
+ * been called, will not be.
+ */
+EAPI Ecore_Event_Handler *
+ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data)
+{
+   Ecore_Event_Handler *eh;
+
+   if (!func) return NULL;
+   if ((type <= ECORE_EVENT_NONE) || (type >= event_id_max)) return NULL;
+   eh = calloc(1, sizeof(Ecore_Event_Handler));
+   if (!eh) return NULL;
+   ECORE_MAGIC_SET(eh, ECORE_MAGIC_EVENT_HANDLER);
+   eh->type = type;
+   eh->func = func;
+   eh->data = (void *)data;
+   if (type >= (event_handlers_num - 1))
+     {
+        int p_alloc_num;
+
+        p_alloc_num = event_handlers_alloc_num;
+        event_handlers_num = type + 1;
+        if (event_handlers_num > event_handlers_alloc_num)
+          {
+             Ecore_Event_Handler **new_handlers;
+             int i;
+
+             event_handlers_alloc_num = ((event_handlers_num + 16) / 16) * 16;
+             new_handlers = realloc(event_handlers, event_handlers_alloc_num * sizeof(Ecore_Event_Handler *));
+             if (!new_handlers)
+               {
+                  free(eh);
+                  return NULL;
+               }
+             event_handlers = new_handlers;
+             for (i = p_alloc_num; i < event_handlers_alloc_num; i++)
+               event_handlers[i] = NULL;
+          }
+     }
+   event_handlers[type] = (Ecore_Event_Handler *) eina_inlist_append(EINA_INLIST_GET(event_handlers[type]), EINA_INLIST_GET(eh));
+   return eh;
+}
+
+/**
+ * Delete an event handler.
+ * @param event_handler Event handler handle to delete
+ * @return Data passed to handler
+ *
+ * Delete a specified event handler from the handler list. On success this will
+ * delete the event handler and return the pointer passed as @p data when the
+ * handler was added by ecore_event_handler_add(). On failure NULL will be
+ * returned. Once a handler is deleted it will no longer be called.
+ */
+EAPI void *
+ecore_event_handler_del(Ecore_Event_Handler *event_handler)
+{
+   if (!ECORE_MAGIC_CHECK(event_handler, ECORE_MAGIC_EVENT_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(event_handler, ECORE_MAGIC_EVENT_HANDLER,
+                         "ecore_event_handler_del");
+        return NULL;
+     }
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(event_handler->delete_me, NULL);
+   event_handler->delete_me = 1;
+   event_handlers_delete_list = eina_list_append(event_handlers_delete_list, event_handler);
+   return event_handler->data;
+}
+
+static void
+_ecore_event_generic_free (void *data __UNUSED__, void *event)
+{
+   free (event);
+}
+
+/**
+ * Add an event to the event queue.
+ * @param type The event type to add to the end of the event queue
+ * @param ev The private data structure for this event type
+ * @param func_free The function to be called to free this private structure
+ * @param data The data pointer to be passed to the free function
+ * @return A Handle for that event
+ *
+ * On success this function returns a handle to an event on the event queue, or
+ * NULL if it fails. If it succeeds, an event of type @p type will be added
+ * to the queue for processing by event handlers added by
+ * ecore_event_handler_add(). The @p ev parameter will be a pointer to the event
+ * private data that is specific to that event type. When the event is no
+ * longer needed, @p func_free will be called and passed the private structure
+ * pointer for cleaning up. If @p func_free is NULL, free() will be called
+ * with the private structure pointer.
+ * func_free is passed @p data as its data parameter.
+ */
+EAPI Ecore_Event *
+ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data)
+{
+/*   if (!ev) return NULL;*/
+   if (type <= ECORE_EVENT_NONE) return NULL;
+   if (type >= event_id_max) return NULL;
+   if ((ev) && (!func_free)) func_free = _ecore_event_generic_free;
+   return _ecore_event_add(type, ev, func_free, data);
+}
+
+/**
+ * Delete an event from the queue.
+ * @param event The event handle to delete
+ * @return The data pointer originally set for the event free function
+ *
+ * This deletes the event @p event from the event queue, and returns the
+ * @p data parameer originally set when adding it with ecore_event_add(). This
+ * does not immediately call the free function, and it may be called later on
+ * cleanup, and so if the free function depends on the data pointer to work,
+ * you should defer cleaning of this till the free function is called later.
+ */
+EAPI void *
+ecore_event_del(Ecore_Event *event)
+{
+   if (!ECORE_MAGIC_CHECK(event, ECORE_MAGIC_EVENT))
+     {
+        ECORE_MAGIC_FAIL(event, ECORE_MAGIC_EVENT, "ecore_event_del");
+        return NULL;
+     }
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(event->delete_me, NULL);
+   event->delete_me = 1;
+   return event->data;
+}
+
+/**
+ * Allocate a new event type id sensibly and return the new id.
+ * @return A new event type id.
+ *
+ * This function allocates a new event type id and returns it. Once an event
+ * type has been allocated it can never be de-allocated during the life of
+ * the program. There is no guarantee of the contents of this event ID, or how
+ * it is calculated, except that the ID will be unique to the current instance
+ * of the process.
+ */
+EAPI int
+ecore_event_type_new(void)
+{
+   event_id_max++;
+   return event_id_max - 1;
+}
+
+/**
+ * Add a filter the current event queue.
+ * @param func_start Function to call just before filtering and return data
+ * @param func_filter Function to call on each event
+ * @param func_end Function to call after the queu has been filtered
+ * @param data Data to pass to the filter functions
+ * @return A filter handle
+ *
+ * This adds a filter to call callbacks to loop through the event queue and
+ * filter events out of the queue. On failure NULL is returned. On success a
+ * Filter handle is returned. Filters are called on the queue just before
+ * Event handler processing to try and remove redundant events. Just as
+ * processing starts @p func_start is called and passed the @p data pointer.
+ * This function returns a pointer that is used as loop_data that is now passed to
+ * @p func_filter as loop_data. @p func_filter is also passed @p data and the
+ * event type and private event structure. If this callback returns 0, the
+ * event is removed from the queue. If it returns 1, the event is kept. When
+ * processing is finished @p func_end is called and is passed the loop_data
+ * and @p data pointer to clean up.
+ */
+EAPI Ecore_Event_Filter *
+ecore_event_filter_add(Ecore_Data_Cb func_start, Ecore_Filter_Cb func_filter, Ecore_End_Cb func_end, const void *data)
+{
+   Ecore_Event_Filter *ef;
+
+   if (!func_filter) return NULL;
+   ef = calloc(1, sizeof(Ecore_Event_Filter));
+   if (!ef) return NULL;
+   ECORE_MAGIC_SET(ef, ECORE_MAGIC_EVENT_FILTER);
+   ef->func_start = func_start;
+   ef->func_filter = func_filter;
+   ef->func_end = func_end;
+   ef->data = (void *)data;
+   event_filters = (Ecore_Event_Filter *) eina_inlist_append(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(ef));
+   return ef;
+}
+
+/**
+ * Delete an event filter.
+ * @param ef The event filter handle
+ * @return The data set for the filter
+ *
+ * Delete a filter that has been added by its @p ef handle. On success this
+ * will return the data pointer set when this filter was added. On failure
+ * NULL is returned.
+ */
+EAPI void *
+ecore_event_filter_del(Ecore_Event_Filter *ef)
+{
+   if (!ECORE_MAGIC_CHECK(ef, ECORE_MAGIC_EVENT_FILTER))
+     {
+        ECORE_MAGIC_FAIL(ef, ECORE_MAGIC_EVENT_FILTER, "ecore_event_filter_del");
+        return NULL;
+     }
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(ef->delete_me, NULL);
+   ef->delete_me = 1;
+   event_filters_delete_me = 1;
+   return ef->data;
+}
+
+/**
+ * Return the current event type being handled.
+ * @return The current event type being handled if inside a handler callback
+ *
+ * If the program is currently inside an Ecore event handler callback this
+ * will return the type of the current event being processed. If Ecore is
+ * not inside an event handler, ECORE_EVENT_NONE is returned.
+ *
+ * This is useful when certain Ecore modules such as Ecore_Evas "swallow"
+ * events and not all the original information is passed on. In special cases
+ * this extra information may be useful or needed and using this call can let
+ * the program know if the event type being handled is one it wants to get more
+ * information about.
+ */
+EAPI int
+ecore_event_current_type_get(void)
+{
+   return ecore_raw_event_type;
+}
+
+/**
+ * Return the current event type pointer handled.
+ * @return The current event pointer being handled if inside a handler callback
+ *
+ * If the program is currently inside an Ecore event handler callback this
+ * will return the pointer of the current event being processed. If Ecore is
+ * not inside an event handler, NULL will be returned.
+ *
+ * This is useful when certain Ecore modules such as Ecore_Evas "swallow"
+ * events and not all the original information is passed on. In special cases
+ * this extra information may be useful or needed and using this call can let
+ * the program access the event data if the type of the event is handled by
+ * the program.
+ */
+EAPI void *
+ecore_event_current_event_get(void)
+{
+   return ecore_raw_event_event;
+}
+
+void
+_ecore_event_shutdown(void)
+{
+   int i;
+   Ecore_Event_Handler *eh;
+   Ecore_Event_Filter *ef;
+
+   while (events) _ecore_event_del(events);
+   event_current = NULL;
+   for (i = 0; i < event_handlers_num; i++)
+     {
+        while ((eh = event_handlers[i]))
+          {
+             event_handlers[i] = (Ecore_Event_Handler *) eina_inlist_remove(EINA_INLIST_GET(event_handlers[i]), EINA_INLIST_GET(event_handlers[i]));
+             ECORE_MAGIC_SET(eh, ECORE_MAGIC_NONE);
+             if (!eh->delete_me) free(eh);
+          }
+     }
+   EINA_LIST_FREE(event_handlers_delete_list, eh)
+     free(eh);
+   if (event_handlers) free(event_handlers);
+   event_handlers = NULL;
+   event_handlers_num = 0;
+   event_handlers_alloc_num = 0;
+   while ((ef = event_filters))
+     {
+        event_filters = (Ecore_Event_Filter *) eina_inlist_remove(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(event_filters));
+        ECORE_MAGIC_SET(ef, ECORE_MAGIC_NONE);
+        free(ef);
+     }
+   event_filters_delete_me = 0;
+   event_filter_current = NULL;
+   event_filter_event_current = NULL;
+}
+
+int
+_ecore_event_exist(void)
+{
+   Ecore_Event *e;
+   EINA_INLIST_FOREACH(events, e)
+      if (!e->delete_me) return 1;
+   return 0;
+}
+
+Ecore_Event *
+_ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data)
+{
+   Ecore_Event *e;
+
+   e = calloc(1, sizeof(Ecore_Event));
+   if (!e) return NULL;
+   ECORE_MAGIC_SET(e, ECORE_MAGIC_EVENT);
+   e->type = type;
+   e->event = ev;
+   e->func_free = func_free;
+   e->data = data;
+   if (inpurge > 0)
+     {
+        purge_events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(purge_events), EINA_INLIST_GET(e));
+        events_num++;
+     }
+   else
+     {
+        events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(events), EINA_INLIST_GET(e));
+        events_num++;
+     }
+   return e;
+}
+
+void *
+_ecore_event_del(Ecore_Event *event)
+{
+   void *data;
+
+   data = event->data;
+   if (event->func_free) event->func_free(event->data, event->event);
+   events = (Ecore_Event *) eina_inlist_remove(EINA_INLIST_GET(events), EINA_INLIST_GET(event));
+   ECORE_MAGIC_SET(event, ECORE_MAGIC_NONE);
+   free(event);
+   events_num--;
+   return data;
+}
+
+static void
+_ecore_event_purge_deleted(void)
+{
+   Ecore_Event *itr = events;
+
+   inpurge++;
+   while (itr)
+     {
+        Ecore_Event *next = (Ecore_Event *)EINA_INLIST_GET(itr)->next;
+        if ((!itr->references) && (itr->delete_me))
+          _ecore_event_del(itr);
+        itr = next;
+     }
+   inpurge--;
+   while (purge_events)
+     {
+        Ecore_Event *e = purge_events;
+        purge_events = (Ecore_Event *)eina_inlist_remove(EINA_INLIST_GET(purge_events), EINA_INLIST_GET(purge_events));
+        events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(events), EINA_INLIST_GET(e));
+     }
+}
+
+static inline void
+_ecore_event_filters_apply()
+{
+
+   if (!event_filter_current)
+     {
+        /* regular main loop, start from head */
+        event_filter_current = event_filters;
+     }
+   else
+     {
+        /* recursive main loop, continue from where we were */
+        event_filter_current = (Ecore_Event_Filter *)EINA_INLIST_GET(event_filter_current)->next;
+     }
+
+   while (event_filter_current)
+     {
+        Ecore_Event_Filter *ef = event_filter_current;
+
+        if (!ef->delete_me)
+          {
+             ef->references++;
+
+             if (ef->func_start)
+               ef->loop_data = ef->func_start(ef->data);
+
+             if (!event_filter_event_current)
+               {
+                  /* regular main loop, start from head */
+                  event_filter_event_current = events;
+               }
+             else
+               {
+                  /* recursive main loop, continue from where we were */
+                  event_filter_event_current = (Ecore_Event *)EINA_INLIST_GET(event_filter_event_current)->next;
+               }
+
+             while (event_filter_event_current)
+               {
+                  Ecore_Event *e = event_filter_event_current;
+
+                  if (!ef->func_filter(ef->data, ef->loop_data,
+                                       e->type, e->event))
+                    {
+                       ecore_event_del(e);
+                    }
+
+                  if (event_filter_event_current) /* may have changed in recursive main loops */
+                    event_filter_event_current = (Ecore_Event *)EINA_INLIST_GET(event_filter_event_current)->next;
+               }
+             if (ef->func_end)
+               ef->func_end(ef->data, ef->loop_data);
+
+             ef->references--;
+          }
+
+        if (event_filter_current) /* may have changed in recursive main loops */
+          event_filter_current = (Ecore_Event_Filter *)EINA_INLIST_GET(event_filter_current)->next;
+     }
+   if (event_filters_delete_me)
+     {
+        int deleted_in_use = 0;
+        Ecore_Event_Filter *l;
+        for (l = event_filters; l;)
+          {
+             Ecore_Event_Filter *ef = l;
+             l = (Ecore_Event_Filter *) EINA_INLIST_GET(l)->next;
+             if (ef->delete_me)
+               {
+                  if (ef->references)
+                    {
+                       deleted_in_use++;
+                       continue;
+                    }
+
+                  event_filters = (Ecore_Event_Filter *) eina_inlist_remove(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(ef));
+                  ECORE_MAGIC_SET(ef, ECORE_MAGIC_NONE);
+                  free(ef);
+               }
+          }
+        if (!deleted_in_use)
+          event_filters_delete_me = 0;
+     }
+}
+void
+_ecore_event_call(void)
+{
+   Eina_List *l, *l_next;
+   Ecore_Event_Handler *eh;
+
+   _ecore_event_filters_apply();
+
+   if (!event_current)
+     {
+        /* regular main loop, start from head */
+        event_current = events;
+        event_handler_current = NULL;
+     }
+
+   while (event_current)
+     {
+        Ecore_Event *e = event_current;
+        int handle_count = 0;
+
+        if (e->delete_me)
+          {
+             event_current = (Ecore_Event *)EINA_INLIST_GET(event_current)->next;
+             continue;
+          }
+
+        ecore_raw_event_type = e->type;
+        ecore_raw_event_event = e->event;
+        e->references++;
+        if ((e->type >= 0) && (e->type < event_handlers_num))
+          {
+             if (!event_handler_current)
+               {
+                  /* regular main loop, start from head */
+                  event_handler_current = event_handlers[e->type];
+               }
+             else
+               {
+                  /* recursive main loop, continue from where we were */
+                  event_handler_current = (Ecore_Event_Handler *)EINA_INLIST_GET(event_handler_current)->next;
+               }
+
+             while ((event_handler_current) && (!e->delete_me))
+               {
+                  Ecore_Event_Handler *eh = event_handler_current;
+                  if (!eh->delete_me)
+                    {
+                       Eina_Bool ret;
+
+                       handle_count++;
+
+                       eh->references++;
+                       ret = eh->func(eh->data, e->type, e->event);
+                       eh->references--;
+
+                       if (!ret)
+                         {
+                            event_handler_current = NULL;
+                            break;  /* 0 == "call no further handlers" */
+                         }
+                    }
+
+                  if (event_handler_current) /* may have changed in recursive main loops */
+                    event_handler_current = (Ecore_Event_Handler *)EINA_INLIST_GET(event_handler_current)->next;
+               }
+          }
+        /* if no handlers were set for EXIT signal - then default is */
+        /* to quit the main loop */
+        if ((e->type == ECORE_EVENT_SIGNAL_EXIT) && (handle_count == 0))
+          ecore_main_loop_quit();
+        e->references--;
+        e->delete_me = 1;
+
+        if (event_current) /* may have changed in recursive main loops */
+          event_current = (Ecore_Event *)EINA_INLIST_GET(event_current)->next;
+     }
+
+   ecore_raw_event_type = ECORE_EVENT_NONE;
+   ecore_raw_event_event = NULL;
+
+   _ecore_event_purge_deleted();
+
+   EINA_LIST_FOREACH_SAFE(event_handlers_delete_list, l, l_next, eh)
+     {
+        if (eh->references) continue;
+
+        event_handlers_delete_list = eina_list_remove_list(event_handlers_delete_list, l);
+
+        event_handlers[eh->type] = (Ecore_Event_Handler *) eina_inlist_remove(EINA_INLIST_GET(event_handlers[eh->type]), EINA_INLIST_GET(eh));
+        ECORE_MAGIC_SET(eh, ECORE_MAGIC_NONE);
+        free(eh);
+     }
+}
+
+EAPI void *
+_ecore_event_signal_user_new(void)
+{
+   Ecore_Event_Signal_User *e;
+
+   e = calloc(1, sizeof(Ecore_Event_Signal_User));
+   return e;
+}
+
+void *
+_ecore_event_signal_hup_new(void)
+{
+   Ecore_Event_Signal_Hup *e;
+
+   e = calloc(1, sizeof(Ecore_Event_Signal_Hup));
+   return e;
+}
+
+void *
+_ecore_event_signal_exit_new(void)
+{
+   Ecore_Event_Signal_Exit *e;
+
+   e = calloc(1, sizeof(Ecore_Event_Signal_Exit));
+   return e;
+}
+
+void *
+_ecore_event_signal_power_new(void)
+{
+   Ecore_Event_Signal_Power *e;
+
+   e = calloc(1, sizeof(Ecore_Event_Signal_Power));
+   return e;
+}
+
+void *
+_ecore_event_signal_realtime_new(void)
+{
+   return calloc(1, sizeof(Ecore_Event_Signal_Realtime));
+}
diff --git a/tests/suite/ecore/src/lib/ecore_exe.c b/tests/suite/ecore/src/lib/ecore_exe.c
new file mode 100644 (file)
index 0000000..251157c
--- /dev/null
@@ -0,0 +1,1846 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__)
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+   /* FIXME: Getting respawn to work
+    *
+    * There is no way that we can do anything about the internal state info of
+    * an external exe.  The same can be said about the state of user code.  User
+    * code in this context means the code that is using ecore_exe to manage exe's
+    * for it.
+    *
+    * Document that the exe must be respawnable, in other words, there is no
+    * state that it cannot regenerate by just killing it and starting it again.
+    * This includes state that the user code knows about, as the respawn is
+    * transparent to that code.  On the other hand, maybe a respawn event might
+    * be useful, or maybe resend the currently non existent add event.  For
+    * consistancy with ecore_con, an add event is good anyway.
+    *
+    * The Ecore_exe structure is reused for respawning, so that the (opaque)
+    * pointer held by the user remains valid.  This means that the Ecore_Exe
+    * init and del functions may need to be split into two parts each to avoid
+    * duplicating code - common code part, and the rest.  This implies that
+    * the unchanging members mentioned next should NEVER change.
+    *
+    * These structure members don't need to change -
+    *   __list_data       - we stay on the list
+    *   ECORE_MAGIC       - this is a constant
+    *   data              - passed in originally
+    *   cmd               - passed in originally
+    *   flags             - passed in originally
+    *
+    * These structure members need to change -
+    *   tag               - state that must be regenerated, zap it
+    *   pid               - it will be different
+    *   child_fd_write    - it will be different
+    *   child_fd_read     - it will be different
+    *   child_fd_error    - it will be different
+    *   write_fd_handler  - we cannot change the fd used by a handler, this changes coz the fd changes.
+    *   read_fd_handler   - we cannot change the fd used by a handler, this changes coz the fd changes.
+    *   error_fd_handler  - we cannot change the fd used by a handler, this changes coz the fd changes.
+    *
+    * Hmm, the read, write, and error buffers could be tricky.
+    * They are not atomic, and could be in a semi complete state.
+    * They fall into the "state must be regenerated" mentioned above.
+    * A respawn/add event should take care of it.
+    *
+    * These structure members need to change -
+    *   write_data_buf    - state that must be regenerated, zap it
+    *   write_data_size   - state that must be regenerated, zap it
+    *   write_data_offset - state that must be regenerated, zap it
+    *   read_data_buf     - state that must be regenerated, zap it
+    *   read_data_size    - state that must be regenerated, zap it
+    *   error_data_buf    - state that must be regenerated, zap it
+    *   error_data_size   - state that must be regenerated, zap it
+    *   close_write       - state that must be regenerated, zap it
+    *
+    * There is the problem that an exe that fell over and needs respawning
+    * might keep falling over, keep needing to be respawned, and tie up system
+    * resources with the constant respawning.  An exponentially increasing
+    * timeout (with maximum timeout) between respawns should take care of that.
+    * Although this is not a "contention for a resource" problem, the exe falling
+    * over may be, so a random element added to the timeout may help, and won't
+    * hurt.  The user code may need to be informed that a timeout is in progress.
+    */
+
+struct _Ecore_Exe
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   pid_t pid;
+   void *data;
+   char *tag, *cmd;
+   Ecore_Exe_Flags  flags;
+   Ecore_Fd_Handler *write_fd_handler; /* the fd_handler to handle write to child - if this was used, or NULL if not */
+   Ecore_Fd_Handler *read_fd_handler; /* the fd_handler to handle read from child - if this was used, or NULL if not */
+   Ecore_Fd_Handler *error_fd_handler; /* the fd_handler to handle errors from child - if this was used, or NULL if not */
+   void  *write_data_buf; /* a data buffer for data to write to the child -
+                                 * realloced as needed for more data and flushed when the fd handler says writes are possible
+                                 */
+   int write_data_size; /* the size in bytes of the data buffer */
+   int write_data_offset; /* the offset in bytes in the data buffer */
+   void *read_data_buf; /* data read from the child awating delivery to an event */
+   int read_data_size; /* data read from child in bytes */
+   void *error_data_buf; /* errors read from the child awating delivery to an event */
+   int error_data_size; /* errors read from child in bytes */
+   int child_fd_write; /* fd to write TO to send data to the child */
+   int child_fd_read; /* fd to read FROM when child has sent us (the parent) data */
+   int child_fd_error; /* fd to read FROM when child has sent us (the parent) errors */
+   int child_fd_write_x; /* fd to write TO to send data to the child */
+   int child_fd_read_x; /* fd to read FROM when child has sent us (the parent) data */
+   int child_fd_error_x; /* fd to read FROM when child has sent us (the parent) errors */
+   Eina_Bool close_stdin : 1;
+
+   int start_bytes, end_bytes, start_lines, end_lines; /* Number of bytes/lines to auto pipe at start/end of stdout/stderr. */
+
+   Ecore_Timer *doomsday_clock; /* The Timer of Death.  Muahahahaha. */
+   void *doomsday_clock_dead; /* data for the doomsday clock */
+
+   Ecore_Exe_Cb pre_free_cb;
+};
+
+
+/* TODO: Something to let people build a command line and does auto escaping -
+ *
+ * ecore_exe_snprintf()
+ *
+ *   OR
+ *
+ * cmd = ecore_exe_comand_parameter_append(cmd, "firefox");
+ * cmd = ecore_exe_comand_parameter_append(cmd, "http://www.foo.com/bar.html?baz=yes");
+ * each parameter appended is one argument, and it gets escaped, quoted, and
+ * appended with a preceding space.  The first is the command off course.
+ */
+
+struct _ecore_exe_dead_exe
+{
+   pid_t pid;
+   char *cmd;
+};
+
+static inline void _ecore_exe_exec_it(const char *exe_cmd, Ecore_Exe_Flags flags);
+static Eina_Bool _ecore_exe_data_generic_handler(void *data, Ecore_Fd_Handler *fd_handler, Ecore_Exe_Flags flags);
+static Eina_Bool _ecore_exe_data_error_handler(void *data, Ecore_Fd_Handler *fd_handler);
+static Eina_Bool _ecore_exe_data_read_handler(void *data, Ecore_Fd_Handler *fd_handler);
+static Eina_Bool _ecore_exe_data_write_handler(void *data, Ecore_Fd_Handler *fd_handler);
+static void _ecore_exe_flush(Ecore_Exe * exe);
+static void _ecore_exe_event_exe_data_free(void *data __UNUSED__, void *ev);
+static Ecore_Exe *_ecore_exe_is_it_alive(pid_t pid);
+static Eina_Bool _ecore_exe_make_sure_its_dead(void *data);
+static Eina_Bool _ecore_exe_make_sure_its_really_dead(void *data);
+static Ecore_Exe_Event_Add *_ecore_exe_event_add_new(void);
+static void _ecore_exe_event_add_free(void *data, void *ev);
+static void _ecore_exe_dead_attach(Ecore_Exe *exe);
+
+EAPI int ECORE_EXE_EVENT_ADD = 0;
+EAPI int ECORE_EXE_EVENT_DEL = 0;
+EAPI int ECORE_EXE_EVENT_DATA = 0;
+EAPI int ECORE_EXE_EVENT_ERROR = 0;
+
+static Ecore_Exe *exes = NULL;
+static const char *shell = NULL;
+
+/* FIXME: This errno checking stuff should be put elsewhere for everybody to use.
+ * For now it lives here though, just to make testing easier.
+ */
+static int _ecore_exe_check_errno(int result, const char *file, int line);
+
+#define E_IF_NO_ERRNO(result, foo, ok) \
+  while (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)) == -1)   sleep(1); \
+  if (ok)
+
+#define E_NO_ERRNO(result, foo, ok) \
+  while (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)) == -1)   sleep(1)
+
+#define E_IF_NO_ERRNO_NOLOOP(result, foo, ok) \
+  if (((ok) = _ecore_exe_check_errno( (result) = (foo), __FILE__, __LINE__)))
+
+static int
+_ecore_exe_check_errno(int result, const char *file, int line)
+{
+   int saved_errno = errno;
+
+   if (result == -1)
+     {
+        perror("*** errno reports ");
+/* What is currently supported -
+ *
+ *   pipe
+ *     EFAULT  Argument is not valid.
+ *     EMFILE  Too many file descriptors used by process.
+ *     ENFILE  Too many open files by system.
+ *   read
+ *     EAGAIN  No data now, try again.
+ *     EBADF   This is not an fd that can be read.
+ *     EFAULT  This is not a valid buffer.
+ *     EINTR   Interupted by signal, try again.
+ *     EINVAL  This is not an fd that can be read.
+ *     EIO     I/O error.
+ *     EISDIR  This is a directory, and cannot be read.
+ *     others  Depending on what sort of thing we are reading from.
+ *   close
+ *     EBADF   This is not an fd that can be closed.
+ *     EINTR   Interupted by signal, try again.
+ *     EIO     I/O error.
+ *   dup2
+ *     EBADF   This is not an fd that can be dup2'ed.
+ *     EBUSY   Race condition between open() and dup()
+ *     EINTR   Interupted by signal, try again.
+ *     EMFILE  Too many file descriptors used by process.
+ *   fcntl
+ *     EACCES, EAGAIN  Locked or mapped by something else, try again later.
+ *     EBADF   This is not an fd that can be fcntl'ed.
+ *     EDEADLK This will cause a deadlock.
+ *     EFAULT  This is not a valid lock.
+ *     EINTR   Interupted by signal, try again.
+ *     EINVAL  This is not a valid arg.
+ *     EMFILE  Too many file descriptors used by process.
+ *     ENOLCK  Problem getting a lock.
+ *     EPERM   Not allowed to do that.
+ *   fsync
+ *     EBADF   This is not an fd that is open for writing.
+ *     EINVAL, EROFS  This is not an fd that can be fsynced.
+ *     EIO     I/O error.
+ *
+ * How to use it -
+ *    int ok = 0;
+ *    int result;
+ *
+ *    E_IF_NO_ERRNO(result, foo(bar), ok)
+ *      {
+ *         E_IF_NO_ERRNO_NOLOOP(result, foo(bar), ok)
+ *            {
+ *            }
+ *      }
+ *
+ *   if (!ok)
+ *     {
+ *        // Something failed, cleanup.
+ *     }
+ */
+        switch (saved_errno)
+          {
+           case EACCES:
+           case EAGAIN:
+           case EINTR:
+               { /* Not now, try later. */
+                  ERR("*** Must try again in %s @%u.", file, line);
+                  result = -1;
+                  break;
+               }
+           case EMFILE:
+           case ENFILE:
+           case ENOLCK:
+               { /* Low on resources. */
+                  ERR("*** Low on resources in %s @%u.", file,
+                      line);
+                  result = 0;
+                  break;
+               }
+           case EIO:
+               { /* I/O error. */
+                  ERR("*** I/O error in %s @%u.", file, line);
+                  result = 0;
+                  break;
+               }
+           case EFAULT:
+           case EBADF:
+           case EINVAL:
+           case EROFS:
+           case EISDIR:
+           case EDEADLK:
+           case EPERM:
+           case EBUSY:
+               { /* Programmer fucked up. */
+                  ERR("*** NAUGHTY PROGRAMMER!!!\n"
+                      "*** SPANK SPANK SPANK!!!\n"
+                      "*** Now go fix your code in %s @%u. Tut tut tut!",
+                      file, line);
+                  result = 0;
+                  break;
+               }
+           default:
+               { /* Unsupported errno code, please add this one. */
+                  ERR("*** NAUGHTY PROGRAMMER!!!\n"
+                      "*** SPANK SPANK SPANK!!!\n"
+                      "*** Unsupported errno code %d, please add this one.\n"
+                      "*** Now go fix your code in %s @%u, from %s @%u. Tut tut tut!",
+                      saved_errno, __FILE__, __LINE__, file, line);
+                  result = 0;
+                  break;
+               }
+          }
+     }
+   else /* Everything is fine. */
+     result = 1;
+
+   errno = saved_errno;
+   return result;
+}
+
+/**
+ * @defgroup Ecore_Exe_Basic_Group Process Spawning Functions
+ *
+ * Functions that deal with spawned processes.
+ */
+
+static int run_pri = ECORE_EXE_PRIORITY_INHERIT;
+
+/**
+ * Sets the priority at which to launch processes
+ *
+ * This sets the priority of processes run by ecore_exe_run() and
+ * ecore_exe_pipe_run().
+ * @li On Windows, the child process is created by default with the
+ * #ECORE_EXE_WIN32_PRIORITY_NORMAL priority, unless the calling
+ * process is in #ECORE_EXE_WIN32_PRIORITY_IDLE or
+ * #ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL priority. In that case, the
+ * child process inherits this priority.
+ * @li On other platforms, if set to #ECORE_EXE_PRIORITY_INHERIT child
+ * processes inherits the priority of their parent. This is the default.
+ *
+ * @param   pri value a Ecore_Exe_Win32_Priority value on Windows, -20
+ * to 19 or ECORE_EXE_PRIORITY_INHERIT on other OS.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void
+ecore_exe_run_priority_set(int pri)
+{
+   run_pri = pri;
+}
+
+/**
+ * Gets the priority at which to launch processes
+ *
+ * This gets ths priority of launched processes. See
+ * ecore_exe_run_priority_set() for details. This just returns the value set
+ * by this call.
+ *
+ * @return the value set by ecore_exe_run_priority_set()
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI int
+ecore_exe_run_priority_get(void)
+{
+   return run_pri;
+}
+
+/**
+ * Spawns a child process.
+ *
+ * This is now just a thin wrapper around ecore_exe_pipe_run()
+ *
+ * @param   exe_cmd The command to run with @c /bin/sh.
+ * @param   data    Data to attach to the returned process handle.
+ * @return  A process handle to the spawned process.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI Ecore_Exe *
+ecore_exe_run(const char *exe_cmd, const void *data)
+{
+/* I'm just being paranoid again, leaving in the original code in case there is a problem. */
+#if 0
+   Ecore_Exe *exe;
+   pid_t pid;
+
+   if (!exe_cmd)
+      return NULL;
+   pid = fork();
+   if (pid)
+     {
+        exe = calloc(1, sizeof(Ecore_Exe));
+        if (!exe)
+          {
+             kill(pid, SIGKILL);
+             return NULL;
+          }
+        ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE);
+        exe->pid = pid;
+        exe->data = (void *)data;
+        exe->cmd = strdup(exe_cmd);
+        exes = _ecore_list2_append(exes, exe);
+        return exe;
+     }
+   _ecore_exe_exec_it(exe_cmd, 0);
+   exit(127);
+   return NULL;
+#else
+   return ecore_exe_pipe_run(exe_cmd, 0, data);
+#endif
+}
+
+/**
+ * Spawns a child process with its stdin/out available for communication.
+ *
+ * This function forks and runs the given command using @c /bin/sh.
+ *
+ * Note that the process handle is only valid until a child process
+ * terminated event is received.  After all handlers for the child process
+ * terminated event have been called, the handle will be freed by Ecore.
+ *
+ * This function does the same thing as ecore_exe_run(), but also makes the
+ * standard in and/or out as well as stderr from the child process available
+ * for reading or writing.  To write use ecore_exe_send().  To read listen to
+ * ECORE_EXE_EVENT_DATA or ECORE_EXE_EVENT_ERROR events (set up handlers).
+ * Ecore may buffer read and error data until a newline character if asked
+ * for with the @p flags.  All data will be included in the events (newlines
+ * will be replaced with NULLS if line buffered).  ECORE_EXE_EVENT_DATA events
+ * will only happen if the process is run with ECORE_EXE_PIPE_READ enabled
+ * in the flags.  The same with the error version.  Writing will only be
+ * allowed with ECORE_EXE_PIPE_WRITE enabled in the flags.
+ *
+ * @param   exe_cmd The command to run with @c /bin/sh.
+ * @param   flags   The flag parameters for how to deal with inter-process I/O
+ * @param   data    Data to attach to the returned process handle.
+ * @return  A process handle to the spawned process.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI Ecore_Exe *
+ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data)
+{
+   Ecore_Exe *exe = NULL;
+   int statusPipe[2] = { -1, -1 };
+   int errorPipe[2] = { -1, -1 };
+   int readPipe[2] = { -1, -1 };
+   int writePipe[2] = { -1, -1 };
+   int n = 0;
+   int ok = 1;
+   int result;
+
+   if (!exe_cmd) return NULL;
+   exe = calloc(1, sizeof(Ecore_Exe));
+   if (!exe) return NULL;
+
+   if ((flags & ECORE_EXE_PIPE_AUTO) && (!(flags & ECORE_EXE_PIPE_ERROR))
+       && (!(flags & ECORE_EXE_PIPE_READ)))
+     /* We need something to auto pipe. */
+     flags |= ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR;
+
+   exe->child_fd_error = -1;
+   exe->child_fd_read = -1;
+   exe->child_fd_write = -1;
+   exe->child_fd_error_x = -1;
+   exe->child_fd_read_x = -1;
+   exe->child_fd_write_x = -1;
+
+   /*  Create some pipes. */
+   if (ok)
+     {
+        E_IF_NO_ERRNO_NOLOOP(result, pipe(statusPipe), ok)
+          {
+          }
+     }
+   if (ok && (flags & ECORE_EXE_PIPE_ERROR))
+     {
+        E_IF_NO_ERRNO_NOLOOP(result, pipe(errorPipe), ok)
+          {
+             exe->child_fd_error = errorPipe[0];
+             exe->child_fd_error_x = errorPipe[1];
+          }
+     }
+   if (ok && (flags & ECORE_EXE_PIPE_READ))
+     {
+        E_IF_NO_ERRNO_NOLOOP(result, pipe(readPipe), ok)
+          {
+             exe->child_fd_read = readPipe[0];
+             exe->child_fd_read_x = readPipe[1];
+          }
+     }
+   if (ok && (flags & ECORE_EXE_PIPE_WRITE))
+     {
+        E_IF_NO_ERRNO_NOLOOP(result, pipe(writePipe), ok)
+          {
+             exe->child_fd_write = writePipe[1];
+             exe->child_fd_write_x = writePipe[0];
+          }
+     }
+   if (ok)
+     {
+        pid_t pid = 0;
+        volatile int vfork_exec_errno = 0;
+
+        /* FIXME: I should double check this.  After a quick look around, this is already done, but via a more modern method. */
+        /* signal(SIGPIPE, SIG_IGN);    We only want EPIPE on errors */
+        pid = fork();
+
+        if (pid == -1)
+          {
+             ERR("Failed to fork process");
+             pid = 0;
+          }
+        else if (pid == 0)        /* child */
+          {
+             if (run_pri != ECORE_EXE_PRIORITY_INHERIT)
+               {
+                  if ((run_pri >= -20) && (run_pri <= 19))
+                    setpriority(PRIO_PROCESS, 0, run_pri);
+               }
+             /* dup2 STDERR, STDIN, and STDOUT.  dup2() allegedly closes the
+              * second pipe if it's open. On the other hand, there was the
+              * Great FD Leak Scare of '06, so let's be paranoid. */
+             if (ok && (flags & ECORE_EXE_PIPE_ERROR))
+               {
+                  E_NO_ERRNO(result, close(STDERR_FILENO), ok);
+                  E_NO_ERRNO(result, dup2(errorPipe[1], STDERR_FILENO), ok);
+               }
+             if (ok && (flags & ECORE_EXE_PIPE_READ))
+               {
+                  E_NO_ERRNO(result, close(STDOUT_FILENO), ok);
+                  E_NO_ERRNO(result, dup2(readPipe[1], STDOUT_FILENO), ok);
+               }
+             if (ok && (flags & ECORE_EXE_PIPE_WRITE))
+               {
+                  E_NO_ERRNO(result, close(STDIN_FILENO), ok);
+                  E_NO_ERRNO(result, dup2(writePipe[0], STDIN_FILENO), ok);
+               }
+
+             if (ok)
+               {
+                  /* Setup the status pipe. */
+                  E_NO_ERRNO(result, close(statusPipe[0]), ok);
+                  E_IF_NO_ERRNO(result, fcntl(statusPipe[1], F_SETFD, FD_CLOEXEC), ok)        /* close on exec shows success */
+                    {
+                       /* Run the actual command. */
+                       _ecore_exe_exec_it(exe_cmd, flags); /* no return */
+                    }
+               }
+
+             /* Something went 'orribly wrong. */
+             vfork_exec_errno = errno;
+
+             /* Close the pipes. */
+             if (flags & ECORE_EXE_PIPE_ERROR)
+               E_NO_ERRNO(result, close(errorPipe[1]), ok);
+             if (flags & ECORE_EXE_PIPE_READ)
+               E_NO_ERRNO(result, close(readPipe[1]), ok);
+             if (flags & ECORE_EXE_PIPE_WRITE)
+               E_NO_ERRNO(result, close(writePipe[0]), ok);
+             E_NO_ERRNO(result, close(statusPipe[1]), ok);
+
+             _exit(-1);
+          }
+        else /* parent */
+          {
+             /* Close the unused pipes. */
+             E_NO_ERRNO(result, close(statusPipe[1]), ok);
+
+             /* FIXME: after having a good look at the current e fd
+              * handling, investigate fcntl(dataPipe[x], F_SETSIG, ...) */
+             /* FIXME: above F_SETSIG etc. - this is async SIGIO based IO
+              * which is also linux specific so we probably don't want to
+              * do this as long as select() is working fine. the only time
+              * we really want to think of SIGIO async IO is when it all
+              * actually works basically everywhere and we can turn all
+              * IO into DMA async activities (i.e. you do a read() then
+              * the read is complete not on return but when you get a
+              * SIGIO - the read() just starts the transfer and it is
+              * completed in the background by DMA (or whatever mechanism
+              * the kernel choses)) */
+
+             /* Wait for it to start executing. */
+             /* FIXME: this doesn't seem very nice - we sit and block
+              * waiting on a child process... even though it's just
+              * the segment between the fork() and the exec) it just feels
+              * wrong */
+             for (;;)
+               {
+                  char buf;
+
+                  E_NO_ERRNO(result, read(statusPipe[0], &buf, 1), ok);
+                  if (result == 0)
+                    {
+                       if (vfork_exec_errno != 0)
+                         {
+                            n = vfork_exec_errno;
+                            ERR("Could not start \"%s\"", exe_cmd);
+                            pid = 0;
+                         }
+                       break;
+                    }
+               }
+
+             /* Close the status pipe. */
+             E_NO_ERRNO(result, close(statusPipe[0]), ok);
+          }
+
+        if (pid)
+          {
+             /* Setup the exe structure. */
+             ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE);
+             exe->start_bytes = -1;
+             exe->end_bytes = -1;
+             exe->start_lines = -1;
+             exe->end_lines = -1;
+             exe->pid = pid;
+             exe->flags = flags;
+             exe->data = (void *)data;
+             if ((exe->cmd = strdup(exe_cmd)))
+               {
+                  if (flags & ECORE_EXE_PIPE_ERROR)
+                    { /* Setup the error stuff. */
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_error, F_SETFL,
+                                           O_NONBLOCK), ok) {}
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_error, F_SETFD,
+                                           FD_CLOEXEC), ok) {}
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_error_x, F_SETFD,
+                                           FD_CLOEXEC), ok) {}
+                       {
+                          exe->error_fd_handler =
+                             ecore_main_fd_handler_add(exe->child_fd_error,
+                                                       ECORE_FD_READ,
+                                                       _ecore_exe_data_error_handler,
+                                                       exe, NULL, NULL);
+                          if (!exe->error_fd_handler)
+                             ok = 0;
+                       }
+                    }
+                  if (ok && (flags & ECORE_EXE_PIPE_READ))
+                    { /* Setup the read stuff. */
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_read, F_SETFL,
+                                           O_NONBLOCK), ok) {}
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_read, F_SETFD,
+                                           FD_CLOEXEC), ok) {}
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_read_x, F_SETFD,
+                                           FD_CLOEXEC), ok) {}
+                       {
+                          exe->read_fd_handler =
+                             ecore_main_fd_handler_add(exe->child_fd_read,
+                                                       ECORE_FD_READ,
+                                                       _ecore_exe_data_read_handler,
+                                                       exe, NULL, NULL);
+                          if (!exe->read_fd_handler)
+                             ok = 0;
+                       }
+                    }
+                  if (ok && (flags & ECORE_EXE_PIPE_WRITE))
+                    {                /* Setup the write stuff. */
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_write, F_SETFL,
+                                           O_NONBLOCK), ok) {}
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_write, F_SETFD,
+                                           FD_CLOEXEC), ok) {}
+                       E_IF_NO_ERRNO(result,
+                                     fcntl(exe->child_fd_write_x, F_SETFD,
+                                           FD_CLOEXEC), ok) {}
+                       {
+                          exe->write_fd_handler =
+                             ecore_main_fd_handler_add(exe->child_fd_write,
+                                                       ECORE_FD_WRITE,
+                                                       _ecore_exe_data_write_handler,
+                                                       exe, NULL, NULL);
+                          if (exe->write_fd_handler)
+                             ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);        /* Nothing to write to start with. */
+                          else
+                             ok = 0;
+                       }
+                    }
+
+                  exes = (Ecore_Exe *) eina_inlist_append(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe));
+                  n = 0;
+               }
+             else
+               ok = 0;
+          }
+        else
+          ok = 0;
+     }
+
+   if (!ok)
+     { /* Something went wrong, so pull down everything. */
+        if (exe->pid) ecore_exe_terminate(exe);
+        IF_FN_DEL(ecore_exe_free, exe);
+     }
+   else
+     {
+        Ecore_Exe_Event_Add *e;
+
+        e = _ecore_exe_event_add_new();
+        e->exe = exe;
+        if (e) /* Send the event. */
+          ecore_event_add(ECORE_EXE_EVENT_ADD, e,
+                          _ecore_exe_event_add_free, NULL);
+        /* INF("Running as %d for %s.\n", exe->pid, exe->cmd); */
+     }
+
+   errno = n;
+   return exe;
+}
+
+/**
+ * Defines a function to be called before really freeing the handle data.
+ *
+ * This might be useful for language bindings such as Python and Perl
+ * that need to deallocate wrappers associated with this handle.
+ *
+ * This handle should never be modified by this call. It should be
+ * considered informative only. All getters are valid when the given
+ * function is called back.
+ *
+ * @param exe The child process to attach the pre_free function.
+ * @param func The function to call before @a exe is freed.
+ */
+EAPI void
+ecore_exe_callback_pre_free_set(Ecore_Exe *exe, Ecore_Exe_Cb func)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
+                         "ecore_exe_callback_pre_free_set");
+        return;
+     }
+   exe->pre_free_cb = func;
+}
+
+/**
+ * Sends data to the given child process which it receives on stdin.
+ *
+ * This function writes to a child processes standard in, with unlimited
+ * buffering. This call will never block. It may fail if the system runs out
+ * of memory.
+ *
+ * @param exe  The child process to send to
+ * @param data The data to send
+ * @param size The size of the data to send, in bytes
+ * @return EINA_TRUE if successful, EINA_FALSE on failure.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI Eina_Bool
+ecore_exe_send(Ecore_Exe * exe, const void *data, int size)
+{
+   void *buf;
+
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_send");
+        return EINA_FALSE;
+     }
+
+   if (exe->close_stdin)
+     {
+        ERR("Ecore_Exe %p stdin is closed! Cannot send %d bytes from %p",
+            exe, size, data);
+        return EINA_FALSE;
+     }
+
+   if (exe->child_fd_write == -1)
+     {
+        ERR("Ecore_Exe %p created without ECORE_EXE_PIPE_WRITE! "
+            "Cannot send %d bytes from %p", exe, size, data);
+        return EINA_FALSE;
+     }
+
+   buf = realloc(exe->write_data_buf, exe->write_data_size + size);
+   if (!buf) return EINA_FALSE;
+
+   exe->write_data_buf = buf;
+   memcpy((char *)exe->write_data_buf + exe->write_data_size, data, size);
+   exe->write_data_size += size;
+
+   if (exe->write_fd_handler)
+      ecore_main_fd_handler_active_set(exe->write_fd_handler, ECORE_FD_WRITE);
+
+   return EINA_TRUE;
+}
+
+/**
+ * The stdin of the given child process will close when the write buffer is empty.
+ *
+ * @param exe  The child process
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void
+ecore_exe_close_stdin(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_close_stdin");
+        return;
+     }
+   exe->close_stdin = 1;
+}
+
+/**
+ * Sets the auto pipe limits for the given process handle. On Windows
+ * this function does nothing.
+ *
+ * @param   exe The given process handle.
+ * @param   start_bytes limit of bytes at start of output to buffer.
+ * @param   end_bytes limit of bytes at end of output to buffer.
+ * @param   start_lines limit of lines at start of output to buffer.
+ * @param   end_lines limit of lines at end of output to buffer.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void
+ecore_exe_auto_limits_set(Ecore_Exe *exe, int start_bytes, int end_bytes, int start_lines, int end_lines)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_auto_limits_set");
+        return;
+     }
+   /* FIXME: sanitize the input. */
+   exe->start_bytes = start_bytes;
+   exe->end_bytes = end_bytes;
+   exe->start_lines = start_lines;
+   exe->end_lines = end_lines;
+
+   /* FIXME: get this can of worms working.
+    *
+    * capture stderr & stdout internally
+    *
+    * raster and onefang keep moving the goal posts on this one.  It started out as
+    * "show users the error output if an exe fails" and is rapidly approaching
+    * "alternative method of getting the data, poll vs event driven".  Some serious
+    * thinking needs to be applied to this.  Do we really want to go that far?  If
+    * so, we should change the names.  The basic design will probably remain the
+    * same which ever way we go.  The constant goal post moving is probably due to
+    * generic design methods leading to feature creep as we inspired each other to
+    * more generic designs.  It does seem like the closer we get to poll driven,
+    * the more issues and corner cases there are.
+    *
+    * Instead of doing the usual register an event handler thing, we are ecore_exe,
+    * we can take some short cuts.  Don't send the events, just leave the exe buffers
+    * as is until the user asks for them, then return the event.
+    *
+    * start = 0,  end = 0;   clogged arteries get flushed, everything is ignored.
+    * start = -1, end = -1;  clogged arteries get transferred to internal buffers.  Actually, either == -1 means buffer everything.
+    * start = X,  end = 0;   buffer first X out of clogged arteries, flush and ignore rest.
+    * start = 0,  end = X;   circular buffer X
+    * start = X,  end = Y;   buffer first X out of clogged arteries, circular buffer Y from beginning.
+    *
+    * bytes vs lines, which ever one reaches the limit first.
+    * Before we go beyond the start+end limit, leave the end buffer empty, and store both in the start buffer, coz they overlap.
+    * After we pass the the start+end limit, insert "\n...\n" at the end of the start buffer, copy the rest to the end buffer, then store in the end buffer.
+    *
+    * Other issues -
+    * Spank programmer for polling data if polling is not turned on.
+    * Spank programmer for setting up event callbacks if polling is turned on.
+    * Spank programmer for freeing the event data if it came from the event system, as that autofrees.
+    * Spank the programmer if they try to set the limits bigger than what has been gathered & ignored already, coz they just lost data.
+    * Spank onefang and raster for opening this can of worms.
+    * Should we have separate out/err limits?
+    * Should we remove from the internal buffer the data that was delivered already?
+    * If so, what to do about limits, start, and end?  They could loose their meaning.
+    */
+}
+
+/**
+ * Gets the auto pipe data for the given process handle
+ *
+ * @param   exe The given process handle.
+ * @param   flags   Is this a ECORE_EXE_PIPE_READ or ECORE_EXE_PIPE_ERROR?
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI Ecore_Exe_Event_Data *
+ecore_exe_event_data_get(Ecore_Exe *exe, Ecore_Exe_Flags flags)
+{
+   Ecore_Exe_Event_Data *e = NULL;
+   int is_buffered = 0;
+   unsigned char *inbuf;
+   int inbuf_num;
+
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_event_data_get");
+        return NULL;
+     }
+
+   /* Sort out what sort of event we are. */
+   if (flags & ECORE_EXE_PIPE_READ)
+     {
+        flags = ECORE_EXE_PIPE_READ;
+        if (exe->flags & ECORE_EXE_PIPE_READ_LINE_BUFFERED)
+           is_buffered = 1;
+     }
+   else
+     {
+        flags = ECORE_EXE_PIPE_ERROR;
+        if (exe->flags & ECORE_EXE_PIPE_ERROR_LINE_BUFFERED)
+           is_buffered = 1;
+     }
+
+   /* Get the data. */
+   if (flags & ECORE_EXE_PIPE_READ)
+     {
+        inbuf = exe->read_data_buf;
+        inbuf_num = exe->read_data_size;
+        exe->read_data_buf = NULL;
+        exe->read_data_size = 0;
+     }
+   else
+     {
+        inbuf = exe->error_data_buf;
+        inbuf_num = exe->error_data_size;
+        exe->error_data_buf = NULL;
+        exe->error_data_size = 0;
+     }
+
+   e = calloc(1, sizeof(Ecore_Exe_Event_Data));
+   if (e)
+     {
+        e->exe = exe;
+        e->data = inbuf;
+        e->size = inbuf_num;
+
+        if (is_buffered)
+          {                        /* Deal with line buffering. */
+             int max = 0;
+             int count = 0;
+             int i;
+             int last = 0;
+             char *c;
+
+             c = (char *)inbuf;
+             for (i = 0; i < inbuf_num; i++)        /* Find the lines. */
+               {
+                  if (inbuf[i] == '\n')
+                    {
+                       if (count >= max)
+                         {
+                            /* In testing, the lines seem to arrive in batches of 500 to 1000 lines at most, roughly speaking. */
+                            max += 10;        /* FIXME: Maybe keep track of the largest number of lines ever sent, and add half that many instead of 10. */
+                            e->lines = realloc(e->lines, sizeof(Ecore_Exe_Event_Data_Line) * (max + 1));        /* Allow room for the NULL termination. */
+                         }
+                       /* raster said to leave the line endings as line endings, however -
+                        * This is line buffered mode, we are not dealing with binary here, but lines.
+                        * If we are not dealing with binary, we must be dealing with ASCII, unicode, or some other text format.
+                        * Thus the user is most likely gonna deal with this text as strings.
+                        * Thus the user is most likely gonna pass this data to str functions.
+                        * rasters way - the endings are always gonna be '\n';  onefangs way - they will always be '\0'
+                        * We are handing them the string length as a convenience.
+                        * Thus if they really want it in raw format, they can e->lines[i].line[e->lines[i].size - 1] = '\n'; easily enough.
+                        * In the default case, we can do this conversion quicker than the user can, as we already have the index and pointer.
+                        * Let's make it easy on them to use these as standard C strings.
+                        *
+                        * onefang is proud to announce that he has just set a new personal record for the
+                        * most over documentation of a simple assignment statement.  B-)
+                        */
+                       inbuf[i] = '\0';
+                       e->lines[count].line = c;
+                       e->lines[count].size = i - last;
+                       last = i + 1;
+                       c = (char *)&inbuf[last];
+                       count++;
+                    }
+               }
+             if (count == 0)        /* No lines to send, cancel the event. */
+               {
+                  _ecore_exe_event_exe_data_free(NULL, e);
+                  e = NULL;
+               }
+             else                /* NULL terminate the array, so that people know where the end is. */
+               {
+                  e->lines[count].line = NULL;
+                  e->lines[count].size = 0;
+               }
+             if (i > last)        /* Partial line left over, save it for next time. */
+               {
+                  if (e) e->size = last;
+                  if (flags & ECORE_EXE_PIPE_READ)
+                    {
+                       exe->read_data_size = i - last;
+                       exe->read_data_buf = malloc(exe->read_data_size);
+                       memcpy(exe->read_data_buf, c, exe->read_data_size);
+                    }
+                  else
+                    {
+                       exe->error_data_size = i - last;
+                       exe->error_data_buf = malloc(exe->error_data_size);
+                       memcpy(exe->error_data_buf, c, exe->error_data_size);
+                    }
+               }
+          }
+     }
+
+   return e;
+}
+
+/**
+ * Sets the string tag for the given process handle
+ *
+ * @param   exe The given process handle.
+ * @param   tag The string tag to set on the process handle.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void
+ecore_exe_tag_set(Ecore_Exe *exe, const char *tag)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_set");
+        return;
+     }
+   IF_FREE(exe->tag);
+   if (tag)
+      exe->tag = strdup(tag);
+   else
+      exe->tag = NULL;
+}
+
+/**
+ * Retrieves the tag attached to the given process handle. There is no need to
+ * free it as it just returns the internal pointer value. This value is only
+ * valid as long as the @p exe is valid or until the tag is set to something
+ * else on this @p exe.
+ *
+ * @param   exe The given process handle.
+ * @return The string attached to @p exe. It is a handle to existing
+ *         internal string and should not be modified, use
+ *         ecore_exe_tag_set() to change it. It might be @c NULL.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI const char *
+ecore_exe_tag_get(const Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_get");
+        return NULL;
+     }
+   return exe->tag;
+}
+
+/**
+ * Frees the given process handle.
+ *
+ * Note that the process that the handle represents is unaffected by this
+ * function.
+ *
+ * @param   exe The given process handle.
+ * @return  The data attached to the handle when @ref ecore_exe_run was
+ *          called.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void *
+ecore_exe_free(Ecore_Exe *exe)
+{
+   void *data;
+   int ok = 0;
+   int result;
+
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_free");
+        return NULL;
+     }
+
+   data = exe->data;
+
+   if (exe->pre_free_cb)
+     exe->pre_free_cb(data, exe);
+
+   if (exe->doomsday_clock)
+     {
+        struct _ecore_exe_dead_exe *dead;
+
+        ecore_timer_del(exe->doomsday_clock);
+        exe->doomsday_clock = NULL;
+        dead = exe->doomsday_clock_dead;
+        if (dead)
+          {
+             IF_FREE(dead->cmd);
+             free(dead);
+             exe->doomsday_clock_dead = NULL;
+          }
+     }
+   IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler);
+   IF_FN_DEL(ecore_main_fd_handler_del, exe->read_fd_handler);
+   IF_FN_DEL(ecore_main_fd_handler_del, exe->error_fd_handler);
+   if (exe->child_fd_write_x != -1)
+      E_NO_ERRNO(result, close(exe->child_fd_write_x), ok);
+   if (exe->child_fd_read_x != -1)
+      E_NO_ERRNO(result, close(exe->child_fd_read_x), ok);
+   if (exe->child_fd_error_x != -1)
+      E_NO_ERRNO(result, close(exe->child_fd_error_x), ok);
+   if (exe->child_fd_write != -1)
+      E_NO_ERRNO(result, close(exe->child_fd_write), ok);
+   if (exe->child_fd_read != -1)
+      E_NO_ERRNO(result, close(exe->child_fd_read), ok);
+   if (exe->child_fd_error != -1)
+      E_NO_ERRNO(result, close(exe->child_fd_error), ok);
+   IF_FREE(exe->write_data_buf);
+   IF_FREE(exe->read_data_buf);
+   IF_FREE(exe->error_data_buf);
+   IF_FREE(exe->cmd);
+
+   exes = (Ecore_Exe *) eina_inlist_remove(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe));
+   ECORE_MAGIC_SET(exe, ECORE_MAGIC_NONE);
+   IF_FREE(exe->tag);
+   free(exe);
+   return data;
+}
+
+/**
+ * Frees the given event data.
+ *
+ * @param   e The given event data.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void
+ecore_exe_event_data_free(Ecore_Exe_Event_Data *e)
+{
+   if (!e) return;
+   IF_FREE(e->lines);
+   IF_FREE(e->data);
+   free(e);
+}
+
+/**
+ * Retrieves the process ID of the given spawned process.
+ * @param   exe Handle to the given spawned process.
+ * @return  The process ID on success.  @c -1 otherwise.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI pid_t
+ecore_exe_pid_get(const Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pid_get");
+        return -1;
+     }
+   return exe->pid;
+}
+
+/**
+ * Retrieves the command of the given spawned process.
+ * @param   exe Handle to the given spawned process.
+ * @return The command on success.  NULL otherwise. This string is the
+ *         pointer to the internal value and must not be modified in
+ *         any way.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI const char *
+ecore_exe_cmd_get(const Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_cmd_get");
+        return NULL;
+     }
+   return exe->cmd;
+}
+
+/**
+ * Retrieves the data attached to the given process handle.
+ * @param   exe The given process handle.
+ * @return The data pointer attached to @p exe Given to
+ *         ecore_exe_run() or ecore_exe_pipe_run()
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI void *
+ecore_exe_data_get(const Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get");
+        return NULL;
+     }
+   return exe->data;
+}
+
+/**
+ * Retrieves the flags attached to the given process handle.
+ * @param   exe The given process handle.
+ * @return  The flags attached to @p exe.
+ * @ingroup Ecore_Exe_Basic_Group
+ */
+EAPI Ecore_Exe_Flags
+ecore_exe_flags_get(const Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get");
+        return 0;
+     }
+   return exe->flags;
+}
+
+/**
+ * @defgroup Ecore_Exe_Signal_Group Spawned Process Signal Functions
+ *
+ * Functions that send signals to spawned processes.
+ */
+
+/**
+ * Pauses the given process by sending it a @c SIGSTOP signal.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_pause(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pause");
+        return;
+     }
+   kill(exe->pid, SIGSTOP);
+}
+
+/**
+ * Continues the given paused process by sending it a @c SIGCONT signal.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_continue(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_continue");
+        return;
+     }
+   kill(exe->pid, SIGCONT);
+}
+
+/**
+ * Sends the given spawned process a interrupt (@c SIGINT) signal.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_interrupt(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_interrupt");
+        return;
+     }
+   _ecore_exe_dead_attach(exe);
+   kill(exe->pid, SIGINT);
+}
+
+/**
+ * Sends the given spawned process a quit (@c SIGQUIT) signal.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_quit(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_quit");
+        return;
+     }
+   _ecore_exe_dead_attach(exe);
+   kill(exe->pid, SIGQUIT);
+}
+
+/**
+ * Sends the given spawned process a terminate (@c SIGTERM) signal.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_terminate(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_terminate");
+        return;
+     }
+   _ecore_exe_dead_attach(exe);
+   INF("Sending TERM signal to %s (%d).", exe->cmd, exe->pid);
+   kill(exe->pid, SIGTERM);
+}
+
+/**
+ * Kills the given spawned process by sending it a @c SIGKILL signal.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_kill(Ecore_Exe *exe)
+{
+   struct _ecore_exe_dead_exe *dead;
+
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_kill");
+        return;
+     }
+
+   dead = calloc(1, sizeof(struct _ecore_exe_dead_exe));
+   if (dead)
+     {
+        dead->pid = exe->pid;
+        dead->cmd = strdup(exe->cmd);
+        IF_FN_DEL(ecore_timer_del, exe->doomsday_clock);
+        exe->doomsday_clock =
+           ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead, dead);
+     }
+
+   INF("Sending KILL signal to %s (%d).", exe->cmd, exe->pid);
+   kill(exe->pid, SIGKILL);
+}
+
+/**
+ * Sends a @c SIGUSR signal to the given spawned process.
+ * @param   exe Process handle to the given process.
+ * @param   num The number user signal to send.  Must be either 1 or 2, or
+ *              the signal will be ignored.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_signal(Ecore_Exe *exe, int num)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_signal");
+        return;
+     }
+   if (num == 1)
+      kill(exe->pid, SIGUSR1);
+   else if (num == 2)
+      kill(exe->pid, SIGUSR2);
+}
+
+/**
+ * Sends a @c SIGHUP signal to the given spawned process.
+ * @param   exe Process handle to the given process.
+ * @ingroup Ecore_Exe_Signal_Group
+ */
+EAPI void
+ecore_exe_hup(Ecore_Exe *exe)
+{
+   if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+     {
+        ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_hup");
+        return;
+     }
+   kill(exe->pid, SIGHUP);
+}
+
+static Ecore_Exe *
+_ecore_exe_is_it_alive(pid_t pid)
+{
+   Ecore_Exe *exe = NULL;
+
+   /* FIXME: There is no nice, safe, OS independent way to tell if a
+    * particular PID is still alive.  I have written code to do so
+    * for my urunlevel busybox applet (http://urunlevel.sourceforge.net/),
+    * but it's for linux only, and still not guaranteed.
+    *
+    * So for now, we just check that a valid Ecore_Exe structure
+    * exists for it.  Even that is not a guarantee, as the structure
+    * can be freed without killing the process.
+    *
+    * I think we can safely put exe's into two categories, those users
+    * that care about the life of the exe, and the run and forget type.
+    * The run and forget type starts up the exe, then free's the
+    * Ecore_Exe structure straight away.  They can never call any of
+    * the functions that can call this, so we don't worry about them.
+    *
+    * Those user's that care about the life of exe's will keep the
+    * Ecore_Exe structure around, terminate them eventually, or
+    * register for exit events.  For these ones the assumption
+    * that valid Ecore_Exe struct == live exe is almost valid.
+    *
+    * I will probably copy my urunlevel code into here someday.
+    */
+   exe = _ecore_exe_find(pid);
+   if (exe)
+     {
+        if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+           exe = NULL;
+     }
+
+   return exe;
+}
+
+static Eina_Bool
+_ecore_exe_make_sure_its_dead(void *data)
+{
+   struct _ecore_exe_dead_exe *dead;
+
+   dead = data;
+   if (dead)
+     {
+        Ecore_Exe *exe = NULL;
+
+        if ((exe = _ecore_exe_is_it_alive(dead->pid)))
+          {
+             if (dead->cmd)
+                INF("Sending KILL signal to allegedly dead %s (%d).",
+                       dead->cmd, dead->pid);
+             else
+                INF("Sending KILL signal to allegedly dead PID %d.",
+                    dead->pid);
+             exe->doomsday_clock =
+                ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead,
+                                dead);
+             kill(dead->pid, SIGKILL);
+          }
+        else
+          {
+             IF_FREE(dead->cmd);
+             free(dead);
+          }
+     }
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_ecore_exe_make_sure_its_really_dead(void *data)
+{
+   struct _ecore_exe_dead_exe *dead;
+
+   dead = data;
+   if (dead)
+     {
+        Ecore_Exe *exe = NULL;
+
+        if ((exe = _ecore_exe_is_it_alive(dead->pid)))
+          {
+             ERR("RUN!  The zombie wants to eat your brains!  And your CPU!");
+             if (dead->cmd)
+                INF("%s (%d) is not really dead.", dead->cmd, dead->pid);
+             else
+                INF("PID %d is not really dead.", dead->pid);
+             exe->doomsday_clock = NULL;
+          }
+        IF_FREE(dead->cmd);
+        free(dead);
+     }
+   return ECORE_CALLBACK_CANCEL;
+}
+
+void
+_ecore_exe_init(void)
+{
+   ECORE_EXE_EVENT_ADD = ecore_event_type_new();
+   ECORE_EXE_EVENT_DEL = ecore_event_type_new();
+   ECORE_EXE_EVENT_DATA = ecore_event_type_new();
+   ECORE_EXE_EVENT_ERROR = ecore_event_type_new();
+}
+
+void
+_ecore_exe_shutdown(void)
+{
+   while (exes)
+     ecore_exe_free(exes);
+}
+
+Ecore_Exe *
+_ecore_exe_find(pid_t pid)
+{
+   Ecore_Exe *exe;
+
+   EINA_INLIST_FOREACH(exes, exe)
+     {
+        if (exe->pid == pid)
+           return exe;
+     }
+   return NULL;
+}
+
+Ecore_Timer *
+_ecore_exe_doomsday_clock_get(Ecore_Exe *exe)
+{
+   return exe->doomsday_clock;
+}
+
+void
+_ecore_exe_doomsday_clock_set(Ecore_Exe *exe, Ecore_Timer *dc)
+{
+   exe->doomsday_clock = dc;
+}
+
+static inline void
+_ecore_exe_exec_it(const char *exe_cmd, Ecore_Exe_Flags flags)
+{
+   char use_sh = 1;
+   char *buf = NULL;
+   char **args = NULL;
+   int  save_errno = 0;
+
+   /* So what is this doing?
+    *
+    * We are trying to avoid wrapping the exe call with /bin/sh -c.
+    * We conservatively search for certain shell meta characters,
+    * If we don't find them, we can call the exe directly.
+    */
+   if (!strpbrk(exe_cmd, "|&;<>()$`\\\"'*?#"))
+     {
+        char *token;
+        char pre_command = 1;
+        int num_tokens = 0;
+
+        if (!(buf = strdup(exe_cmd)))
+           return;
+
+        token = strtok(buf, " \t\n\v");
+        while (token)
+          {
+             if (token[0] == '~')
+               break;
+             if (pre_command)
+               {
+                  if (token[0] == '[')
+                    break;
+                  if (strchr(token, '='))
+                    break;
+                  else
+                    pre_command = 0;
+               }
+             num_tokens++;
+             token = strtok(NULL, " \t\n\v");
+          }
+        IF_FREE(buf);
+        if ((!token) && (num_tokens))
+          {
+             int i = 0;
+
+             if (!(buf = strdup(exe_cmd)))
+               return;
+
+             token = strtok(buf, " \t\n\v");
+             use_sh = 0;
+             if (!(args = (char **)calloc(num_tokens + 1, sizeof(char *))))
+               {
+                  IF_FREE(buf);
+                  return;
+               }
+             for (i = 0; i < num_tokens; i++)
+               {
+                  if (token)
+                    args[i] = token;
+                  token = strtok(NULL, " \t\n\v");
+               }
+             args[num_tokens] = NULL;
+          }
+     }
+
+   if (!(flags & ECORE_EXE_NOT_LEADER)) setsid();
+   if ((flags & ECORE_EXE_USE_SH))
+     {
+        errno = 0;
+        execl("/bin/sh", "/bin/sh", "-c", exe_cmd, (char *)NULL);
+     }
+   else if (use_sh)
+     {                                /* We have to use a shell to run this. */
+        if (!shell)
+          {                        /* Find users preferred shell. */
+             shell = getenv("SHELL");
+             if (!shell)
+               shell = "/bin/sh";
+          }
+        errno = 0;
+        execl(shell, shell, "-c", exe_cmd, (char *)NULL);
+     }
+   else
+     {                                /* We can run this directly. */
+        errno = 0;
+        execvp(args[0], args);
+     }
+
+   save_errno = errno;
+   IF_FREE(buf);
+   IF_FREE(args);
+   errno = save_errno;
+   return;
+}
+
+static Eina_Bool
+_ecore_exe_data_generic_handler(void *data, Ecore_Fd_Handler *fd_handler, Ecore_Exe_Flags flags)
+{
+   Ecore_Exe *exe;
+   int child_fd;
+   int event_type;
+
+   exe = data;
+
+   /* Sort out what sort of handler we are. */
+   if (flags & ECORE_EXE_PIPE_READ)
+     {
+        flags = ECORE_EXE_PIPE_READ;
+        event_type = ECORE_EXE_EVENT_DATA;
+        child_fd = exe->child_fd_read;
+     }
+   else
+     {
+        flags = ECORE_EXE_PIPE_ERROR;
+        event_type = ECORE_EXE_EVENT_ERROR;
+        child_fd = exe->child_fd_error;
+     }
+
+   if ((fd_handler)
+       && (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)))
+     {
+        unsigned char *inbuf;
+        int inbuf_num;
+
+        /* Get any left over data from last time. */
+        if (flags & ECORE_EXE_PIPE_READ)
+          {
+             inbuf = exe->read_data_buf;
+             inbuf_num = exe->read_data_size;
+             exe->read_data_buf = NULL;
+             exe->read_data_size = 0;
+          }
+        else
+          {
+             inbuf = exe->error_data_buf;
+             inbuf_num = exe->error_data_size;
+             exe->error_data_buf = NULL;
+             exe->error_data_size = 0;
+          }
+
+        for (;;)
+          {
+             int num, lost_exe;
+             char buf[READBUFSIZ];
+
+             lost_exe = 0;
+             errno = 0;
+             if ((num = read(child_fd, buf, READBUFSIZ)) < 1)
+               /* FIXME: SPEED/SIZE TRADE OFF - add a smaller READBUFSIZE
+                * (currently 64k) to inbuf, use that instead of buf, and
+                * save ourselves a memcpy(). */
+               {
+                  lost_exe = ((errno == EIO) ||
+                              (errno == EBADF) ||
+                              (errno == EPIPE) ||
+                              (errno == EINVAL) || (errno == ENOSPC));
+                  if ((errno != EAGAIN) && (errno != EINTR))
+                     perror("_ecore_exe_generic_handler() read problem ");
+               }
+             if (num > 0)
+               {                /* data got read. */
+                  inbuf = realloc(inbuf, inbuf_num + num);
+                  memcpy(inbuf + inbuf_num, buf, num);
+                  inbuf_num += num;
+               }
+             else
+               {                /* No more data to read. */
+                  if (inbuf)
+                    {
+                       Ecore_Exe_Event_Data *e;
+
+                       /* Stash the data away for later. */
+                       if (flags & ECORE_EXE_PIPE_READ)
+                         {
+                            exe->read_data_buf = inbuf;
+                            exe->read_data_size = inbuf_num;
+                         }
+                       else
+                         {
+                            exe->error_data_buf = inbuf;
+                            exe->error_data_size = inbuf_num;
+                         }
+
+                       if (!(exe->flags & ECORE_EXE_PIPE_AUTO))
+                         {
+                            e = ecore_exe_event_data_get(exe, flags);
+                            if (e)        /* Send the event. */
+                              ecore_event_add(event_type, e,
+                                              _ecore_exe_event_exe_data_free,
+                                              NULL);
+                         }
+                    }
+                  if (lost_exe)
+                    {
+                       if (flags & ECORE_EXE_PIPE_READ)
+                         {
+                            if (exe->read_data_size)
+                              INF("There are %d bytes left unsent from the dead exe %s.",
+                                  exe->read_data_size, exe->cmd);
+                         }
+                       else
+                         {
+                            if (exe->error_data_size)
+                              INF("There are %d bytes left unsent from the dead exe %s.",
+                                  exe->error_data_size, exe->cmd);
+                         }
+                       /* Thought about this a bit.  If the exe has actually
+                        * died, this won't do any harm as it must have died
+                        * recently and the pid has not had a chance to recycle.
+                        * It is also a paranoid catchall, coz the usual ecore_signal
+                        * mechenism should kick in.  But let's give it a good
+                        * kick in the head anyway.
+                        */
+                       ecore_exe_terminate(exe);
+                    }
+                  break;
+               }
+          }
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_ecore_exe_data_error_handler(void *data, Ecore_Fd_Handler *fd_handler)
+{
+   return _ecore_exe_data_generic_handler(data, fd_handler,
+                                          ECORE_EXE_PIPE_ERROR);
+}
+
+static Eina_Bool
+_ecore_exe_data_read_handler(void *data, Ecore_Fd_Handler *fd_handler)
+{
+   return _ecore_exe_data_generic_handler(data, fd_handler,
+                                          ECORE_EXE_PIPE_READ);
+}
+
+static Eina_Bool
+_ecore_exe_data_write_handler(void *data, Ecore_Fd_Handler *fd_handler __UNUSED__)
+{
+   Ecore_Exe *exe;
+
+   exe = data;
+   if ((exe->write_fd_handler) &&
+       (ecore_main_fd_handler_active_get
+        (exe->write_fd_handler, ECORE_FD_WRITE)))
+     _ecore_exe_flush(exe);
+
+   /* If we have sent all there is to send, and we need to close the pipe, then close it. */
+   if ((exe->close_stdin == 1)
+       && (exe->write_data_size == exe->write_data_offset))
+     {
+        int ok = 0;
+        int result;
+
+        INF("Closing stdin for %s", exe->cmd);
+        /* if (exe->child_fd_write != -1)  E_NO_ERRNO(result, fsync(exe->child_fd_write), ok);   This a) doesn't work, and b) isn't needed. */
+        IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler);
+        if (exe->child_fd_write != -1)
+           E_NO_ERRNO(result, close(exe->child_fd_write), ok);
+        exe->child_fd_write = -1;
+        IF_FREE(exe->write_data_buf);
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_ecore_exe_flush(Ecore_Exe *exe)
+{
+   int count;
+
+   /* check whether we need to write anything at all. */
+   if ((exe->child_fd_write == -1) || (!exe->write_data_buf))
+      return;
+   if (exe->write_data_size == exe->write_data_offset)
+      return;
+
+   count = write(exe->child_fd_write,
+                 (char *)exe->write_data_buf + exe->write_data_offset,
+                 exe->write_data_size - exe->write_data_offset);
+   if (count < 1)
+     {
+        if (errno == EIO || errno == EBADF || errno == EPIPE || errno == EINVAL || errno == ENOSPC)        /* we lost our exe! */
+          {
+             ecore_exe_terminate(exe);
+             if (exe->write_fd_handler)
+                ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);
+          }
+     }
+   else
+     {
+        exe->write_data_offset += count;
+        if (exe->write_data_offset >= exe->write_data_size)
+          {                        /* Nothing left to write, clean up. */
+             exe->write_data_size = 0;
+             exe->write_data_offset = 0;
+             IF_FREE(exe->write_data_buf);
+             if (exe->write_fd_handler)
+                ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);
+          }
+     }
+}
+
+static void
+_ecore_exe_event_exe_data_free(void *data __UNUSED__, void *ev)
+{
+   Ecore_Exe_Event_Data *e;
+
+   e = ev;
+   ecore_exe_event_data_free(e);
+}
+
+static Ecore_Exe_Event_Add *
+_ecore_exe_event_add_new(void)
+{
+   Ecore_Exe_Event_Add *e;
+
+   e = calloc(1, sizeof(Ecore_Exe_Event_Add));
+   return e;
+}
+
+static void
+_ecore_exe_event_add_free(void *data __UNUSED__, void *ev)
+{
+   Ecore_Exe_Event_Add *e;
+
+   e = ev;
+   free(e);
+}
+
+void               *
+_ecore_exe_event_del_new(void)
+{
+   Ecore_Exe_Event_Del *e;
+
+   e = calloc(1, sizeof(Ecore_Exe_Event_Del));
+   return e;
+}
+
+void
+_ecore_exe_event_del_free(void *data __UNUSED__, void *ev)
+{
+   Ecore_Exe_Event_Del *e;
+
+   e = ev;
+   if (e->exe)
+     ecore_exe_free(e->exe);
+   free(e);
+}
+
+static void
+_ecore_exe_dead_attach(Ecore_Exe *exe)
+{
+   struct _ecore_exe_dead_exe *dead;
+
+   if (exe->doomsday_clock_dead) return;
+   dead = calloc(1, sizeof(struct _ecore_exe_dead_exe));
+   if (dead)
+     {
+        dead->pid = exe->pid;
+        dead->cmd = strdup(exe->cmd);
+        IF_FN_DEL(ecore_timer_del, exe->doomsday_clock);
+        exe->doomsday_clock =
+           ecore_timer_add(10.0, _ecore_exe_make_sure_its_dead, dead);
+        exe->doomsday_clock_dead = dead;
+     }
+}
diff --git a/tests/suite/ecore/src/lib/ecore_getopt.c b/tests/suite/ecore/src/lib/ecore_getopt.c
new file mode 100644 (file)
index 0000000..5b1c7bf
--- /dev/null
@@ -0,0 +1,1735 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+#else
+# define gettext(x) (x)
+# define dgettext(domain, x) (x)
+#endif
+
+#define _(x) dgettext("ecore", x)
+
+#ifdef _WIN32_WCE
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Getopt.h"
+
+static const char *prog = NULL;
+static char **argv = NULL;
+static int argc = 0;
+static int cols = 80;
+static int helpcol = 80 / 3;
+
+static void
+_ecore_getopt_help_print_replace_program(FILE *fp, const Ecore_Getopt *parser __UNUSED__, const char *text)
+{
+   do
+     {
+        const char *d = strchr(text, '%');
+
+        if (!d)
+          {
+             fputs(text, fp);
+             break;
+          }
+
+        if (fwrite(text, 1, d - text, fp) != (size_t)(d - text))
+          return;
+        d++;
+        if (strncmp(d, "prog", sizeof("prog") - 1) == 0)
+          {
+             fputs(prog ? prog : "???", fp);
+             d += sizeof("prog") - 1;
+          }
+        else
+          {
+             if (d[0] == '%')
+               d++;
+             fputc('%', fp);
+          }
+
+        text = d;
+     }
+   while (text[0] != '\0');
+
+   fputc('\n', fp);
+}
+
+static void
+_ecore_getopt_version(FILE *fp, const Ecore_Getopt *parser)
+{
+   fputs(_("Version:"), fp);
+   fputc(' ', fp);
+   _ecore_getopt_help_print_replace_program(fp, parser, parser->version);
+}
+
+static void
+_ecore_getopt_help_usage(FILE *fp, const Ecore_Getopt *parser)
+{
+   fputs(_("Usage:"), fp);
+   fputc(' ', fp);
+
+   if (!parser->usage)
+     {
+        fprintf(fp, _("%s [options]\n"), prog);
+        return;
+     }
+
+   _ecore_getopt_help_print_replace_program(fp, parser, gettext(parser->usage));
+}
+
+static int
+_ecore_getopt_help_line(FILE *fp, const int base, const int total, int used, const char *text, int len)
+{
+   int linebreak = 0;
+   do
+     {
+        /* process line considering spaces (new line and tabs are spaces!) */
+        while ((used < total) && (len > 0))
+          {
+             const char *space = NULL;
+             int i, todo;
+
+             todo = total - used;
+             if (todo > len)
+               todo = len;
+
+             for (i = 0; i < todo; i++)
+               if (isspace(text[i]))
+                 {
+                    space = text + i;
+                    break;
+                 }
+
+             if (space)
+               {
+                  i = fwrite(text, 1, i, fp);
+                  i++;
+                  text += i;
+                  len -= i;
+                  used += i;
+
+                  if (linebreak)
+                    {
+                       linebreak = 0;
+                       continue;
+                    }
+
+                  if (space[0] == '\n')
+                    break;
+                  else if (space[0] == '\t')
+                    {
+                       int c;
+
+                       used--;
+                       c = ((used / 8) + 1) * 8;
+                       if (c < total)
+                         {
+                            for (; used < c; used++)
+                              fputc(' ', fp);
+                         }
+                       else
+                         {
+                            text--;
+                            len++;
+                            break;
+                         }
+                    }
+                  else if (used < total)
+                    fputc(space[0], fp);
+               }
+             else
+               {
+                  i = fwrite(text, 1, i, fp);
+                  text += i;
+                  len -= i;
+                  used += i;
+               }
+             linebreak = 0;
+          }
+        if (len <= 0)
+          break;
+        linebreak = 1;
+        fputc('\n', fp);
+        for (used = 0; used < base; used++)
+          fputc(' ', fp);
+     }
+   while (1);
+
+   return used;
+}
+
+static void
+_ecore_getopt_help_description(FILE *fp, const Ecore_Getopt *parser)
+{
+   const char *p, *prg, *ver;
+   int used, prglen, verlen;
+
+   p = gettext(parser->description);
+   if (!p)
+     return;
+
+   fputc('\n', fp);
+
+   prg = prog ? prog : "???";
+   ver = parser->version ? parser->version : "???";
+
+   prglen = strlen(prg);
+   verlen = strlen(ver);
+
+   used = 0;
+
+   do
+     {
+        const char *d = strchr(p, '%');
+
+        if (!d)
+          {
+             _ecore_getopt_help_line(fp, 0, cols, used, p, strlen(p));
+             break;
+          }
+
+        used = _ecore_getopt_help_line(fp, 0, cols, used, p, d - p);
+        d++;
+        if (strncmp(d, "prog", sizeof("prog") - 1) == 0)
+          {
+             used = _ecore_getopt_help_line(fp, 0, cols, used, prg, prglen);
+             d += sizeof("prog") - 1;
+          }
+        else if (strncmp(d, "version", sizeof("version") - 1) == 0)
+          {
+             used = _ecore_getopt_help_line(fp, 0, cols, used, ver, verlen);
+             d += sizeof("version") - 1;
+          }
+        else
+          {
+             if (d[0] == '%')
+               d++;
+             used = _ecore_getopt_help_line(fp, 0, cols, used, "%", 1);
+          }
+
+        p = d;
+     }
+   while (p[0] != '\0');
+
+   fputs("\n\n", fp);
+}
+
+static void
+_ecore_getopt_copyright(FILE *fp, const Ecore_Getopt *parser)
+{
+   const char *txt = gettext(parser->copyright);
+   fputs(_("Copyright:"), fp);
+   fputs("\n   ", fp);
+   _ecore_getopt_help_line
+     (fp, 3, cols, 3, txt, strlen(txt));
+   fputc('\n', fp);
+}
+
+static void
+_ecore_getopt_license(FILE *fp, const Ecore_Getopt *parser)
+{
+   const char *txt = gettext(parser->license);
+   fputs(_("License:"), fp);
+   fputs("\n   ", fp);
+   _ecore_getopt_help_line
+     (fp, 3, cols, 3, txt, strlen(txt));
+   fputc('\n', fp);
+}
+
+static Ecore_Getopt_Desc_Arg_Requirement
+_ecore_getopt_desc_arg_requirement(const Ecore_Getopt_Desc *desc)
+{
+   switch (desc->action)
+     {
+      case ECORE_GETOPT_ACTION_STORE:
+         return desc->action_param.store.arg_req;
+      case ECORE_GETOPT_ACTION_STORE_CONST:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+      case ECORE_GETOPT_ACTION_STORE_TRUE:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+      case ECORE_GETOPT_ACTION_STORE_FALSE:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+      case ECORE_GETOPT_ACTION_CHOICE:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES;
+      case ECORE_GETOPT_ACTION_APPEND:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES;
+      case ECORE_GETOPT_ACTION_COUNT:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+      case ECORE_GETOPT_ACTION_CALLBACK:
+         return desc->action_param.callback.arg_req;
+      case ECORE_GETOPT_ACTION_HELP:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+      case ECORE_GETOPT_ACTION_VERSION:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+      default:
+         return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+     }
+}
+
+static void
+_ecore_getopt_help_desc_setup_metavar(const Ecore_Getopt_Desc *desc, char *metavar, int *metavarlen, int maxsize)
+{
+   if (desc->metavar)
+     {
+        const char *txt = gettext(desc->metavar);
+        *metavarlen = strlen(txt);
+        if (*metavarlen > maxsize - 1)
+          *metavarlen = maxsize - 1;
+
+        memcpy(metavar, txt, *metavarlen);
+        metavar[*metavarlen] = '\0';
+     }
+   else if (desc->longname)
+     {
+        int i;
+
+        *metavarlen = strlen(desc->longname);
+        if (*metavarlen > maxsize - 1)
+          *metavarlen = maxsize - 1;
+
+        for (i = 0; i < *metavarlen; i++)
+          metavar[i] = toupper(desc->longname[i]);
+        metavar[i] = '\0';
+     }
+}
+
+static int
+_ecore_getopt_help_desc_show_arg(FILE *fp, Ecore_Getopt_Desc_Arg_Requirement requirement, const char *metavar, int metavarlen)
+{
+   int used;
+
+   if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+     return 0;
+
+   used = 0;
+
+   if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL)
+     {
+        fputc('[', fp);
+        used++;
+     }
+
+   if (requirement != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+     {
+        fputc('=', fp);
+        fputs(metavar, fp);
+        used += metavarlen + 1;
+     }
+
+   if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL)
+     {
+        fputc(']', fp);
+        used++;
+     }
+
+   return used;
+}
+
+static int
+_ecore_getopt_help_desc_store(FILE *fp, const int base, const int total, int used, const Ecore_Getopt_Desc *desc)
+{
+   const Ecore_Getopt_Desc_Store *store = &desc->action_param.store;
+   char buf[64];
+   const char *str;
+   size_t len;
+
+   fputc('\n', fp);
+   for (used = 0; used < base; used++)
+     fputc(' ', fp);
+
+   switch (store->type)
+     {
+      case ECORE_GETOPT_TYPE_STR:
+         str = "STR";
+         len = sizeof("STR") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_BOOL:
+         str = "BOOL";
+         len = sizeof("BOOL") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_SHORT:
+         str = "SHORT";
+         len = sizeof("SHORT") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_INT:
+         str = "INT";
+         len = sizeof("INT") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_LONG:
+         str = "LONG";
+         len = sizeof("LONG") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_USHORT:
+         str = "USHORT";
+         len = sizeof("USHORT") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_UINT:
+         str = "UINT";
+         len = sizeof("UINT") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_ULONG:
+         str = "ULONG";
+         len = sizeof("ULONG") - 1;
+         break;
+      case ECORE_GETOPT_TYPE_DOUBLE:
+         str = "DOUBLE";
+         len = sizeof("DOUBLE") - 1;
+         break;
+      default:
+         str = "???";
+         len = sizeof("???") - 1;
+     }
+
+   used = _ecore_getopt_help_line
+     (fp, base, total, used, _("Type: "), strlen(_("Type: ")));
+   used = _ecore_getopt_help_line(fp, base, total, used, str, len);
+
+   if (store->arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES)
+     goto end;
+
+   used = _ecore_getopt_help_line
+     (fp, base, total, used, ". ", sizeof(". ") - 1);
+
+   switch (store->type)
+     {
+      case ECORE_GETOPT_TYPE_STR:
+         str = store->def.strv;
+         len = str ? strlen(str) : 0;
+         break;
+      case ECORE_GETOPT_TYPE_BOOL:
+         str = store->def.boolv ? "true" : "false";
+         len = strlen(str);
+         break;
+      case ECORE_GETOPT_TYPE_SHORT:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%hd", store->def.shortv);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      case ECORE_GETOPT_TYPE_INT:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%d", store->def.intv);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      case ECORE_GETOPT_TYPE_LONG:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%ld", store->def.longv);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      case ECORE_GETOPT_TYPE_USHORT:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%hu", store->def.ushortv);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      case ECORE_GETOPT_TYPE_UINT:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%u", store->def.uintv);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      case ECORE_GETOPT_TYPE_ULONG:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%lu", store->def.ulongv);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      case ECORE_GETOPT_TYPE_DOUBLE:
+         str = buf;
+         len = snprintf(buf, sizeof(buf), "%f", store->def.doublev);
+         if (len > sizeof(buf) - 1)
+           len = sizeof(buf) - 1;
+         break;
+      default:
+         str = "???";
+         len = sizeof("???") - 1;
+     }
+
+   used = _ecore_getopt_help_line
+     (fp, base, total, used, _("Default: "), strlen(_("Default: ")));
+   used = _ecore_getopt_help_line(fp, base, total, used, str, len);
+
+ end:
+   return _ecore_getopt_help_line(fp, base, total, used, ".", 1);
+}
+
+static int
+_ecore_getopt_help_desc_choices(FILE *fp, const int base, const int total, int used, const Ecore_Getopt_Desc *desc)
+{
+   const char *const *itr;
+   const char sep[] = ", ";
+   const int seplen = sizeof(sep) - 1;
+
+   if (used > 0)
+     {
+        fputc('\n', fp);
+        used = 0;
+     }
+   for (; used < base; used++)
+     fputc(' ', fp);
+
+   used = _ecore_getopt_help_line
+     (fp, base, total, used, _("Choices: "), strlen(_("Choices: ")));
+
+   for (itr = desc->action_param.choices; *itr; itr++)
+     {
+        used = _ecore_getopt_help_line
+          (fp, base, total, used, *itr, strlen(*itr));
+        if (itr[1])
+          used = _ecore_getopt_help_line(fp, base, total, used, sep, seplen);
+     }
+
+   return _ecore_getopt_help_line(fp, base, total, used, ".", 1);
+}
+
+static void
+_ecore_getopt_help_desc(FILE *fp, const Ecore_Getopt_Desc *desc)
+{
+   Ecore_Getopt_Desc_Arg_Requirement arg_req;
+   char metavar[32] = "ARG";
+   int metavarlen = 3;
+   int used;
+
+   arg_req = _ecore_getopt_desc_arg_requirement(desc);
+   if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+     _ecore_getopt_help_desc_setup_metavar
+       (desc, metavar, &metavarlen, sizeof(metavar));
+
+   fputs("  ", fp);
+   used = 2;
+
+   if (desc->shortname)
+     {
+        fputc('-', fp);
+        fputc(desc->shortname, fp);
+        used += 2;
+        used += _ecore_getopt_help_desc_show_arg
+          (fp, arg_req, metavar, metavarlen);
+     }
+
+   if (desc->shortname && desc->longname)
+     {
+        fputs(", ", fp);
+        used += 2;
+     }
+
+   if (desc->longname)
+     {
+        int namelen = strlen(desc->longname);
+
+        fputs("--", fp);
+        fputs(desc->longname, fp);
+        used += 2 + namelen;
+        used += _ecore_getopt_help_desc_show_arg
+          (fp, arg_req, metavar, metavarlen);
+     }
+
+   if (!desc->help)
+     goto end;
+
+   if (used + 3 >= helpcol)
+     {
+        fputc('\n', fp);
+        used = 0;
+     }
+
+   for (; used < helpcol; used++)
+     fputc(' ', fp);
+
+   used = _ecore_getopt_help_line
+     (fp, helpcol, cols, used, desc->help, strlen(desc->help));
+
+   switch (desc->action)
+     {
+      case ECORE_GETOPT_ACTION_STORE:
+         _ecore_getopt_help_desc_store(fp, helpcol, cols, used, desc);
+         break;
+      case ECORE_GETOPT_ACTION_CHOICE:
+         _ecore_getopt_help_desc_choices(fp, helpcol, cols, used, desc);
+         break;
+      default:
+         break;
+     }
+
+ end:
+   fputc('\n', fp);
+}
+
+static unsigned char
+_ecore_getopt_desc_is_sentinel(const Ecore_Getopt_Desc *desc)
+{
+   return (desc->shortname == '\0') && (!desc->longname);
+}
+
+static void
+_ecore_getopt_help_options(FILE *fp, const Ecore_Getopt *parser)
+{
+   const Ecore_Getopt_Desc *desc;
+
+   fputs(_("Options:\n"), fp);
+
+   for (desc = parser->descs; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     _ecore_getopt_help_desc(fp, desc);
+
+   fputc('\n', fp);
+}
+
+/**
+ * Show nicely formatted help message for the given parser.
+ *
+ * Message will be print to stderr.
+ */
+void
+ecore_getopt_help(FILE *fp, const Ecore_Getopt *parser)
+{
+   const char *var;
+
+   if (!parser) return;
+
+   if (argc < 1)
+     {
+        ecore_app_args_get(&argc, &argv);
+        if ((argc > 0) && (argv[0]))
+          prog = argv[0];
+        else
+          prog = parser->prog;
+     }
+
+   var = getenv("COLUMNS");
+   if (var)
+     {
+        cols = atoi(var);
+        if (cols < 20)
+          cols = 20;
+
+        helpcol = cols / 3;
+     }
+
+   _ecore_getopt_help_usage(fp, parser);
+   _ecore_getopt_help_description(fp, parser);
+   _ecore_getopt_help_options(fp, parser);
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_long(const Ecore_Getopt *parser, const char *name)
+{
+   const Ecore_Getopt_Desc *desc = parser->descs;
+   const char *p = strchr(name, '=');
+   int len = 0;
+
+   if (p)
+     len = p - name;
+
+   for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     {
+        if (!desc->longname)
+          continue;
+
+        if (p)
+          {
+             if ((strncmp(name, desc->longname, len) == 0) &&
+                 (desc->longname[len] == '\0'))
+               return desc;
+          }
+        else
+          {
+             if (strcmp(name, desc->longname) == 0)
+               return desc;
+          }
+     }
+
+   return NULL;
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_short(const Ecore_Getopt *parser, char name)
+{
+   const Ecore_Getopt_Desc *desc = parser->descs;
+   for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     if (name == desc->shortname)
+       return desc;
+   return NULL;
+}
+
+static int
+_ecore_getopt_parse_find_nonargs_base(const Ecore_Getopt *parser, int argc, char **argv)
+{
+   char **nonargs;
+   int src, dst, used, base;
+
+   nonargs = alloca(sizeof(char*) * argc);
+   src = 1;
+   dst = 1;
+   used = 0;
+   base = 0;
+   while (src < argc)
+     {
+        const Ecore_Getopt_Desc *desc;
+        Ecore_Getopt_Desc_Arg_Requirement arg_req;
+        char *arg = argv[src];
+
+        if (arg[0] != '-')
+          goto found_nonarg;
+
+        if (arg[1] == '-')
+          {
+             if (arg[2] == '\0') /* explicit end of options, "--" */
+               {
+                  base = 1;
+                  break;
+               }
+             desc = _ecore_getopt_parse_find_long(parser, arg + 2);
+          }
+        else
+          desc = _ecore_getopt_parse_find_short(parser, arg[1]);
+
+        if (!desc)
+          {
+             if (arg[1] == '-')
+               fprintf(stderr, _("ERROR: unknown option --%s.\n"), arg + 2);
+             else
+               fprintf(stderr, _("ERROR: unknown option -%c.\n"), arg[1]);
+             if (parser->strict)
+               {
+                  memmove(argv + dst, nonargs, used * sizeof(char *));
+                  return -1;
+               }
+             else
+               goto found_nonarg;
+          }
+
+        if (src != dst)
+          argv[dst] = argv[src];
+        src++;
+        dst++;
+
+        arg_req = _ecore_getopt_desc_arg_requirement(desc);
+        if (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+          continue;
+
+        if (strchr(arg, '='))
+          continue;
+
+        if ((src >= argc) || (argv[src][0] == '-'))
+          continue;
+
+        if (src != dst)
+          argv[dst] = argv[src];
+        src++;
+        dst++;
+        continue;
+
+     found_nonarg:
+        nonargs[used] = arg;
+        used++;
+        src++;
+     }
+
+   if (!base) /* '--' not found */
+     base = dst;
+   else
+     {
+        base = dst;
+        if (src != dst)
+          argv[dst] = argv[src];
+        dst++;
+     }
+
+   memmove(argv + dst, nonargs, used * sizeof(char *));
+   return base;
+}
+
+static void
+_ecore_getopt_desc_print_error(const Ecore_Getopt_Desc *desc, const char *fmt, ...)
+{
+   va_list ap;
+
+   fputs(_("ERROR: "), stderr);
+
+   if (desc->shortname)
+     {
+        fputc('-', stderr);
+        fputc(desc->shortname, stderr);
+     }
+
+   if (desc->shortname && desc->longname)
+     fputs(", ", stderr);
+
+   if (desc->longname)
+     {
+        fputs("--", stderr);
+        fputs(desc->longname, stderr);
+     }
+
+   fputs(": ", stderr);
+
+   va_start(ap, fmt);
+   vfprintf(stderr, fmt, ap);
+   va_end(ap);
+}
+
+static unsigned char
+_ecore_getopt_parse_bool(const char *str, unsigned char *v)
+{
+   if ((strcmp(str, "0") == 0) ||
+       (strcasecmp(str, "f") == 0) ||
+       (strcasecmp(str, "false") == 0) ||
+       (strcasecmp(str, "no") == 0) ||
+       (strcasecmp(str, "off") == 0)
+       )
+     {
+        *v = 0;
+        return 1;
+     }
+   else if ((strcmp(str, "1") == 0) ||
+            (strcasecmp(str, "t") == 0) ||
+            (strcasecmp(str, "true") == 0) ||
+            (strcasecmp(str, "yes") == 0) ||
+            (strcasecmp(str, "on") == 0)
+            )
+     {
+        *v = 1;
+        return 1;
+     }
+
+   return 0;
+}
+
+static unsigned char
+_ecore_getopt_parse_long(const char *str, long int *v)
+{
+   char *endptr = NULL;
+   *v = strtol(str, &endptr, 0);
+   return endptr > str;
+}
+
+static unsigned char
+_ecore_getopt_parse_double(const char *str, double *v)
+{
+   char *endptr = NULL;
+   *v = strtod(str, &endptr);
+   return endptr > str;
+}
+
+static unsigned char
+_ecore_getopt_parse_store(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *value, const char *arg_val)
+{
+   const Ecore_Getopt_Desc_Store *store = &desc->action_param.store;
+   long int v;
+   double d;
+   unsigned char b;
+
+   if (!value->ptrp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+
+   switch (store->arg_req)
+     {
+      case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO:
+         goto use_optional;
+      case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL:
+         if (!arg_val)
+           goto use_optional;
+      case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES:
+         break;
+     }
+
+   switch (store->type)
+     {
+      case ECORE_GETOPT_TYPE_STR:
+         *value->strp = (char *)arg_val;
+         return 1;
+      case ECORE_GETOPT_TYPE_BOOL:
+         if (_ecore_getopt_parse_bool(arg_val, &b))
+           {
+              *value->boolp = b;
+              return 1;
+           }
+         else
+           {
+              _ecore_getopt_desc_print_error
+                (desc, _("unknown boolean value %s.\n"), arg_val);
+              return 0;
+           }
+      case ECORE_GETOPT_TYPE_SHORT:
+         if (!_ecore_getopt_parse_long(arg_val, &v))
+           goto error;
+         *value->shortp = v;
+         return 1;
+      case ECORE_GETOPT_TYPE_INT:
+         if (!_ecore_getopt_parse_long(arg_val, &v))
+           goto error;
+         *value->intp = v;
+         return 1;
+      case ECORE_GETOPT_TYPE_LONG:
+         if (!_ecore_getopt_parse_long(arg_val, &v))
+           goto error;
+         *value->longp = v;
+         return 1;
+      case ECORE_GETOPT_TYPE_USHORT:
+         if (!_ecore_getopt_parse_long(arg_val, &v))
+           goto error;
+         *value->ushortp = v;
+         return 1;
+      case ECORE_GETOPT_TYPE_UINT:
+         if (!_ecore_getopt_parse_long(arg_val, &v))
+           goto error;
+         *value->uintp = v;
+         return 1;
+      case ECORE_GETOPT_TYPE_ULONG:
+         if (!_ecore_getopt_parse_long(arg_val, &v))
+           goto error;
+         *value->ulongp = v;
+         return 1;
+      case ECORE_GETOPT_TYPE_DOUBLE:
+         if (!_ecore_getopt_parse_double(arg_val, &d))
+           goto error;
+         *value->doublep = d;
+         break;
+     }
+
+   return 1;
+
+ error:
+   _ecore_getopt_desc_print_error
+     (desc, _("invalid number format %s\n"), arg_val);
+   return 0;
+
+ use_optional:
+   switch (store->type)
+     {
+      case ECORE_GETOPT_TYPE_STR:
+         *value->strp = (char *)store->def.strv;
+         break;
+      case ECORE_GETOPT_TYPE_BOOL:
+         *value->boolp = store->def.boolv;
+         break;
+      case ECORE_GETOPT_TYPE_SHORT:
+         *value->shortp = store->def.shortv;
+         break;
+      case ECORE_GETOPT_TYPE_INT:
+         *value->intp = store->def.intv;
+         break;
+      case ECORE_GETOPT_TYPE_LONG:
+         *value->longp = store->def.longv;
+         break;
+      case ECORE_GETOPT_TYPE_USHORT:
+         *value->ushortp = store->def.ushortv;
+         break;
+      case ECORE_GETOPT_TYPE_UINT:
+         *value->uintp = store->def.uintv;
+         break;
+      case ECORE_GETOPT_TYPE_ULONG:
+         *value->ulongp = store->def.ulongv;
+         break;
+      case ECORE_GETOPT_TYPE_DOUBLE:
+         *value->doublep = store->def.doublev;
+         break;
+     }
+
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_store_const(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (!val->ptrp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+
+   *val->ptrp = (void *)desc->action_param.store_const;
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_store_true(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (!val->boolp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+   *val->boolp = 1;
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_store_false(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (!val->boolp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+   *val->boolp = 0;
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_choice(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val)
+{
+   const char * const *pchoice;
+
+   if (!val->strp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+
+   pchoice = desc->action_param.choices;
+   for (; *pchoice; pchoice++)
+     if (strcmp(*pchoice, arg_val) == 0)
+       {
+          *val->strp = (char *)*pchoice;
+          return 1;
+       }
+
+   _ecore_getopt_desc_print_error
+     (desc, _("invalid choice \"%s\". Valid values are: "), arg_val);
+
+   pchoice = desc->action_param.choices;
+   for (; *pchoice; pchoice++)
+     {
+        fputs(*pchoice, stderr);
+        if (pchoice[1])
+          fputs(", ", stderr);
+     }
+
+   fputs(".\n", stderr);
+   return 0;
+}
+
+static unsigned char
+_ecore_getopt_parse_append(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val)
+{
+   void *data;
+   long int v;
+   double d;
+   unsigned char b;
+
+   if (!arg_val)
+     {
+        _ecore_getopt_desc_print_error
+          (desc, _("missing parameter to append.\n"));
+        return 0;
+     }
+
+   if (!val->listp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+
+   switch (desc->action_param.append_type)
+     {
+      case ECORE_GETOPT_TYPE_STR:
+         data = strdup(arg_val);
+         break;
+      case ECORE_GETOPT_TYPE_BOOL:
+        {
+           if (_ecore_getopt_parse_bool(arg_val, &b))
+             {
+                data = malloc(sizeof(unsigned char));
+                if (data)
+                  *(unsigned char *)data = b;
+             }
+           else
+             {
+                _ecore_getopt_desc_print_error
+                  (desc, _("unknown boolean value %s.\n"), arg_val);
+                return 0;
+             }
+        }
+        break;
+      case ECORE_GETOPT_TYPE_SHORT:
+        {
+           if (!_ecore_getopt_parse_long(arg_val, &v))
+             goto error;
+           data = malloc(sizeof(short));
+           if (data)
+             *(short *)data = (short)v;
+        }
+        break;
+      case ECORE_GETOPT_TYPE_INT:
+        {
+           if (!_ecore_getopt_parse_long(arg_val, &v))
+             goto error;
+           data = malloc(sizeof(int));
+           if (data)
+             *(int *)data = (int)v;
+        }
+        break;
+      case ECORE_GETOPT_TYPE_LONG:
+        {
+           if (!_ecore_getopt_parse_long(arg_val, &v))
+             goto error;
+           data = malloc(sizeof(long));
+           if (data)
+             *(long *)data = v;
+        }
+        break;
+      case ECORE_GETOPT_TYPE_USHORT:
+        {
+           if (!_ecore_getopt_parse_long(arg_val, &v))
+             goto error;
+           data = malloc(sizeof(unsigned short));
+           if (data)
+             *(unsigned short *)data = (unsigned short)v;
+        }
+        break;
+      case ECORE_GETOPT_TYPE_UINT:
+        {
+           if (!_ecore_getopt_parse_long(arg_val, &v))
+             goto error;
+           data = malloc(sizeof(unsigned int));
+           if (data)
+             *(unsigned int *)data = (unsigned int)v;
+        }
+        break;
+      case ECORE_GETOPT_TYPE_ULONG:
+        {
+           if (!_ecore_getopt_parse_long(arg_val, &v))
+             goto error;
+           data = malloc(sizeof(unsigned long));
+           if (data)
+             *(unsigned long *)data = v;
+        }
+        break;
+      case ECORE_GETOPT_TYPE_DOUBLE:
+        {
+           if (!_ecore_getopt_parse_double(arg_val, &d))
+             goto error;
+           data = malloc(sizeof(double));
+           if (data)
+             *(double *)data = d;
+        }
+        break;
+      default:
+        {
+           _ecore_getopt_desc_print_error(desc, _("could not parse value.\n"));
+           return 0;
+        }
+     }
+
+   *val->listp = eina_list_append(*val->listp, data);
+   return 1;
+
+ error:
+   _ecore_getopt_desc_print_error
+     (desc, _("invalid number format %s\n"), arg_val);
+   return 0;
+}
+
+static unsigned char
+_ecore_getopt_parse_count(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (!val->intp)
+     {
+        _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+        return 0;
+     }
+
+   (*val->intp)++;
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_callback(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val)
+{
+   const Ecore_Getopt_Desc_Callback *cb = &desc->action_param.callback;
+
+   switch (cb->arg_req)
+     {
+      case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO:
+         arg_val = cb->def;
+         break;
+      case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL:
+         if (!arg_val)
+           arg_val = cb->def;
+         break;
+      case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES:
+         break;
+     }
+
+   if (cb->arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+     {
+        if ((!arg_val) || (arg_val[0] == '\0'))
+          {
+             _ecore_getopt_desc_print_error(desc, _("missing parameter.\n"));
+             return 0;
+          }
+
+        if (!val->ptrp)
+          {
+             _ecore_getopt_desc_print_error
+               (desc, _("value has no pointer set.\n"));
+             return 0;
+          }
+     }
+
+   if (!cb->func)
+     {
+        _ecore_getopt_desc_print_error(desc, _("missing callback function!\n"));
+        return 0;
+     }
+
+   return cb->func(parser, desc, arg_val, (void *)cb->data, val);
+}
+
+static unsigned char
+_ecore_getopt_parse_help(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc __UNUSED__, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (val->boolp)
+     (*val->boolp) = 1;
+   ecore_getopt_help(stdout, parser);
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_version(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (val->boolp)
+     (*val->boolp) = 1;
+   if (!parser->version)
+     {
+        _ecore_getopt_desc_print_error(desc, _("no version was defined.\n"));
+        return 0;
+     }
+   _ecore_getopt_version(stdout, parser);
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_copyright(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (val->boolp)
+     (*val->boolp) = 1;
+   if (!parser->copyright)
+     {
+        _ecore_getopt_desc_print_error(desc, _("no copyright was defined.\n"));
+        return 0;
+     }
+   _ecore_getopt_copyright(stdout, parser);
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_license(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val __UNUSED__)
+{
+   if (val->boolp)
+     (*val->boolp) = 1;
+   if (!parser->license)
+     {
+        _ecore_getopt_desc_print_error(desc, _("no license was defined.\n"));
+        return 0;
+     }
+   _ecore_getopt_license(stdout, parser);
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_desc_handle(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *value, const char *arg_val)
+{
+   switch (desc->action)
+     {
+      case ECORE_GETOPT_ACTION_STORE:
+         return _ecore_getopt_parse_store(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_STORE_CONST:
+         return _ecore_getopt_parse_store_const(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_STORE_TRUE:
+         return _ecore_getopt_parse_store_true(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_STORE_FALSE:
+         return _ecore_getopt_parse_store_false(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_CHOICE:
+         return _ecore_getopt_parse_choice(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_APPEND:
+         return _ecore_getopt_parse_append(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_COUNT:
+         return _ecore_getopt_parse_count(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_CALLBACK:
+         return _ecore_getopt_parse_callback(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_HELP:
+         return _ecore_getopt_parse_help(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_VERSION:
+         return _ecore_getopt_parse_version(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_COPYRIGHT:
+         return _ecore_getopt_parse_copyright(parser, desc, value, arg_val);
+      case ECORE_GETOPT_ACTION_LICENSE:
+         return _ecore_getopt_parse_license(parser, desc, value, arg_val);
+      default:
+         return 0;
+     }
+}
+
+static unsigned char
+_ecore_getopt_parse_arg_long(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc __UNUSED__, char **argv, int *idx, int *nonargs, const char *arg)
+{
+   const Ecore_Getopt_Desc *desc;
+   Ecore_Getopt_Desc_Arg_Requirement arg_req;
+   const char *arg_val;
+   int desc_idx;
+   Ecore_Getopt_Value *value;
+   unsigned char ret;
+
+   desc = _ecore_getopt_parse_find_long(parser, arg);
+   if (!desc)
+     {
+        fprintf(stderr, _("ERROR: unknown option --%s, ignored.\n"), arg);
+        if (parser->strict)
+          return 0;
+
+        (*idx)++;
+        return 1;
+     }
+
+   (*idx)++;
+
+   arg_req = _ecore_getopt_desc_arg_requirement(desc);
+   if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+     {
+        arg_val = strchr(arg, '=');
+        if (arg_val)
+          arg_val++;
+        else
+          {
+             if ((*idx < *nonargs) && (argv[*idx][0] != '-'))
+               {
+                  arg_val = argv[*idx];
+                  (*idx)++;
+               }
+             else
+               arg_val = NULL;
+          }
+
+        if (arg_val && arg_val[0] == '\0')
+          arg_val = NULL;
+
+        if ((!arg_val) && (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES))
+          {
+             fprintf
+               (stderr, _("ERROR: option --%s requires an argument!\n"), arg);
+             if (parser->strict)
+               return 0;
+             return 1;
+          }
+     }
+   else
+     arg_val = NULL;
+
+   desc_idx = desc - parser->descs;
+   value = values + desc_idx;
+   ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val);
+   if ((!ret) && parser->strict)
+     return 0;
+
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_arg_short(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc __UNUSED__, char **argv, int *idx, int *nonargs, const char *arg)
+{
+   int run = 1;
+   while (run && (arg[0] != '\0'))
+     {
+        int opt = arg[0];
+        const Ecore_Getopt_Desc *desc;
+        Ecore_Getopt_Desc_Arg_Requirement arg_req;
+        const char *arg_val;
+        int desc_idx;
+        Ecore_Getopt_Value *value;
+        unsigned char ret;
+
+        desc = _ecore_getopt_parse_find_short(parser, arg[0]);
+        if (!desc)
+          {
+             fprintf
+               (stderr, _("ERROR: unknown option -%c, ignored.\n"), arg[0]);
+             if (parser->strict)
+               return 0;
+
+             arg++;
+             continue;
+          }
+
+        arg++;
+
+        arg_req = _ecore_getopt_desc_arg_requirement(desc);
+        if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+          {
+             (*idx)++;
+             run = 0;
+
+             if (arg[0] == '=')
+               arg_val = arg + 1;
+             else if (arg[0] != '\0')
+               arg_val = arg;
+             else
+               {
+                  if ((*idx < *nonargs) && (argv[*idx][0] != '-'))
+                    {
+                       arg_val = argv[*idx];
+                       (*idx)++;
+                    }
+                  else
+                    arg_val = NULL;
+               }
+
+             if (arg_val && arg_val[0] == '\0')
+               arg_val = NULL;
+
+             if ((!arg_val) &&
+                 (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES))
+               {
+                  fprintf
+                    (stderr, _("ERROR: option -%c requires an argument!\n"),
+                     opt);
+                  if (parser->strict)
+                    return 0;
+                  return 1;
+               }
+          }
+        else
+          arg_val = NULL;
+
+        desc_idx = desc - parser->descs;
+        value = values + desc_idx;
+        ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val);
+        if ((!ret) && parser->strict)
+          return 0;
+     }
+
+   if (run)
+     (*idx)++;
+
+   return 1;
+}
+
+static unsigned char
+_ecore_getopt_parse_arg(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv, int *idx, int *nonargs)
+{
+   char *arg = argv[*idx];
+
+   if (arg[0] != '-')
+     {
+        char **dst, **src, **src_end;
+
+        dst = argv + *idx;
+        src = dst + 1;
+        src_end = src + *nonargs - *idx - 1;
+
+        for (; src < src_end; src++, dst++)
+          *dst = *src;
+
+        *dst = arg;
+        (*nonargs)--;
+        return 1;
+     }
+
+   if (arg[1] == '-')
+     return _ecore_getopt_parse_arg_long
+       (parser, values, argc, argv, idx, nonargs, arg + 2);
+   else
+     return _ecore_getopt_parse_arg_short
+       (parser, values, argc, argv, idx, nonargs, arg + 1);
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_short_other(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *orig)
+{
+   const Ecore_Getopt_Desc *desc = parser->descs;
+   const char c = orig->shortname;
+
+   for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     {
+        if (desc == orig)
+          return NULL;
+
+        if (c == desc->shortname)
+          return desc;
+     }
+
+   return NULL;
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_long_other(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *orig)
+{
+   const Ecore_Getopt_Desc *desc = parser->descs;
+   const char *name = orig->longname;
+
+   for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     {
+        if (desc == orig)
+          return NULL;
+
+        if (desc->longname && (strcmp(name, desc->longname) == 0))
+          return desc;
+     }
+
+   return NULL;
+}
+
+/**
+ * Check parser for duplicate entries, print them out.
+ *
+ * @return 1 if there are duplicates, 0 otherwise.
+ */
+unsigned char
+ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser)
+{
+   const Ecore_Getopt_Desc *desc = parser->descs;
+   for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     {
+       if (desc->shortname)
+         {
+           const Ecore_Getopt_Desc *other;
+           other = _ecore_getopt_parse_find_short_other(parser, desc);
+           if (other)
+             {
+                _ecore_getopt_desc_print_error
+                  (desc, "short name -%c already exists.", desc->shortname);
+
+                if (other->longname)
+                  fprintf(stderr, " Other is --%s.\n", other->longname);
+                else
+                  fputc('\n', stderr);
+                return 1;
+             }
+         }
+
+       if (desc->longname)
+         {
+           const Ecore_Getopt_Desc *other;
+           other = _ecore_getopt_parse_find_long_other(parser, desc);
+           if (other)
+             {
+                _ecore_getopt_desc_print_error
+                  (desc, "long name --%s already exists.", desc->longname);
+
+                if (other->shortname)
+                  fprintf(stderr, " Other is -%c.\n", other->shortname);
+                else
+                  fputc('\n', stderr);
+                return 1;
+             }
+         }
+     }
+   return 0;
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_find_help(const Ecore_Getopt *parser)
+{
+   const Ecore_Getopt_Desc *desc = parser->descs;
+   for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+     if (desc->action == ECORE_GETOPT_ACTION_HELP)
+       return desc;
+   return NULL;
+}
+
+/**
+ * Parse command line parameters.
+ *
+ * Walks the command line parameters and parse them based on @a parser
+ * description, doing actions based on @c parser->descs->action, like
+ * showing help text, license, copyright, storing values in values and
+ * so on.
+ *
+ * It is expected that values is of the same size than @c parser->descs,
+ * options that do not need a value it will be left untouched.
+ *
+ * All values are expected to be initialized before use. Options with
+ * action @c ECORE_GETOPT_ACTION_STORE and non required arguments
+ * (others than @c ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES), are expected
+ * to provide a value in @c def to be used.
+ *
+ * The following actions will store 1 on value as a boolean
+ * (@c value->boolp) if it's not NULL to indicate these actions were executed:
+ *   - @c ECORE_GETOPT_ACTION_HELP
+ *   - @c ECORE_GETOPT_ACTION_VERSION
+ *   - @c ECORE_GETOPT_ACTION_COPYRIGHT
+ *   - @c ECORE_GETOPT_ACTION_LICENSE
+ *
+ * Just @c ECORE_GETOPT_ACTION_APPEND will allocate memory and thus
+ * need to be freed. For consistency between all of appended subtypes,
+ * @c eina_list->data will contain an allocated memory with the value,
+ * that is, for @c ECORE_GETOPT_TYPE_STR it will contain a copy of the
+ * argument, @c ECORE_GETOPT_TYPE_INT a pointer to an allocated
+ * integer and so on.
+ *
+ * If parser is in strict mode (see @c Ecore_Getopt->strict), then any
+ * error will abort parsing and -1 is returned. Otherwise it will try
+ * to continue as far as possible.
+ *
+ * This function may reorder @a argv elements.
+ *
+ * Translation of help strings (description), metavar, usage, license
+ * and copyright may be translated, standard/global gettext() call
+ * will be applied on them if ecore was compiled with such support.
+ *
+ * @param parser description of how to work.
+ * @param value where to store values, it is assumed that this is a vector
+ *        of the same size as @c parser->descs. Values should be previously
+ *        initialized.
+ * @param argc how many elements in @a argv. If not provided it will be
+ *        retrieved with ecore_app_args_get().
+ * @param argv command line parameters.
+ *
+ * @return index of first non-option parameter or -1 on error.
+ */
+int
+ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv)
+{
+   int i, nonargs;
+
+   if (!parser)
+     {
+        fputs(_("ERROR: no parser provided.\n"), stderr);
+        return -1;
+     }
+   if (!values)
+     {
+        fputs(_("ERROR: no values provided.\n"), stderr);
+        return -1;
+     }
+
+   if ((argc < 1) || (!argv))
+     ecore_app_args_get(&argc, &argv);
+
+   if (argc < 1)
+     {
+        fputs(_("ERROR: no arguments provided.\n"), stderr);
+        return -1;
+     }
+
+   if (argv[0])
+     prog = argv[0];
+   else
+     prog = parser->prog;
+
+   nonargs = _ecore_getopt_parse_find_nonargs_base(parser, argc, argv);
+   if (nonargs < 0)
+     goto error;
+
+   if (nonargs > argc)
+     nonargs = argc;
+
+   i = 1;
+   while (i < nonargs)
+     if (!_ecore_getopt_parse_arg(parser, values, argc, argv, &i, &nonargs))
+       goto error;
+
+   return nonargs;
+
+ error:
+   {
+      const Ecore_Getopt_Desc *help;
+      fputs(_("ERROR: invalid options found."), stderr);
+
+      help = _ecore_getopt_find_help(parser);
+      if (!help)
+        fputc('\n', stderr);
+      else if (help->longname)
+        fprintf(stderr, _(" See --%s.\n"), help->longname);
+      else
+        fprintf(stderr, _(" See -%c.\n"), help->shortname);
+   }
+
+   return -1;
+}
+
+/**
+ * Utility to free list and nodes allocated by @a ECORE_GETOPT_ACTION_APPEND.
+ *
+ * @param list pointer to list to be freed.
+ * @return always NULL, so you can easily make your list head NULL.
+ */
+Eina_List *
+ecore_getopt_list_free(Eina_List *list)
+{
+   void *data;
+
+   EINA_LIST_FREE(list, data)
+     free(data);
+   return NULL;
+}
+
+/**
+ * Helper ecore_getopt callback to parse geometry (x:y:w:h).
+ *
+ * Storage must be a pointer to @c Eina_Rectangle and will be used to
+ * store the four values passed in the given string.
+ *
+ * @c callback_data value is ignored, you can safely use @c NULL.
+ */
+unsigned char
+ecore_getopt_callback_geometry_parse(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc __UNUSED__, const char *str, void *data __UNUSED__, Ecore_Getopt_Value *storage)
+{
+   Eina_Rectangle *v = (Eina_Rectangle *)storage->ptrp;
+
+   if (sscanf(str, "%d:%d:%d:%d", &v->x, &v->y, &v->w, &v->h) != 4)
+     {
+        fprintf(stderr, _("ERROR: incorrect geometry value '%s'\n"), str);
+        return 0;
+     }
+
+   return 1;
+}
+
+/**
+ * Helper ecore_getopt callback to parse geometry size (WxH).
+ *
+ * Storage must be a pointer to @c Eina_Rectangle and will be used to
+ * store the two values passed in the given string and 0 in the x and y
+ * fields.
+ *
+ * @c callback_data value is ignored, you can safely use @c NULL.
+ */
+unsigned char
+ecore_getopt_callback_size_parse(const Ecore_Getopt *parser __UNUSED__, const Ecore_Getopt_Desc *desc __UNUSED__, const char *str, void *data __UNUSED__, Ecore_Getopt_Value *storage)
+{
+   Eina_Rectangle *v = (Eina_Rectangle *)storage->ptrp;
+
+   if (sscanf(str, "%dx%d", &v->w, &v->h) != 2)
+     {
+        fprintf(stderr, _("ERROR: incorrect size value '%s'\n"), str);
+        return 0;
+     }
+   v->x = 0;
+   v->y = 0;
+
+   return 1;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_glib.c b/tests/suite/ecore/src/lib/ecore_glib.c
new file mode 100644 (file)
index 0000000..0972776
--- /dev/null
@@ -0,0 +1,286 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+
+static Eina_Bool _ecore_glib_active = EINA_FALSE;
+static Ecore_Select_Function _ecore_glib_select_original;
+static GCond *_ecore_glib_cond = NULL;
+static GPollFD *_ecore_glib_fds = NULL;
+static size_t _ecore_glib_fds_size = 0;
+static const size_t ECORE_GLIB_FDS_INITIAL = 128;
+static const size_t ECORE_GLIB_FDS_STEP = 8;
+static const size_t ECORE_GLIB_FDS_MAX_FREE = 256;
+
+static Eina_Bool
+_ecore_glib_fds_resize(size_t size)
+{
+   void *tmp = realloc(_ecore_glib_fds, sizeof(GPollFD) * size);
+   
+   if (!tmp)
+     {
+        ERR("Could not realloc from %zu to %zu buckets.",
+            _ecore_glib_fds_size, size);
+        return EINA_FALSE;
+     }
+   
+   _ecore_glib_fds = tmp;
+   _ecore_glib_fds_size = size;
+   return EINA_TRUE;
+}
+
+static int
+_ecore_glib_context_query(GMainContext *ctx, int priority, int *p_timer)
+{
+   int reqfds;
+
+   if (_ecore_glib_fds_size == 0)
+     {
+        if (!_ecore_glib_fds_resize(ECORE_GLIB_FDS_INITIAL)) return -1;
+     }
+   
+   while (1)
+     {
+        size_t size;
+        
+        reqfds = g_main_context_query
+          (ctx, priority, p_timer, _ecore_glib_fds, _ecore_glib_fds_size);
+        if (reqfds <= (int)_ecore_glib_fds_size) break;
+
+        size = (1 + reqfds / ECORE_GLIB_FDS_STEP) * ECORE_GLIB_FDS_STEP;
+        if (!_ecore_glib_fds_resize(size)) return -1;
+     }
+
+   if (reqfds + ECORE_GLIB_FDS_MAX_FREE < _ecore_glib_fds_size)
+     {
+        size_t size;
+
+        size = (1 + reqfds / ECORE_GLIB_FDS_MAX_FREE) * ECORE_GLIB_FDS_MAX_FREE;
+        _ecore_glib_fds_resize(size);
+     }
+
+   return reqfds;
+}
+
+static int
+_ecore_glib_context_poll_from(const GPollFD *pfds, int count, fd_set *rfds, fd_set *wfds, fd_set *efds)
+{
+   const GPollFD *itr = pfds, *itr_end = pfds + count;
+   int glib_fds = -1;
+   
+   for (; itr < itr_end; itr++)
+     {
+        if (glib_fds < itr->fd)
+          glib_fds = itr->fd;
+
+        if (itr->events & G_IO_IN)
+          FD_SET(itr->fd, rfds);
+        if (itr->events & G_IO_OUT)
+          FD_SET(itr->fd, wfds);
+        if (itr->events & (G_IO_HUP | G_IO_ERR))
+          FD_SET(itr->fd, efds);
+     }
+
+   return glib_fds + 1;
+}
+
+static int
+_ecore_glib_context_poll_to(GPollFD *pfds, int count, const fd_set *rfds, const fd_set *wfds, const fd_set *efds, int ready)
+{
+   GPollFD *itr = pfds, *itr_end = pfds + count;
+   
+   for (; itr < itr_end && ready > 0; itr++)
+     {
+        itr->revents = 0;
+        if (FD_ISSET(itr->fd, rfds))
+          {
+             itr->revents |= G_IO_IN;
+             ready--;
+          }
+        if (FD_ISSET(itr->fd, wfds))
+          {
+             itr->revents |= G_IO_OUT;
+             ready--;
+          }
+        if (FD_ISSET(itr->fd, efds))
+          {
+             itr->revents |= G_IO_ERR;
+             ready--;
+          }
+     }
+   return ready;
+}
+
+static int
+_ecore_glib_select__locked(GMainContext *ctx, int ecore_fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *ecore_timeout)
+{
+   int priority, maxfds, glib_fds, reqfds, reqtimeout, ret;
+   struct timeval *timeout, glib_timeout;
+
+   g_main_context_prepare(ctx, &priority);
+   reqfds = _ecore_glib_context_query(ctx, priority, &reqtimeout);
+   if (reqfds < 0) goto error;
+
+   glib_fds = _ecore_glib_context_poll_from
+     (_ecore_glib_fds, reqfds, rfds, wfds, efds);
+
+   if (reqtimeout == -1)
+     timeout = ecore_timeout;
+   else
+     {
+        glib_timeout.tv_sec = reqtimeout / 1000;
+        glib_timeout.tv_usec = (reqtimeout % 1000) * 1000;
+        
+        if (!ecore_timeout || timercmp(ecore_timeout, &glib_timeout, >))
+          timeout = &glib_timeout;
+        else
+          timeout = ecore_timeout;
+     }
+
+   maxfds = (ecore_fds >= glib_fds) ? ecore_fds : glib_fds;
+   ret = _ecore_glib_select_original(maxfds, rfds, wfds, efds, timeout);
+
+   ret = _ecore_glib_context_poll_to
+     (_ecore_glib_fds, reqfds, rfds, wfds, efds, ret);
+
+   if (g_main_context_check(ctx, priority, _ecore_glib_fds, reqfds))
+     g_main_context_dispatch(ctx);
+
+   return ret;
+
+ error:
+   return _ecore_glib_select_original
+     (ecore_fds, rfds, wfds, efds, ecore_timeout);
+}
+
+static int
+_ecore_glib_select(int ecore_fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *ecore_timeout)
+{
+   GStaticMutex lock = G_STATIC_MUTEX_INIT;
+   GMutex *mutex = g_static_mutex_get_mutex(&lock);
+   GMainContext *ctx = g_main_context_default();
+   int ret;
+
+   if (g_main_context_acquire(ctx))
+     g_mutex_lock(mutex);
+   else
+     {
+        if (!_ecore_glib_cond)
+          _ecore_glib_cond = g_cond_new();
+
+        while (!g_main_context_wait(ctx, _ecore_glib_cond, mutex))
+          g_thread_yield();
+     }
+
+   ret = _ecore_glib_select__locked
+     (ctx, ecore_fds, rfds, wfds, efds, ecore_timeout);
+
+   g_mutex_unlock(mutex);
+   g_main_context_release(ctx);
+
+   return ret;
+}
+#endif
+
+void
+_ecore_glib_init(void)
+{
+}
+
+void
+_ecore_glib_shutdown(void)
+{
+#ifdef HAVE_GLIB
+   if (!_ecore_glib_active) return;
+   _ecore_glib_active = EINA_FALSE;
+
+   if (ecore_main_loop_select_func_get() == _ecore_glib_select)
+     ecore_main_loop_select_func_set(_ecore_glib_select_original);
+   
+   if (_ecore_glib_fds)
+     {
+        free(_ecore_glib_fds);
+        _ecore_glib_fds = NULL;
+     }
+   _ecore_glib_fds_size = 0;
+
+   if (_ecore_glib_cond)
+     {
+        g_cond_free(_ecore_glib_cond);
+        _ecore_glib_cond = NULL;
+     }
+#endif
+}
+
+/**
+ * Request ecore to integrate GLib's main loop.
+ *
+ * This will add a small overhead during every main loop interaction
+ * by checking glib's default main context (used by its main loop). If
+ * it have events to be checked (timers, file descriptors or idlers),
+ * then these will be polled alongside with Ecore's own events, then
+ * dispatched before Ecore's. This is done by calling
+ * ecore_main_loop_select_func_set().
+ *
+ * This will cooperate with previously set
+ * ecore_main_loop_select_func_set() by calling the old
+ * function. Similarly, if you want to override
+ * ecore_main_loop_select_func_set() after main loop is integrated,
+ * call the new select function set by this call (get it by calling
+ * ecore_main_loop_select_func_get() right after
+ * ecore_main_loop_glib_integrate()).
+ *
+ * This is useful to use GMainLoop libraries, like GTK, GUPnP,
+ * LibSoup, GConf and more. Adobe Flash plugin and other plugins
+ * systems depend on this as well.
+ *
+ * Once initialized/integrated, it will be valid until Ecore is
+ * completely shut down.
+ *
+ * @note this is only available if Ecore was compiled with GLib support.
+ *
+ * @return @c EINA_TRUE on success of @c EINA_FALSE if it failed,
+ *         likely no GLib support in Ecore.
+ */
+EAPI Eina_Bool
+ecore_main_loop_glib_integrate(void)
+{
+#ifdef HAVE_GLIB
+   void *func;
+
+   if (_ecore_glib_active) return EINA_TRUE;
+   func = ecore_main_loop_select_func_get();
+   if (func == _ecore_glib_select) return EINA_TRUE;
+   _ecore_glib_select_original = func;
+   ecore_main_loop_select_func_set(_ecore_glib_select);
+   _ecore_glib_active = EINA_TRUE;
+   return EINA_TRUE;
+#else
+   fputs("ERROR: no glib support in ecore.\n", stderr);
+   return EINA_FALSE;
+#endif
+}
+
+Eina_Bool _ecore_glib_always_integrate = 1;
+
+/**
+ * Disable always integrating glib
+ * 
+ * If ecore is compiled with --enable-glib-integration-always (to always
+ * call ecore_main_loop_glib_integrate() when ecore_init() is called), then
+ * calling this before calling ecore_init() will disable the integration.
+ * This is for apps that explicitly do not want this to happen for whatever
+ * reasons they may have.
+ */
+EAPI void
+ecore_main_loop_glib_always_integrate_disable(void)
+{
+   _ecore_glib_always_integrate = 0;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_idle_enterer.c b/tests/suite/ecore/src/lib/ecore_idle_enterer.c
new file mode 100644 (file)
index 0000000..2b827ce
--- /dev/null
@@ -0,0 +1,171 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+struct _Ecore_Idle_Enterer
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   Ecore_Task_Cb func;
+   void        *data;
+   int          references;
+   Eina_Bool    delete_me : 1;
+};
+
+
+static Ecore_Idle_Enterer *idle_enterers = NULL;
+static Ecore_Idle_Enterer *idle_enterer_current = NULL;
+static int                 idle_enterers_delete_me = 0;
+
+/**
+ * Add an idle enterer handler.
+ * @param   func The function to call when entering an idle state.
+ * @param   data The data to be passed to the @p func call
+ * @return  A handle to the idle enterer callback if successful.  Otherwise,
+ *          NULL is returned.
+ * @ingroup Idle_Group
+ */
+EAPI Ecore_Idle_Enterer *
+ecore_idle_enterer_add(Ecore_Task_Cb func, const void *data)
+{
+   Ecore_Idle_Enterer *ie;
+
+   if (!func) return NULL;
+   ie = calloc(1, sizeof(Ecore_Idle_Enterer));
+   if (!ie) return NULL;
+   ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLE_ENTERER);
+   ie->func = func;
+   ie->data = (void *)data;
+   idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_append(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie));
+   return ie;
+}
+
+/**
+ * Add an idle enterer handler at the start of the list so it gets called earlier than others.
+ * @param   func The function to call when entering an idle state.
+ * @param   data The data to be passed to the @p func call
+ * @return  A handle to the idle enterer callback if successful.  Otherwise,
+ *          NULL is returned.
+ * @ingroup Idle_Group
+ */
+EAPI Ecore_Idle_Enterer *
+ecore_idle_enterer_before_add(Ecore_Task_Cb func, const void *data)
+{
+   Ecore_Idle_Enterer *ie;
+
+   if (!func) return NULL;
+   ie = calloc(1, sizeof(Ecore_Idle_Enterer));
+   if (!ie) return NULL;
+   ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLE_ENTERER);
+   ie->func = func;
+   ie->data = (void *)data;
+   idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_prepend(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie));
+   return ie;
+}
+
+/**
+ * Delete an idle enterer callback.
+ * @param   idle_enterer The idle enterer to delete
+ * @return  The data pointer passed to the idler enterer callback on success.
+ *          NULL otherwise.
+ * @ingroup Idle_Group
+ */
+EAPI void *
+ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer)
+{
+   if (!ECORE_MAGIC_CHECK(idle_enterer, ECORE_MAGIC_IDLE_ENTERER))
+     {
+        ECORE_MAGIC_FAIL(idle_enterer, ECORE_MAGIC_IDLE_ENTERER,
+                         "ecore_idle_enterer_del");
+        return NULL;
+     }
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(idle_enterer->delete_me, NULL);
+   idle_enterer->delete_me = 1;
+   idle_enterers_delete_me = 1;
+   return idle_enterer->data;
+}
+
+void
+_ecore_idle_enterer_shutdown(void)
+{
+   Ecore_Idle_Enterer *ie;
+   while ((ie = idle_enterers))
+     {
+        idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_remove(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(idle_enterers));
+        ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE);
+        free(ie);
+     }
+   idle_enterers_delete_me = 0;
+   idle_enterer_current = NULL;
+}
+
+void
+_ecore_idle_enterer_call(void)
+{
+   if (!idle_enterer_current)
+     {
+        /* regular main loop, start from head */
+        idle_enterer_current = idle_enterers;
+     }
+   else
+     {
+        /* recursive main loop, continue from where we were */
+        idle_enterer_current =
+          (Ecore_Idle_Enterer *)EINA_INLIST_GET(idle_enterer_current)->next;
+     }
+
+   while (idle_enterer_current)
+     {
+        Ecore_Idle_Enterer *ie = (Ecore_Idle_Enterer *)idle_enterer_current;
+        if (!ie->delete_me)
+          {
+             ie->references++;
+             if (!ie->func(ie->data))
+               {
+                  if (!ie->delete_me) ecore_idle_enterer_del(ie);
+               }
+             ie->references--;
+          }
+        if (idle_enterer_current) /* may have changed in recursive main loops */
+          idle_enterer_current =
+            (Ecore_Idle_Enterer *)EINA_INLIST_GET(idle_enterer_current)->next;
+     }
+   if (idle_enterers_delete_me)
+     {
+        Ecore_Idle_Enterer *l;
+        int deleted_idler_enterers_in_use = 0;
+
+        for (l = idle_enterers; l;)
+          {
+             Ecore_Idle_Enterer *ie = l;
+             l = (Ecore_Idle_Enterer *) EINA_INLIST_GET(l)->next;
+             if (ie->delete_me)
+               {
+                  if (ie->references)
+                    {
+                       deleted_idler_enterers_in_use++;
+                       continue;
+                    }
+
+                  idle_enterers = (Ecore_Idle_Enterer *) eina_inlist_remove(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie));
+                  ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE);
+                  free(ie);
+               }
+          }
+        if (!deleted_idler_enterers_in_use)
+          idle_enterers_delete_me = 0;
+     }
+}
+
+int
+_ecore_idle_enterer_exist(void)
+{
+   if (idle_enterers) return 1;
+   return 0;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_idle_exiter.c b/tests/suite/ecore/src/lib/ecore_idle_exiter.c
new file mode 100644 (file)
index 0000000..d8234e3
--- /dev/null
@@ -0,0 +1,148 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+struct _Ecore_Idle_Exiter
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   Ecore_Task_Cb func;
+   void        *data;
+   int          references;
+   Eina_Bool    delete_me : 1;
+};
+
+
+static Ecore_Idle_Exiter *idle_exiters = NULL;
+static Ecore_Idle_Exiter *idle_exiter_current = NULL;
+static int                idle_exiters_delete_me = 0;
+
+/**
+ * Add an idle exiter handler.
+ * @param func The function to call when exiting an idle state.
+ * @param data The data to be passed to the @p func call
+ * @return A handle to the idle exiter callback on success.  NULL otherwise.
+ * @ingroup Idle_Group
+ */
+EAPI Ecore_Idle_Exiter *
+ecore_idle_exiter_add(Ecore_Task_Cb func, const void *data)
+{
+   Ecore_Idle_Exiter *ie;
+
+   if (!func) return NULL;
+   ie = calloc(1, sizeof(Ecore_Idle_Exiter));
+   if (!ie) return NULL;
+   ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLE_EXITER);
+   ie->func = func;
+   ie->data = (void *)data;
+   idle_exiters = (Ecore_Idle_Exiter *) eina_inlist_append(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(ie));
+   return ie;
+}
+
+/**
+ * Delete an idle exiter handler from the list to be run on exiting idle state.
+ * @param idle_exiter The idle exiter to delete
+ * @return The data pointer that was being being passed to the handler if
+ *         successful.  NULL otherwise.
+ * @ingroup Idle_Group
+ */
+EAPI void *
+ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter)
+{
+   if (!ECORE_MAGIC_CHECK(idle_exiter, ECORE_MAGIC_IDLE_EXITER))
+     {
+        ECORE_MAGIC_FAIL(idle_exiter, ECORE_MAGIC_IDLE_EXITER,
+                         "ecore_idle_exiter_del");
+        return NULL;
+     }
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(idle_exiter->delete_me, NULL);
+   idle_exiter->delete_me = 1;
+   idle_exiters_delete_me = 1;
+   return idle_exiter->data;
+}
+
+void
+_ecore_idle_exiter_shutdown(void)
+{
+   Ecore_Idle_Exiter *ie;
+   while ((ie = idle_exiters))
+     {
+        idle_exiters = (Ecore_Idle_Exiter *) eina_inlist_remove(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(idle_exiters));
+        ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE);
+        free(ie);
+     }
+   idle_exiters_delete_me = 0;
+   idle_exiter_current = NULL;
+}
+
+void
+_ecore_idle_exiter_call(void)
+{
+   if (!idle_exiter_current)
+     {
+        /* regular main loop, start from head */
+        idle_exiter_current = idle_exiters;
+     }
+   else
+     {
+        /* recursive main loop, continue from where we were */
+        idle_exiter_current =
+          (Ecore_Idle_Exiter *)EINA_INLIST_GET(idle_exiter_current)->next;
+     }
+
+   while (idle_exiter_current)
+     {
+        Ecore_Idle_Exiter *ie = (Ecore_Idle_Exiter *)idle_exiter_current;
+        if (!ie->delete_me)
+          {
+             ie->references++;
+             if (!ie->func(ie->data))
+               {
+                  if (!ie->delete_me) ecore_idle_exiter_del(ie);
+               }
+             ie->references--;
+          }
+        if (idle_exiter_current) /* may have changed in recursive main loops */
+          idle_exiter_current =
+            (Ecore_Idle_Exiter *)EINA_INLIST_GET(idle_exiter_current)->next;
+     }
+   if (idle_exiters_delete_me)
+     {
+        Ecore_Idle_Exiter *l;
+        int deleted_idler_exiters_in_use = 0;
+
+        for (l = idle_exiters; l;)
+          {
+             Ecore_Idle_Exiter *ie = l;
+
+             l = (Ecore_Idle_Exiter *) EINA_INLIST_GET(l)->next;
+             if (ie->delete_me)
+               {
+                  if (ie->references)
+                    {
+                       deleted_idler_exiters_in_use++;
+                       continue;
+                    }
+
+                  idle_exiters = (Ecore_Idle_Exiter *) eina_inlist_remove(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(ie));
+                  ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE);
+                  free(ie);
+               }
+          }
+        if (!deleted_idler_exiters_in_use)
+          idle_exiters_delete_me = 0;
+     }
+}
+
+int
+_ecore_idle_exiter_exist(void)
+{
+   if (idle_exiters) return 1;
+   return 0;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_idler.c b/tests/suite/ecore/src/lib/ecore_idler.c
new file mode 100644 (file)
index 0000000..8f1c820
--- /dev/null
@@ -0,0 +1,154 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+struct _Ecore_Idler
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   Ecore_Task_Cb func;
+   void        *data;
+   int          references;
+   Eina_Bool    delete_me : 1;
+};
+
+
+static Ecore_Idler *idlers = NULL;
+static Ecore_Idler *idler_current = NULL;
+static int          idlers_delete_me = 0;
+
+/**
+ * Add an idler handler.
+ * @param  func The function to call when idling.
+ * @param  data The data to be passed to this @p func call.
+ * @return A idler handle if successfully added.  NULL otherwise.
+ * @ingroup Idle_Group
+ *
+ * Add an idler handle to the event loop, returning a handle on success and
+ * NULL otherwise.  The function @p func will be called repeatedly while
+ * no other events are ready to be processed, as long as it returns 1
+ * (or ECORE_CALLBACK_RENEW). A return of 0 (or ECORE_CALLBACK_CANCEL) deletes
+ * the idler.
+ *
+ * Idlers are useful for progressively prossessing data without blocking.
+ */
+EAPI Ecore_Idler *
+ecore_idler_add(Ecore_Task_Cb func, const void *data)
+{
+   Ecore_Idler *ie;
+
+   if (!func) return NULL;
+   ie = calloc(1, sizeof(Ecore_Idler));
+   if (!ie) return NULL;
+   ECORE_MAGIC_SET(ie, ECORE_MAGIC_IDLER);
+   ie->func = func;
+   ie->data = (void *)data;
+   idlers = (Ecore_Idler *) eina_inlist_append(EINA_INLIST_GET(idlers), EINA_INLIST_GET(ie));
+   return ie;
+}
+
+/**
+ * Delete an idler callback from the list to be executed.
+ * @param  idler The handle of the idler callback to delete
+ * @return The data pointer passed to the idler callback on success.  NULL
+ *         otherwise.
+ * @ingroup Idle_Group
+ */
+EAPI void *
+ecore_idler_del(Ecore_Idler *idler)
+{
+   if (!ECORE_MAGIC_CHECK(idler, ECORE_MAGIC_IDLER))
+     {
+        ECORE_MAGIC_FAIL(idler, ECORE_MAGIC_IDLER,
+                         "ecore_idler_del");
+        return NULL;
+     }
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(idler->delete_me, NULL);
+   idler->delete_me = 1;
+   idlers_delete_me = 1;
+   return idler->data;
+}
+
+void
+_ecore_idler_shutdown(void)
+{
+   Ecore_Idler *ie;
+   while ((ie = idlers))
+     {
+        idlers = (Ecore_Idler *) eina_inlist_remove(EINA_INLIST_GET(idlers), EINA_INLIST_GET(idlers));
+        ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE);
+        free(ie);
+     }
+   idlers_delete_me = 0;
+   idler_current = NULL;
+}
+
+int
+_ecore_idler_call(void)
+{
+   if (!idler_current)
+     {
+        /* regular main loop, start from head */
+        idler_current = idlers;
+     }
+   else
+     {
+        /* recursive main loop, continue from where we were */
+        idler_current = (Ecore_Idler *)EINA_INLIST_GET(idler_current)->next;
+     }
+
+   while (idler_current)
+     {
+        Ecore_Idler *ie = (Ecore_Idler *)idler_current;
+        if (!ie->delete_me)
+          {
+             ie->references++;
+             if (!ie->func(ie->data))
+               {
+                  if (!ie->delete_me) ecore_idler_del(ie);
+               }
+             ie->references--;
+          }
+        if (idler_current) /* may have changed in recursive main loops */
+          idler_current = (Ecore_Idler *)EINA_INLIST_GET(idler_current)->next;
+     }
+   if (idlers_delete_me)
+     {
+        Ecore_Idler *l;
+        int deleted_idlers_in_use = 0;
+        for (l = idlers; l;)
+          {
+             Ecore_Idler *ie = l;
+             l = (Ecore_Idler *) EINA_INLIST_GET(l)->next;
+             if (ie->delete_me)
+               {
+                  if (ie->references)
+                    {
+                       deleted_idlers_in_use++;
+                       continue;
+                    }
+
+                  idlers = (Ecore_Idler *) eina_inlist_remove(EINA_INLIST_GET(idlers), EINA_INLIST_GET(ie));
+                  ECORE_MAGIC_SET(ie, ECORE_MAGIC_NONE);
+                  free(ie);
+               }
+          }
+        if (!deleted_idlers_in_use)
+          idlers_delete_me = 0;
+     }
+   if (idlers) return 1;
+   return 0;
+}
+
+int
+_ecore_idler_exist(void)
+{
+   if (idlers) return 1;
+   return 0;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_job.c b/tests/suite/ecore/src/lib/ecore_job.c
new file mode 100644 (file)
index 0000000..cd519f7
--- /dev/null
@@ -0,0 +1,106 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+static Eina_Bool _ecore_job_event_handler(void *data, int type, void *ev);
+static void _ecore_job_event_free(void *data, void *ev);
+
+static int ecore_event_job_type = 0;
+static Ecore_Event_Handler* _ecore_job_handler = NULL;
+
+struct _Ecore_Job
+{
+   ECORE_MAGIC;
+   Ecore_Event  *event;
+   Ecore_Cb func;
+   void         *data;
+};
+
+void
+_ecore_job_init(void)
+{
+   ecore_event_job_type = ecore_event_type_new();
+   _ecore_job_handler = ecore_event_handler_add(ecore_event_job_type, _ecore_job_event_handler, NULL);
+}
+
+void
+_ecore_job_shutdown(void)
+{
+   ecore_event_handler_del(_ecore_job_handler);
+   _ecore_job_handler = NULL;
+}
+
+/**
+ * Add a job to the event queue.
+ * @param   func The function to call when the job gets handled.
+ * @param   data Data pointer to be passed to the job function when the job is
+ *               handled.
+ * @return  The handle of the job.  @c NULL is returned if the job could not be
+ *          added to the queue.
+ * @ingroup Ecore_Job_Group
+ * @note    Once the job has been executed, the job handle is invalid.
+ */
+EAPI Ecore_Job *
+ecore_job_add(Ecore_Cb func, const void *data)
+{
+   Ecore_Job *job;
+   
+   if (!func) return NULL;
+
+   job = calloc(1, sizeof(Ecore_Job));
+   if (!job) return NULL;
+   ECORE_MAGIC_SET(job, ECORE_MAGIC_JOB);
+   job->event = ecore_event_add(ecore_event_job_type, job, _ecore_job_event_free, NULL);
+   if (!job->event)
+     {
+        free(job);
+        return NULL;
+     }
+   job->func = func;
+   job->data = (void *)data;
+   return job;
+}
+
+/**
+ * Delete a queued job that has not yet been executed.
+ * @param   job  Handle of the job to delete.
+ * @return  The data pointer that was to be passed to the job.
+ * @ingroup Ecore_Job_Group
+ */
+EAPI void *
+ecore_job_del(Ecore_Job *job)
+{
+   void *data;
+   
+   if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_JOB))
+     {
+        ECORE_MAGIC_FAIL(job, ECORE_MAGIC_JOB,
+                         "ecore_job_del");
+        return NULL;
+     }
+   data = job->data;
+   ECORE_MAGIC_SET(job, ECORE_MAGIC_NONE);
+   ecore_event_del(job->event);
+   return data;
+}
+
+static Eina_Bool
+_ecore_job_event_handler(void *data __UNUSED__, int type __UNUSED__, void *ev)
+{
+   Ecore_Job *job;
+   
+   job = ev;
+   job->func(job->data);
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+_ecore_job_event_free(void *data __UNUSED__, void *ev)
+{
+   free(ev);
+}
diff --git a/tests/suite/ecore/src/lib/ecore_main.c b/tests/suite/ecore/src/lib/ecore_main.c
new file mode 100644 (file)
index 0000000..de507da
--- /dev/null
@@ -0,0 +1,1453 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <winsock2.h>
+# undef WIN32_LEAN_AND_MEAN
+# ifndef USER_TIMER_MINIMUM
+#  define USER_TIMER_MINIMUM 0x0a
+# endif
+#endif
+
+#ifdef __SUNPRO_C
+# include <ieeefp.h>
+# include <string.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifndef _MSC_VER
+#include <sys/time.h>
+# include <unistd.h>
+#else
+# include <float.h>
+#endif
+
+#define FIX_HZ 1
+
+#ifdef FIX_HZ
+# ifndef _MSC_VER
+#  include <sys/param.h>
+# endif
+# ifndef HZ
+#  define HZ 100
+# endif
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#ifdef HAVE_SYS_EPOLL_H
+# define HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
+
+#ifdef USE_G_MAIN_LOOP
+#include <glib.h>
+#endif
+
+struct _Ecore_Fd_Handler
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   int                      fd;
+   Ecore_Fd_Handler_Flags   flags;
+   Ecore_Fd_Cb              func;
+   void                    *data;
+   Ecore_Fd_Cb              buf_func;
+   void                    *buf_data;
+   Ecore_Fd_Prep_Cb         prep_func;
+   void                    *prep_data;
+   int                      references;
+   Eina_Bool                read_active : 1;
+   Eina_Bool                write_active : 1;
+   Eina_Bool                error_active : 1;
+   Eina_Bool                delete_me : 1;
+};
+
+#ifdef _WIN32
+struct _Ecore_Win32_Handler
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   HANDLE         h;
+   Ecore_Fd_Win32_Cb func;
+   void          *data;
+   int            references;
+   Eina_Bool      delete_me : 1;
+};
+#endif
+
+
+static int  _ecore_main_select(double timeout);
+static void _ecore_main_prepare_handlers(void);
+static void _ecore_main_fd_handlers_cleanup(void);
+#ifndef _WIN32
+static void _ecore_main_fd_handlers_bads_rem(void);
+#endif
+static void _ecore_main_fd_handlers_call(void);
+static int  _ecore_main_fd_handlers_buf_call(void);
+#ifndef USE_G_MAIN_LOOP
+static void _ecore_main_loop_iterate_internal(int once_only);
+#endif
+
+#ifdef _WIN32
+static int _ecore_main_win32_select(int nfds, fd_set *readfds, fd_set *writefds,
+                                    fd_set *exceptfds, struct timeval *timeout);
+static void _ecore_main_win32_handlers_cleanup(void);
+#endif
+
+static int               in_main_loop = 0;
+static int               do_quit = 0;
+static Ecore_Fd_Handler *fd_handlers = NULL;
+static Ecore_Fd_Handler *fd_handler_current = NULL;
+static int               fd_handlers_delete_me = 0;
+#ifdef _WIN32
+static Ecore_Win32_Handler *win32_handlers = NULL;
+static Ecore_Win32_Handler *win32_handler_current = NULL;
+static int                  win32_handlers_delete_me = 0;
+#endif
+
+#ifdef _WIN32
+static Ecore_Select_Function main_loop_select = _ecore_main_win32_select;
+#else
+static Ecore_Select_Function main_loop_select = select;
+#endif
+
+static double            t1 = 0.0;
+static double            t2 = 0.0;
+
+#ifdef HAVE_EPOLL
+static int epoll_fd = -1;
+#endif
+
+#ifdef USE_G_MAIN_LOOP
+static GSource *ecore_epoll_source;
+static GPollFD ecore_epoll_fd;
+static guint ecore_epoll_id;
+static GMainLoop* ecore_main_loop;
+static gboolean ecore_idling;
+static gboolean ecore_fds_ready;
+#endif
+
+#ifdef HAVE_EPOLL
+static inline int _ecore_poll_events_from_fdh(Ecore_Fd_Handler *fdh)
+{
+   int events = 0;
+   if (fdh->flags & ECORE_FD_READ)  events |= EPOLLIN;
+   if (fdh->flags & ECORE_FD_WRITE) events |= EPOLLOUT;
+   if (fdh->flags & ECORE_FD_ERROR) events |= EPOLLERR;
+   return events;
+}
+#else
+static inline int _ecore_poll_events_from_fdh(Ecore_Fd_Handler *fdh __UNUSED__)
+{
+   return 0;
+}
+#endif
+
+#ifdef HAVE_EPOLL
+static inline int _ecore_main_fdh_epoll_add(Ecore_Fd_Handler *fdh)
+{
+   int r = 0;
+   struct epoll_event ev;
+
+   memset(&ev, 0, sizeof (ev));
+   ev.events = _ecore_poll_events_from_fdh(fdh);
+   ev.data.ptr = fdh;
+   INF("adding poll on %d %08x", fdh->fd, ev.events);
+   r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fdh->fd, &ev);
+   return r;
+}
+#else
+static inline int _ecore_main_fdh_epoll_add(Ecore_Fd_Handler *fdh __UNUSED__)
+{
+   return 0;
+}
+#endif
+
+#ifdef HAVE_EPOLL
+static inline void _ecore_main_fdh_epoll_del(Ecore_Fd_Handler *fdh)
+{
+   struct epoll_event ev;
+
+   memset(&ev, 0, sizeof (ev));
+   INF("removing poll on %d", fdh->fd);
+   /* could get an EBADF if somebody closed the FD before removing it */
+   if ((epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fdh->fd, &ev) < 0) &&
+       (errno != EBADF))
+     {
+        ERR("Failed to delete epoll fd %d! (errno=%d)", fdh->fd, errno);
+     }
+}
+#else
+static inline void _ecore_main_fdh_epoll_del(Ecore_Fd_Handler *fdh __UNUSED__)
+{
+}
+#endif
+
+#ifdef HAVE_EPOLL
+static inline int _ecore_main_fdh_epoll_modify(Ecore_Fd_Handler *fdh)
+{
+   int r = 0;
+   struct epoll_event ev;
+
+   memset(&ev, 0, sizeof (ev));
+   ev.events = _ecore_poll_events_from_fdh(fdh);
+   ev.data.ptr = fdh;
+   INF("modifing epoll on %d to %08x", fdh->fd, ev.events);
+   r = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fdh->fd, &ev);
+   return r;
+}
+#else
+static inline int _ecore_main_fdh_epoll_modify(Ecore_Fd_Handler *fdh __UNUSED__)
+{
+   return 0;
+}
+#endif
+
+#ifdef HAVE_EPOLL
+static inline int _ecore_main_fdh_epoll_mark_active(void)
+{
+   struct epoll_event ev[32];
+   int i, ret;
+
+   memset(&ev, 0, sizeof (ev));
+   ret = epoll_wait(epoll_fd, ev, sizeof(ev) / sizeof(struct epoll_event), 0);
+   if (ret < 0)
+     {
+        if (errno == EINTR) return -1;
+        ERR("epoll_wait failed %d", errno);
+        return -1;
+     }
+
+   for (i = 0; i < ret; i++)
+     {
+        Ecore_Fd_Handler *fdh;
+        
+        fdh = ev[i].data.ptr;
+        if (!ECORE_MAGIC_CHECK(fdh, ECORE_MAGIC_FD_HANDLER))
+          {
+             ECORE_MAGIC_FAIL(fdh, ECORE_MAGIC_FD_HANDLER,
+                              "_ecore_main_fdh_epoll_mark_active");
+             continue;
+          }
+        if (fdh->delete_me)
+          {
+             ERR("deleted fd in epoll");
+             continue;
+          }
+        if (ev->events & EPOLLIN)
+          fdh->read_active = 1;
+        if (ev->events & EPOLLOUT)
+          fdh->write_active = 1;
+        if (ev->events & EPOLLERR)
+          fdh->error_active = 1;
+     }
+
+   return ret;
+}
+#endif
+
+#ifdef USE_G_MAIN_LOOP
+
+/* like we are about to enter main_loop_select in  _ecore_main_select */
+static gboolean
+_ecore_main_gsource_prepare(GSource *source, gint *next_time)
+{
+   double t = _ecore_timer_next_get();
+   gboolean running;
+
+   INF("enter, next timeout in %.1f", t);
+   in_main_loop++;
+
+   if (!ecore_idling)
+     {
+         while (_ecore_timer_call(_ecore_time_loop_time));
+          _ecore_timer_cleanup();
+
+         /* when idling, busy loop checking the fds only */
+         if (!ecore_idling) _ecore_idle_enterer_call();
+     }
+
+   /* don't check fds if somebody quit */
+   running = g_main_loop_is_running(ecore_main_loop);
+   if (running)
+     {
+        /* only set idling state in dispatch */
+        if (ecore_idling && !_ecore_idler_exist())
+          {
+             if (_ecore_timers_exists())
+               {
+                  double t = _ecore_timer_next_get();
+                  *next_time = (t / 1000.0);
+               }
+             else
+               *next_time = -1;
+          }
+        else
+          *next_time = 0;
+
+        _ecore_main_prepare_handlers();
+     }
+
+   in_main_loop--;
+   INF("leave, timeout = %d", *next_time);
+
+   /* ready if we're not running (about to quit) */
+   return !running;
+}
+
+static gboolean
+_ecore_main_gsource_check(GSource *source)
+{
+   INF("enter");
+   in_main_loop++;
+
+   ecore_fds_ready = (_ecore_main_fdh_epoll_mark_active() > 0);
+   _ecore_main_fd_handlers_cleanup();
+
+   _ecore_time_loop_time = ecore_time_get();
+   _ecore_timer_enable_new();
+
+   in_main_loop--;
+   INF("leave");
+
+   return TRUE; /* always dispatch */
+}
+
+/* like we just came out of main_loop_select in  _ecore_main_select */
+static gboolean
+_ecore_main_gsource_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
+{
+   gboolean events_ready, timers_ready, idlers_ready, signals_ready;
+   double next_time = _ecore_timer_next_get();
+
+   events_ready = _ecore_event_exist();
+   timers_ready = _ecore_timers_exists() && (0.0 <= next_time);
+   idlers_ready = _ecore_idler_exist();
+   signals_ready = (_ecore_signal_count_get() > 0);
+
+   in_main_loop++;
+   INF("enter idling=%d fds=%d events=%d signals=%d timers=%d (next=%.2f) idlers=%d",
+       ecore_idling, ecore_fds_ready, events_ready, signals_ready,
+       _ecore_timers_exists(), next_time, idlers_ready);
+
+   if (ecore_idling && events_ready)
+     {
+        INF("calling idle exiters");
+        _ecore_idle_exiter_call();
+        ecore_idling = 0;
+     }
+   else if (!ecore_idling && !events_ready)
+     {
+        INF("start idling");
+        ecore_idling = 1;
+     }
+
+   if (ecore_idling)
+     {
+        INF("calling idler");
+        _ecore_idler_call();
+
+        events_ready = _ecore_event_exist();
+        timers_ready = _ecore_timers_exists() && (0.0 <= next_time);
+        idlers_ready = _ecore_idler_exist();
+
+        if ((ecore_fds_ready || events_ready || timers_ready || idlers_ready || signals_ready))
+          {
+             INF("calling idle exiters");
+             _ecore_idle_exiter_call();
+             ecore_idling = 0;
+          }
+     }
+
+   /* process events */
+   if (!ecore_idling)
+     {
+        INF("work");
+        _ecore_main_fd_handlers_call();
+        _ecore_main_fd_handlers_buf_call();
+        while (_ecore_signal_count_get()) _ecore_signal_call();
+        _ecore_event_call();
+        _ecore_main_fd_handlers_cleanup();
+     }
+
+   in_main_loop--;
+
+   INF("leave");
+
+   return TRUE; /* what should be returned here? */
+}
+
+static void
+_ecore_main_gsource_finalize(GSource *source)
+{
+   INF("finalize");
+}
+
+static GSourceFuncs ecore_gsource_funcs =
+{
+   .prepare  = _ecore_main_gsource_prepare,
+   .check    = _ecore_main_gsource_check,
+   .dispatch = _ecore_main_gsource_dispatch,
+   .finalize = _ecore_main_gsource_finalize,
+};
+
+#endif
+
+void
+_ecore_main_loop_init(void)
+{
+   INF("enter");
+#ifdef HAVE_EPOLL
+   epoll_fd = epoll_create(1);
+   if (epoll_fd < 0)
+      CRIT("Failed to create epoll fd!");
+#endif
+
+#ifdef USE_G_MAIN_LOOP
+   ecore_epoll_source = g_source_new(&ecore_gsource_funcs, sizeof (GSource));
+   if (!ecore_epoll_source)
+      CRIT("Failed to create glib source for epoll!");
+   else
+     {
+        ecore_epoll_fd.fd = epoll_fd;
+        ecore_epoll_fd.events = G_IO_IN;
+        ecore_epoll_fd.revents = 0;
+        g_source_add_poll(ecore_epoll_source, &ecore_epoll_fd);
+        ecore_epoll_id = g_source_attach(ecore_epoll_source, NULL);
+        if (ecore_epoll_id <= 0)
+           CRIT("Failed to attach glib source to default context");
+     }
+#endif
+   INF("leave");
+}
+
+void
+_ecore_main_loop_shutdown(void)
+{
+#ifdef USE_G_MAIN_LOOP
+   if (ecore_epoll_source)
+     {
+        g_source_destroy(ecore_epoll_source);
+        ecore_epoll_source = NULL;
+     }
+#endif
+
+#ifdef HAVE_EPOLL
+   if (epoll_fd >= 0)
+     {
+        close(epoll_fd);
+        epoll_fd = -1;
+     }
+#endif
+}
+
+
+/**
+ * @defgroup Ecore_Main_Loop_Group Main Loop Functions
+ *
+ * These functions control the Ecore event handling loop.  This loop is
+ * designed to work on embedded systems all the way to large and
+ * powerful mutli-cpu workstations.
+ *
+ * It serialises all system signals and events into a single event
+ * queue, that can be easily processed without needing to worry about
+ * concurrency.  A properly written, event-driven program using this
+ * kind of programming does not need threads.  It makes the program very
+ * robust and easy to follow.
+ *
+ * Here is an example of simple program and its basic event loop flow:
+ * @image html prog_flow.png
+ *
+ * For examples of setting up and using a main loop, see
+ * @ref event_handler_example.c and @ref timer_example.c.
+ */
+
+/**
+ * Runs a single iteration of the main loop to process everything on the
+ * queue.
+ * @ingroup Ecore_Main_Loop_Group
+ */
+EAPI void
+ecore_main_loop_iterate(void)
+{
+#ifndef USE_G_MAIN_LOOP
+   _ecore_main_loop_iterate_internal(1);
+#else
+    g_main_context_iteration(NULL, 1);
+#endif
+}
+
+/**
+ * Runs the application main loop.
+ *
+ * This function will not return until @ref ecore_main_loop_quit is called.
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ */
+EAPI void
+ecore_main_loop_begin(void)
+{
+#ifndef USE_G_MAIN_LOOP
+   in_main_loop++;
+   while (do_quit == 0) _ecore_main_loop_iterate_internal(0);
+   do_quit = 0;
+   in_main_loop--;
+#else
+   ecore_main_loop = g_main_loop_new(NULL, FALSE);
+   g_main_loop_run(ecore_main_loop);
+#endif
+}
+
+/**
+ * Quits the main loop once all the events currently on the queue have
+ * been processed.
+ * @ingroup Ecore_Main_Loop_Group
+ */
+EAPI void
+ecore_main_loop_quit(void)
+{
+#ifndef USE_G_MAIN_LOOP
+   do_quit = 1;
+#else
+   INF("enter");
+   g_main_loop_quit(ecore_main_loop);
+   INF("leave");
+#endif
+}
+
+/**
+ * Sets the function to use when monitoring multiple file descriptors,
+ * and waiting until one of more of the file descriptors before ready
+ * for some class of I/O operation.
+ *
+ * This function will be used instead of the system call select and
+ * could possible be used to integrate the Ecore event loop with an
+ * external event loop.
+ *
+ * @warning you don't know how to use, don't even try to use it.
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ */
+EAPI void
+ecore_main_loop_select_func_set(Ecore_Select_Function func)
+{
+   main_loop_select = func;
+}
+
+/**
+ * Gets the select function set by ecore_select_func_set(),
+ * or the native select function if none was set.
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ */
+EAPI void *
+ecore_main_loop_select_func_get(void)
+{
+   return main_loop_select;
+}
+
+/**
+ * @defgroup Ecore_FD_Handler_Group File Event Handling Functions
+ *
+ * Functions that deal with file descriptor handlers.
+ */
+
+/**
+ * Adds a callback for activity on the given file descriptor.
+ *
+ * @p func will be called during the execution of @ref ecore_main_loop_begin
+ * when the file descriptor is available for reading, or writing, or both.
+ *
+ * Normally the return value from the @p func is "zero means this handler is
+ * finished and can be deleted" as is usual for handler callbacks.  However,
+ * if the @p buf_func is supplied, then the return value from the @p func is
+ * "non zero means the handler should be called again in a tight loop".
+ *
+ * @p buf_func is called during event loop handling to check if data that has
+ * been read from the file descriptor is in a buffer and is available to
+ * read.  Some systems (notably xlib) handle their own buffering, and would
+ * otherwise not work with select().  These systems should use a @p buf_func.
+ * This is a most annoying hack, only ecore_x uses it, so refer to that for
+ * an example.  NOTE - @p func should probably return "one" always if
+ * @p buf_func is used, to avoid confusion with the other return value
+ * semantics.
+ *
+ * @param   fd       The file descriptor to watch.
+ * @param   flags    To watch it for read (@c ECORE_FD_READ) and/or
+ *                   (@c ECORE_FD_WRITE) write ability.  @c ECORE_FD_ERROR
+ *
+ * @param   func     The callback function.
+ * @param   data     The data to pass to the callback.
+ * @param   buf_func The function to call to check if any data has been
+ *                   buffered and already read from the fd.  Can be @c NULL.
+ * @param   buf_data The data to pass to the @p buf_func function.
+ * @return  A fd handler handle if successful.  @c NULL otherwise.
+ * @ingroup Ecore_FD_Handler_Group
+ */
+EAPI Ecore_Fd_Handler *
+ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data,
+                          Ecore_Fd_Cb buf_func, const void *buf_data)
+{
+   Ecore_Fd_Handler *fdh;
+
+   if ((fd < 0) || (flags == 0) || (!func)) return NULL;
+
+   fdh = calloc(1, sizeof(Ecore_Fd_Handler));
+   if (!fdh) return NULL;
+   ECORE_MAGIC_SET(fdh, ECORE_MAGIC_FD_HANDLER);
+   fdh->fd = fd;
+   fdh->flags = flags;
+   if (0 > _ecore_main_fdh_epoll_add(fdh))
+     {
+        ERR("Failed to add epoll fd %d (errno = %d)!", fd, errno);
+        free(fdh);
+        return NULL;
+     }
+   fdh->read_active = 0;
+   fdh->write_active = 0;
+   fdh->error_active = 0;
+   fdh->delete_me = 0;
+   fdh->func = func;
+   fdh->data = (void *)data;
+   fdh->buf_func = buf_func;
+   fdh->buf_data = (void *)buf_data;
+   fd_handlers = (Ecore_Fd_Handler *)
+     eina_inlist_append(EINA_INLIST_GET(fd_handlers),
+                        EINA_INLIST_GET(fdh));
+   return fdh;
+}
+
+#ifdef _WIN32
+EAPI Ecore_Win32_Handler *
+ecore_main_win32_handler_add(void *h, Ecore_Fd_Win32_Cb func, const void *data)
+{
+   Ecore_Win32_Handler *wh;
+
+   if (!h || !func) return NULL;
+
+   wh = calloc(1, sizeof(Ecore_Win32_Handler));
+   if (!wh) return NULL;
+   ECORE_MAGIC_SET(wh, ECORE_MAGIC_WIN32_HANDLER);
+   wh->h = (HANDLE)h;
+   wh->delete_me = 0;
+   wh->func = func;
+   wh->data = (void *)data;
+   win32_handlers = (Ecore_Win32_Handler *)
+     eina_inlist_append(EINA_INLIST_GET(win32_handlers),
+                        EINA_INLIST_GET(wh));
+   return wh;
+}
+#else
+EAPI Ecore_Win32_Handler *
+ecore_main_win32_handler_add(void *h __UNUSED__, Ecore_Fd_Win32_Cb func __UNUSED__,
+                             const void *data __UNUSED__)
+{
+   return NULL;
+}
+#endif
+
+/**
+ * Deletes the given FD handler.
+ * @param   fd_handler The given FD handler.
+ * @return  The data pointer set using @ref ecore_main_fd_handler_add,
+ *          for @p fd_handler on success.  @c NULL otherwise.
+ * @ingroup Ecore_FD_Handler_Group
+ *
+ * Beware that if the fd is already closed, ecore may complain if it uses
+ * epoll internally, and that in some rare cases this may be able to cause
+ * crashes and instability. Remember to delete your fd handlers before the
+ * fd's they listen to are closed.
+ */
+EAPI void *
+ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler)
+{
+   if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+                         "ecore_main_fd_handler_del");
+        return NULL;
+     }
+   fd_handler->delete_me = 1;
+   fd_handlers_delete_me = 1;
+   _ecore_main_fdh_epoll_del(fd_handler);
+   return fd_handler->data;
+}
+
+#ifdef _WIN32
+EAPI void *
+ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler)
+{
+   if (!ECORE_MAGIC_CHECK(win32_handler, ECORE_MAGIC_WIN32_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(win32_handler, ECORE_MAGIC_WIN32_HANDLER,
+                         "ecore_main_win32_handler_del");
+        return NULL;
+     }
+   win32_handler->delete_me = 1;
+   win32_handlers_delete_me = 1;
+   return win32_handler->data;
+}
+#else
+EAPI void *
+ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler __UNUSED__)
+{
+   return NULL;
+}
+#endif
+
+EAPI void
+ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data)
+{
+   if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+                         "ecore_main_fd_handler_prepare_callback_set");
+        return;
+     }
+   fd_handler->prep_func = func;
+   fd_handler->prep_data = (void *) data;
+}
+
+/**
+ * Retrieves the file descriptor that the given handler is handling.
+ * @param   fd_handler The given FD handler.
+ * @return  The file descriptor the handler is watching.
+ * @ingroup Ecore_FD_Handler_Group
+ */
+EAPI int
+ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler)
+{
+   if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+                         "ecore_main_fd_handler_fd_get");
+        return -1;
+     }
+   return fd_handler->fd;
+}
+
+/**
+ * Return if read, write or error, or a combination thereof, is active on the
+ * file descriptor of the given FD handler.
+ * @param   fd_handler The given FD handler.
+ * @param   flags      The flags, @c ECORE_FD_READ, @c ECORE_FD_WRITE or
+ *                     @c ECORE_FD_ERROR to query.
+ * @return  #EINA_TRUE if any of the given flags are active. #EINA_FALSE otherwise.
+ * @ingroup Ecore_FD_Handler_Group
+ */
+EAPI Eina_Bool
+ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags)
+{
+   int ret = EINA_FALSE;
+
+   if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+                         "ecore_main_fd_handler_active_get");
+        return EINA_FALSE;
+     }
+   if ((flags & ECORE_FD_READ) && (fd_handler->read_active)) ret = EINA_TRUE;
+   if ((flags & ECORE_FD_WRITE) && (fd_handler->write_active)) ret = EINA_TRUE;
+   if ((flags & ECORE_FD_ERROR) && (fd_handler->error_active)) ret = EINA_TRUE;
+   return ret;
+}
+
+/**
+ * Set what active streams the given FD handler should be monitoring.
+ * @param   fd_handler The given FD handler.
+ * @param   flags      The flags to be watching.
+ * @ingroup Ecore_FD_Handler_Group
+ */
+EAPI void
+ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags)
+{
+   if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+     {
+        ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+                         "ecore_main_fd_handler_active_set");
+        return;
+     }
+   fd_handler->flags = flags;
+   if (0 > _ecore_main_fdh_epoll_modify(fd_handler))
+     {
+       ERR("Failed to mod epoll fd %d!", fd_handler->fd);
+     }
+}
+
+void
+_ecore_main_shutdown(void)
+{
+   if (in_main_loop)
+     {
+       ERR("\n"
+           "*** ECORE WARINING: Calling ecore_shutdown() while still in the main loop.\n"
+           "***                 Program may crash or behave strangely now.");
+        return;
+     }
+   while (fd_handlers)
+     {
+        Ecore_Fd_Handler *fdh;
+
+        fdh = fd_handlers;
+        fd_handlers = (Ecore_Fd_Handler *) eina_inlist_remove(EINA_INLIST_GET(fd_handlers),
+                                                              EINA_INLIST_GET(fdh));
+        ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE);
+        free(fdh);
+     }
+   fd_handlers_delete_me = 0;
+   fd_handler_current = NULL;
+
+#ifdef _WIN32
+   while (win32_handlers)
+     {
+        Ecore_Win32_Handler *wh;
+
+        wh = win32_handlers;
+        win32_handlers = (Ecore_Win32_Handler *) eina_inlist_remove(EINA_INLIST_GET(win32_handlers),
+                                                                    EINA_INLIST_GET(wh));
+        ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE);
+        free(wh);
+     }
+   win32_handlers_delete_me = 0;
+   win32_handler_current = NULL;
+#endif
+}
+
+static void
+_ecore_main_prepare_handlers(void)
+{
+   Ecore_Fd_Handler *fdh;
+
+   /* call the prepare callback for all handlers */
+   EINA_INLIST_FOREACH(fd_handlers, fdh)
+     {
+        if (!fdh->delete_me && fdh->prep_func)
+          {
+             fdh->references++;
+             fdh->prep_func (fdh->prep_data, fdh);
+             fdh->references--;
+          }
+     }
+}
+
+static int
+_ecore_main_select(double timeout)
+{
+   struct timeval tv, *t;
+   fd_set         rfds, wfds, exfds;
+   int            max_fd;
+   int            ret;
+
+   t = NULL;
+   if ((!finite(timeout)) || (timeout == 0.0)) /* finite() tests for NaN, too big, too small, and infinity.  */
+     {
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+        t = &tv;
+     }
+   else if (timeout > 0.0)
+     {
+        int sec, usec;
+
+#ifdef FIX_HZ
+        timeout += (0.5 / HZ);
+        sec = (int)timeout;
+        usec = (int)((timeout - (double)sec) * 1000000);
+#else
+        sec = (int)timeout;
+        usec = (int)((timeout - (double)sec) * 1000000);
+#endif
+        tv.tv_sec = sec;
+        tv.tv_usec = usec;
+        t = &tv;
+     }
+   max_fd = 0;
+   FD_ZERO(&rfds);
+   FD_ZERO(&wfds);
+   FD_ZERO(&exfds);
+
+   /* call the prepare callback for all handlers */
+   _ecore_main_prepare_handlers();
+#ifndef HAVE_EPOLL
+   Ecore_Fd_Handler *fdh;
+
+   EINA_INLIST_FOREACH(fd_handlers, fdh)
+     {
+        if (!fdh->delete_me)
+          {
+             if (fdh->flags & ECORE_FD_READ)
+               {
+                  FD_SET(fdh->fd, &rfds);
+                  if (fdh->fd > max_fd) max_fd = fdh->fd;
+               }
+             if (fdh->flags & ECORE_FD_WRITE)
+               {
+                  FD_SET(fdh->fd, &wfds);
+                  if (fdh->fd > max_fd) max_fd = fdh->fd;
+               }
+             if (fdh->flags & ECORE_FD_ERROR)
+               {
+                  FD_SET(fdh->fd, &exfds);
+                  if (fdh->fd > max_fd) max_fd = fdh->fd;
+               }
+          }
+     }
+#else /* HAVE_EPOLL */
+   /* polling on the epoll fd will wake when an fd in the epoll set is active */
+   FD_SET(epoll_fd, &rfds);
+   max_fd = epoll_fd;
+#endif /* HAVE_EPOLL */
+
+   if (_ecore_signal_count_get()) return -1;
+
+   ret = main_loop_select(max_fd + 1, &rfds, &wfds, &exfds, t);
+
+   _ecore_time_loop_time = ecore_time_get();
+   if (ret < 0)
+     {
+#ifndef _WIN32
+        if (errno == EINTR) return -1;
+        else if (errno == EBADF) _ecore_main_fd_handlers_bads_rem();
+#endif
+     }
+   if (ret > 0)
+     {
+#ifdef HAVE_EPOLL
+        _ecore_main_fdh_epoll_mark_active();
+#else /* HAVE_EPOLL */
+        Ecore_Fd_Handler *fdh;
+
+        EINA_INLIST_FOREACH(fd_handlers, fdh)
+          {
+             if (!fdh->delete_me)
+               {
+                  if (FD_ISSET(fdh->fd, &rfds))
+                     fdh->read_active = 1;
+                  if (FD_ISSET(fdh->fd, &wfds))
+                     fdh->write_active = 1;
+                  if (FD_ISSET(fdh->fd, &exfds))
+                     fdh->error_active = 1;
+               }
+          }
+#endif /* HAVE_EPOLL */
+        _ecore_main_fd_handlers_cleanup();
+#ifdef _WIN32
+        _ecore_main_win32_handlers_cleanup();
+#endif
+        return 1;
+     }
+   return 0;
+}
+
+#ifndef _WIN32
+static void
+_ecore_main_fd_handlers_bads_rem(void)
+{
+   Ecore_Fd_Handler *fdh;
+   Eina_Inlist *l;
+   int found = 0;
+
+   ERR("Removing bad fds");
+   for (l = EINA_INLIST_GET(fd_handlers); l; )
+     {
+        fdh = (Ecore_Fd_Handler *) l;
+        l = l->next;
+        errno = 0;
+
+        if ((fcntl(fdh->fd, F_GETFD) < 0) && (errno == EBADF))
+          {
+             ERR("Found bad fd at index %d", fdh->fd);
+             if (fdh->flags & ECORE_FD_ERROR)
+               {
+                  ERR("Fd set for error! calling user");
+                  fdh->references++;
+                  if (!fdh->func(fdh->data, fdh))
+                    {
+                       ERR("Fd function err returned 0, remove it");
+                       fdh->delete_me = 1;
+                       fd_handlers_delete_me = 1;
+                       found++;
+                    }
+                  fdh->references--;
+               }
+             else
+               {
+                  ERR("Problematic fd found at %d! setting it for delete", fdh->fd);
+                  fdh->delete_me = 1;
+                  fd_handlers_delete_me = 1;
+                  found++;
+               }
+          }
+    }
+   if (found == 0)
+     {
+#ifdef HAVE_GLIB
+        ERR("No bad fd found. Maybe a foreign fd from glib?");
+#else        
+        ERR("No bad fd found. EEEK!");
+#endif        
+     }
+   _ecore_main_fd_handlers_cleanup();
+}
+#endif
+
+static void
+_ecore_main_fd_handlers_cleanup(void)
+{
+   Ecore_Fd_Handler *fdh;
+   Eina_Inlist *l;
+   int deleted_in_use = 0;
+
+   if (!fd_handlers_delete_me) return;
+   for (l = EINA_INLIST_GET(fd_handlers); l; )
+     {
+        fdh = (Ecore_Fd_Handler *) l;
+
+        l = l->next;
+        if (fdh->delete_me)
+          {
+             if (fdh->references)
+               {
+                  deleted_in_use++;
+                  continue;
+               }
+             
+             fd_handlers = (Ecore_Fd_Handler *)
+                eina_inlist_remove(EINA_INLIST_GET(fd_handlers),
+                                   EINA_INLIST_GET(fdh));
+             ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE);
+             free(fdh);
+          }
+     }
+   if (!deleted_in_use) fd_handlers_delete_me = 0;
+}
+
+#ifdef _WIN32
+static void
+_ecore_main_win32_handlers_cleanup(void)
+{
+   Ecore_Win32_Handler *wh;
+   Eina_Inlist *l;
+   int deleted_in_use = 0;
+
+   if (!win32_handlers_delete_me) return;
+   for (l = EINA_INLIST_GET(win32_handlers); l; )
+     {
+        wh = (Ecore_Win32_Handler *)l;
+
+        l = l->next;
+        if (wh->delete_me)
+          {
+             if (wh->references)
+               {
+                  deleted_in_use++;
+                  continue;
+               }
+             
+             win32_handlers = (Ecore_Win32_Handler *)
+                eina_inlist_remove(EINA_INLIST_GET(win32_handlers),
+                                   EINA_INLIST_GET(wh));
+             ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE);
+             free(wh);
+          }
+     }
+   if (!deleted_in_use) win32_handlers_delete_me = 0;
+}
+#endif
+
+static void
+_ecore_main_fd_handlers_call(void)
+{
+   if (!fd_handler_current)
+     {
+        /* regular main loop, start from head */
+        fd_handler_current = fd_handlers;
+     }
+   else
+     {
+        /* recursive main loop, continue from where we were */
+        fd_handler_current = (Ecore_Fd_Handler *)EINA_INLIST_GET(fd_handler_current)->next;
+     }
+
+   while (fd_handler_current)
+     {
+        Ecore_Fd_Handler *fdh = fd_handler_current;
+
+        if (!fdh->delete_me)
+          {
+             if ((fdh->read_active) ||
+                 (fdh->write_active) ||
+                 (fdh->error_active))
+               {
+                  fdh->references++;
+                  if (!fdh->func(fdh->data, fdh))
+                    {
+                       fdh->delete_me = 1;
+                       fd_handlers_delete_me = 1;
+                    }
+                  fdh->references--;
+
+                  fdh->read_active = 0;
+                  fdh->write_active = 0;
+                  fdh->error_active = 0;
+               }
+          }
+
+        if (fd_handler_current) /* may have changed in recursive main loops */
+          fd_handler_current = (Ecore_Fd_Handler *)EINA_INLIST_GET(fd_handler_current)->next;
+     }
+}
+
+static int
+_ecore_main_fd_handlers_buf_call(void)
+{
+   Ecore_Fd_Handler *fdh;
+   int ret;
+
+   ret = 0;
+   EINA_INLIST_FOREACH(fd_handlers, fdh)
+     {
+        if (!fdh->delete_me)
+          {
+             if (fdh->buf_func)
+               {
+                  fdh->references++;
+                  if (fdh->buf_func(fdh->buf_data, fdh))
+                    {
+                       ret |= fdh->func(fdh->data, fdh);
+                       fdh->read_active = 1;
+                    }
+                  fdh->references--;
+               }
+          }
+     }
+   return ret;
+}
+
+#ifndef USE_G_MAIN_LOOP
+static void
+_ecore_main_loop_iterate_internal(int once_only)
+{
+   double next_time = -1.0;
+   int    have_event = 0;
+   int    have_signal;
+
+   in_main_loop++;
+   /* expire any timers */
+   while (_ecore_timer_call(_ecore_time_loop_time));
+   _ecore_timer_cleanup();
+
+   /* process signals into events .... */
+   while (_ecore_signal_count_get()) _ecore_signal_call();
+   if (_ecore_event_exist())
+     {
+        _ecore_idle_enterer_call();
+        have_event = 1;
+        _ecore_main_select(0.0);
+        _ecore_time_loop_time = ecore_time_get();
+        _ecore_timer_enable_new();
+        goto process_events;
+     }
+   /* call idle enterers ... */
+   if (!once_only) _ecore_idle_enterer_call();
+   else
+     {
+        have_event = have_signal = 0;
+
+        if (_ecore_main_select(0.0) > 0) have_event = 1;
+        if (_ecore_signal_count_get() > 0) have_signal = 1;
+        if (have_signal || have_event)
+          {
+             _ecore_time_loop_time = ecore_time_get();
+             _ecore_timer_enable_new();
+             goto process_events;
+          }
+     }
+
+   /* if these calls caused any buffered events to appear - deal with them */
+   _ecore_main_fd_handlers_buf_call();
+
+   /* if there are any - jump to processing them */
+   if (_ecore_event_exist())
+     {
+        have_event = 1;
+        _ecore_main_select(0.0);
+        _ecore_time_loop_time = ecore_time_get();
+        _ecore_timer_enable_new();
+        goto process_events;
+     }
+   if (once_only)
+     {
+        _ecore_idle_enterer_call();
+        in_main_loop--;
+        _ecore_time_loop_time = ecore_time_get();
+        _ecore_timer_enable_new();
+        return;
+     }
+
+   if (_ecore_fps_debug)
+     {
+        t2 = ecore_time_get();
+        if ((t1 > 0.0) && (t2 > 0.0))
+          _ecore_fps_debug_runtime_add(t2 - t1);
+     }
+   start_loop:
+   /* any timers re-added as a result of these are allowed to go */
+   _ecore_timer_enable_new();
+   if (do_quit)
+     {
+        _ecore_time_loop_time = ecore_time_get();
+        in_main_loop--;
+        _ecore_timer_enable_new();
+        return;
+     }
+   if (!_ecore_event_exist())
+     {
+        /* init flags */
+        have_event = have_signal = 0;
+        next_time = _ecore_timer_next_get();
+        /* no timers */
+        if (next_time < 0)
+          {
+             /* no idlers */
+             if (!_ecore_idler_exist())
+               {
+                  if (_ecore_main_select(-1.0) > 0) have_event = 1;
+               }
+             /* idlers */
+             else
+               {
+                  for (;;)
+                    {
+                       if (!_ecore_idler_call()) goto start_loop;
+                       if (_ecore_event_exist()) break;
+                       if (_ecore_main_select(0.0) > 0) have_event = 1;
+                       if (_ecore_signal_count_get() > 0) have_signal = 1;
+                       if (have_event || have_signal) break;
+                       if (_ecore_timers_exists()) goto start_loop;
+                       if (do_quit) break;
+                    }
+               }
+          }
+        /* timers */
+        else
+          {
+             /* no idlers */
+             if (!_ecore_idler_exist())
+               {
+                  if (_ecore_main_select(next_time) > 0) have_event = 1;
+               }
+             /* idlers */
+             else
+               {
+                  for (;;)
+                    {
+                       if (!_ecore_idler_call()) goto start_loop;
+                       if (_ecore_event_exist()) break;
+                       if (_ecore_main_select(0.0) > 0) have_event = 1;
+                       if (_ecore_signal_count_get() > 0) have_signal = 1;
+                       if (have_event || have_signal) break;
+                       next_time = _ecore_timer_next_get();
+                       if (next_time <= 0) break;
+                       if (do_quit) break;
+                    }
+               }
+          }
+        _ecore_time_loop_time = ecore_time_get();
+     }
+   if (_ecore_fps_debug) t1 = ecore_time_get();
+   /* we came out of our "wait state" so idle has exited */
+   if (!once_only) _ecore_idle_exiter_call();
+   /* call the fd handler per fd that became alive... */
+   /* this should read or write any data to the monitored fd and then */
+   /* post events onto the ecore event pipe if necessary */
+   process_events:
+   _ecore_main_fd_handlers_call();
+   _ecore_main_fd_handlers_buf_call();
+   /* process signals into events .... */
+   while (_ecore_signal_count_get()) _ecore_signal_call();
+   /* handle events ... */
+   _ecore_event_call();
+   _ecore_main_fd_handlers_cleanup();
+
+   if (once_only) _ecore_idle_enterer_call();
+   in_main_loop--;
+}
+#endif
+
+#ifdef _WIN32
+static int
+_ecore_main_win32_select(int nfds __UNUSED__, fd_set *readfds, fd_set *writefds,
+                         fd_set *exceptfds, struct timeval *tv)
+{
+   HANDLE       objects[MAXIMUM_WAIT_OBJECTS];
+   int          sockets[MAXIMUM_WAIT_OBJECTS];
+   Ecore_Fd_Handler *fdh;
+   Ecore_Win32_Handler *wh;
+   unsigned int objects_nbr = 0;
+   unsigned int handles_nbr = 0;
+   unsigned int events_nbr = 0;
+   DWORD        result;
+   DWORD        timeout;
+   MSG          msg;
+   unsigned int i;
+   int          res;
+
+   /* Create an event object per socket */
+   EINA_INLIST_FOREACH(fd_handlers, fdh)
+     {
+        WSAEVENT event;
+        long network_event;
+
+        network_event = 0;
+        if (FD_ISSET(fdh->fd, readfds))
+          network_event |= FD_READ;
+        if (FD_ISSET(fdh->fd, writefds))
+          network_event |= FD_WRITE;
+        if (FD_ISSET(fdh->fd, exceptfds))
+          network_event |= FD_OOB;
+
+        if (network_event)
+          {
+             event = WSACreateEvent();
+             WSAEventSelect(fdh->fd, event, network_event);
+             objects[objects_nbr] = event;
+             sockets[events_nbr] = fdh->fd;
+             events_nbr++;
+             objects_nbr++;
+          }
+     }
+
+   /* store the HANDLEs in the objects to wait for */
+   EINA_INLIST_FOREACH(win32_handlers, wh)
+     {
+        objects[objects_nbr] = wh->h;
+        handles_nbr++;
+        objects_nbr++;
+     }
+
+   /* Empty the queue before waiting */
+   while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+     {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+     }
+
+   /* Wait for any message sent or posted to this queue */
+   /* or for one of the passed handles be set to signaled. */
+   if (!tv)
+     timeout = INFINITE;
+   else
+     timeout = (DWORD)((tv->tv_sec * 1000.0) + (tv->tv_usec / 1000.0));
+
+   if (timeout == 0) return 0;
+
+   result = MsgWaitForMultipleObjects(objects_nbr, (const HANDLE *)objects, EINA_FALSE,
+                                      timeout, QS_ALLINPUT);
+
+   FD_ZERO(readfds);
+   FD_ZERO(writefds);
+   FD_ZERO(exceptfds);
+
+   /* The result tells us the type of event we have. */
+   if (result == WAIT_FAILED)
+     {
+        char *msg;
+
+        msg = evil_last_error_get();
+        ERR(" * %s\n", msg);
+        free(msg);
+        res = -1;
+     }
+   else if (result == WAIT_TIMEOUT)
+     {
+        /* ERR("time out\n"); */
+        res = 0;
+     }
+   else if (result == (WAIT_OBJECT_0 + objects_nbr))
+     {
+        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+          {
+             TranslateMessage(&msg);
+             DispatchMessage(&msg);
+          }
+
+        res = 0;
+     }
+   else if ((result >= 0) && (result < WAIT_OBJECT_0 + events_nbr))
+     {
+        WSANETWORKEVENTS network_event;
+
+        WSAEnumNetworkEvents(sockets[result], objects[result], &network_event);
+
+        if (network_event.lNetworkEvents & FD_READ)
+          FD_SET(sockets[result], readfds);
+        if (network_event.lNetworkEvents & FD_WRITE)
+          FD_SET(sockets[result], writefds);
+        if (network_event.lNetworkEvents & FD_OOB)
+          FD_SET(sockets[result], exceptfds);
+
+        res = 1;
+     }
+   else if ((result >= (WAIT_OBJECT_0 + events_nbr)) && 
+            (result < (WAIT_OBJECT_0 + objects_nbr)))
+     {
+        if (!win32_handler_current)
+          {
+             /* regular main loop, start from head */
+             win32_handler_current = win32_handlers;
+          }
+        else
+          {
+             /* recursive main loop, continue from where we were */
+             win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next;
+          }
+
+        while (win32_handler_current)
+          {
+             wh = win32_handler_current;
+
+             if (objects[result - WAIT_OBJECT_0] == wh->h)
+               {
+                  if (!wh->delete_me)
+                    {
+                       wh->references++;
+                       if (!wh->func(wh->data, wh))
+                         {
+                            wh->delete_me = 1;
+                            win32_handlers_delete_me = 1;
+                         }
+                       wh->references--;
+                    }
+               }
+             if (win32_handler_current) /* may have changed in recursive main loops */
+               win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next;
+          }
+        res = 1;
+     }
+   else
+     {
+        ERR("unknown result...\n");
+        res = -1;
+     }
+
+   /* Remove event objects again */
+   for (i = 0; i < events_nbr; i++) WSACloseEvent(objects[i]);
+
+   return res;
+}
+#endif
diff --git a/tests/suite/ecore/src/lib/ecore_pipe.c b/tests/suite/ecore/src/lib/ecore_pipe.c
new file mode 100644 (file)
index 0000000..8a8aca5
--- /dev/null
@@ -0,0 +1,595 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+/* How of then we should retry to write to the pipe */
+#define ECORE_PIPE_WRITE_RETRY 6
+
+/*
+ * On Windows, pipe() is implemented with sockets.
+ * Contrary to Linux, Windows uses different functions
+ * for sockets and fd's: write() is for fd's and send
+ * is for sockets. So I need to put some win32 code
+ * here. I can't think of a solution where the win32
+ * code is in Evil and not here.
+ */
+
+#ifdef _WIN32
+
+# include <winsock2.h>
+
+# define pipe_write(fd, buffer, size) send((fd), (char *)(buffer), size, 0)
+# define pipe_read(fd, buffer, size)  recv((fd), (char *)(buffer), size, 0)
+# define pipe_close(fd)               closesocket(fd)
+# define PIPE_FD_INVALID              INVALID_SOCKET
+# define PIPE_FD_ERROR                SOCKET_ERROR
+
+#else
+
+# include <unistd.h>
+# include <fcntl.h>
+
+# define pipe_write(fd, buffer, size) write((fd), buffer, size)
+# define pipe_read(fd, buffer, size)  read((fd), buffer, size)
+# define pipe_close(fd)               close(fd)
+# define PIPE_FD_INVALID              -1
+# define PIPE_FD_ERROR                -1
+
+#endif /* ! _WIN32 */
+
+struct _Ecore_Pipe
+{
+   ECORE_MAGIC;
+   int               fd_read;
+   int               fd_write;
+   Ecore_Fd_Handler *fd_handler;
+   const void       *data;
+   Ecore_Pipe_Cb     handler;
+   unsigned int      len;
+   size_t            already_read;
+   void             *passed_data;
+};
+
+
+static Eina_Bool _ecore_pipe_read(void *data, Ecore_Fd_Handler *fd_handler);
+
+/**
+ * @defgroup Ecore_Pipe_Group Pipe wrapper
+ *
+ * These functions wrap the pipe / write / read functions to
+ * easily integrate a loop that is in its own thread to the ecore
+ * main loop.
+ *
+ * The ecore_pipe_add() function creates file descriptors (sockets on
+ * Windows) and attach an handle to the ecore main loop. That handle is
+ * called when data is read in the pipe. To write data in the pipe,
+ * just call ecore_pipe_write(). When you are done, just call
+ * ecore_pipe_del().
+ *
+ * Here is an example that uses the pipe wrapper with a Gstreamer
+ * pipeline. For each decoded frame in the Gstreamer thread, a handle
+ * is called in the ecore thread.
+ *
+ * @code#include <gst/gst.h>
+ * #include <Ecore.h>
+ *
+ * static int nbr = 0;
+ *
+ * static GstElement *_buid_pipeline (gchar *filename, Ecore_Pipe *pipe);
+ *
+ * static void new_decoded_pad_cb (GstElement *demuxer,
+ *                                 GstPad     *new_pad,
+ *                                 gpointer    user_data);
+ *
+ * static void handler(void *data, void *buf, unsigned int len)
+ * {
+ *   GstBuffer  *buffer = *((GstBuffer **)buf);
+ *
+ *   printf ("handler : %p\n", buffer);
+ *   printf ("frame  : %d %p %lld %p\n", nbr++, data, (long long)GST_BUFFER_DURATION(buffer), buffer);
+ *   gst_buffer_unref (buffer);
+ * }
+ *
+ *
+ * static void handoff (GstElement* object,
+ *                      GstBuffer* arg0,
+ *                      GstPad* arg1,
+ *                      gpointer user_data)
+ * {
+ *   Ecore_Pipe *pipe;
+ *
+ *   pipe = (Ecore_Pipe *)user_data;
+ *   printf ("handoff : %p\n", arg0);
+ *   gst_buffer_ref (arg0);
+ *   ecore_pipe_write(pipe, &arg0, sizeof(arg0));
+ * }
+ *
+ * int
+ * main (int argc, char *argv[])
+ * {
+ *   GstElement *pipeline;
+ *   char *filename;
+ *   Ecore_Pipe *pipe;
+ *
+ *   gst_init (&argc, &argv);
+ *
+ *   if (!ecore_init ())
+ *     {
+ *       gst_deinit ();
+ *       return 0;
+ *     }
+ *
+ *   pipe = ecore_pipe_add (handler);
+ *   if (!pipe)
+ *     {
+ *       ecore_shutdown ();
+ *       gst_deinit ();
+ *       return 0;
+ *     }
+ *
+ *   if (argc < 2) {
+ *     g_print ("usage: %s file.avi\n", argv[0]);
+ *     ecore_pipe_del (pipe);
+ *     ecore_shutdown ();
+ *     gst_deinit ();
+ *     return 0;
+ *   }
+ *   filename = argv[1];
+ *
+ *   pipeline = _buid_pipeline (filename, pipe);
+ *   if (!pipeline) {
+ *     g_print ("Error during the pipeline building\n");
+ *     ecore_pipe_del (pipe);
+ *     ecore_shutdown ();
+ *     gst_deinit ();
+ *     return -1;
+ *   }
+ *
+ *   gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ *
+ *   ecore_main_loop_begin();
+ *
+ *   ecore_pipe_del (pipe);
+ *   ecore_shutdown ();
+ *   gst_deinit ();
+ *
+ *   return 0;
+ * }
+ *
+ * static void
+ * new_decoded_pad_cb (GstElement *demuxer,
+ *                     GstPad     *new_pad,
+ *                     gpointer    user_data)
+ * {
+ *   GstElement *decoder;
+ *   GstPad *pad;
+ *   GstCaps *caps;
+ *   gchar *str;
+ *
+ *   caps = gst_pad_get_caps (new_pad);
+ *   str = gst_caps_to_string (caps);
+ *
+ *   if (g_str_has_prefix (str, "video/")) {
+ *     decoder = GST_ELEMENT (user_data);
+ *
+ *     pad = gst_element_get_pad (decoder, "sink");
+ *     if (GST_PAD_LINK_FAILED (gst_pad_link (new_pad, pad))) {
+ *       g_warning ("Failed to link %s:%s to %s:%s", GST_DEBUG_PAD_NAME (new_pad),
+ *                  GST_DEBUG_PAD_NAME (pad));
+ *     }
+ *   }
+ *   g_free (str);
+ *   gst_caps_unref (caps);
+ * }
+ *
+ * static GstElement *
+ * _buid_pipeline (gchar *filename, Ecore_Pipe *pipe)
+ * {
+ *   GstElement          *pipeline;
+ *   GstElement          *filesrc;
+ *   GstElement          *demuxer;
+ *   GstElement          *decoder;
+ *   GstElement          *sink;
+  GstStateChangeReturn res;
+ *
+ *   pipeline = gst_pipeline_new ("pipeline");
+ *   if (!pipeline)
+ *     return NULL;
+ *
+ *   filesrc = gst_element_factory_make ("filesrc", "filesrc");
+ *   if (!filesrc) {
+ *     printf ("no filesrc");
+ *     goto failure;
+ *   }
+ *   g_object_set (G_OBJECT (filesrc), "location", filename, NULL);
+ *
+ *   demuxer = gst_element_factory_make ("oggdemux", "demuxer");
+ *   if (!demuxer) {
+ *     printf ("no demux");
+ *     goto failure;
+ *   }
+ *
+ *   decoder = gst_element_factory_make ("theoradec", "decoder");
+ *   if (!decoder) {
+ *     printf ("no dec");
+ *     goto failure;
+ *   }
+ *
+ *   g_signal_connect (demuxer, "pad-added",
+ *                     G_CALLBACK (new_decoded_pad_cb), decoder);
+ *
+ *   sink = gst_element_factory_make ("fakesink", "sink");
+ *   if (!sink) {
+ *     printf ("no sink");
+ *     goto failure;
+ *   }
+ *   g_object_set (G_OBJECT (sink), "sync", EINA_TRUE, NULL);
+ *   g_object_set (G_OBJECT (sink), "signal-handoffs", EINA_TRUE, NULL);
+ *   g_signal_connect (sink, "handoff",
+ *                     G_CALLBACK (handoff), pipe);
+ *
+ *   gst_bin_add_many (GST_BIN (pipeline),
+ *                     filesrc, demuxer, decoder, sink, NULL);
+ *
+ *   if (!gst_element_link (filesrc, demuxer))
+ *     goto failure;
+ *   if (!gst_element_link (decoder, sink))
+ *     goto failure;
+ *
+ *   res = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ *   if (res == GST_STATE_CHANGE_FAILURE)
+ *     goto failure;
+ *
+ *   res = gst_element_get_state( pipeline, NULL, NULL, GST_CLOCK_TIME_NONE );
+ *   if (res != GST_STATE_CHANGE_SUCCESS)
+ *     goto failure;
+ *
+ *   return pipeline;
+ *
+ *  failure:
+ *   gst_object_unref (GST_OBJECT (pipeline));
+ *   return NULL;
+ * }
+ * @endcode
+ */
+
+
+/**
+ * Create two file descriptors (sockets on Windows). Add
+ * a callback that will be called when the file descriptor that
+ * is listened receives data. An event is also put in the event
+ * queue when data is received.
+ *
+ * @param handler The handler called when data is received.
+ * @param data    Data to pass to @p handler when it is called.
+ * @return        A newly created Ecore_Pipe object if successful.
+ *                @c NULL otherwise.
+ * @ingroup Ecore_Pipe_Group
+ */
+EAPI Ecore_Pipe *
+ecore_pipe_add(Ecore_Pipe_Cb handler, const void *data)
+{
+   Ecore_Pipe *p;
+   int         fds[2];
+
+   if (!handler) return NULL;
+
+   p = (Ecore_Pipe *)calloc(1, sizeof(Ecore_Pipe));
+   if (!p) return NULL;
+
+   if (pipe(fds))
+     {
+        free(p);
+        return NULL;
+     }
+
+   ECORE_MAGIC_SET(p, ECORE_MAGIC_PIPE);
+   p->fd_read = fds[0];
+   p->fd_write = fds[1];
+   p->handler = handler;
+   p->data = data;
+
+   fcntl(p->fd_read, F_SETFL, O_NONBLOCK);
+   p->fd_handler = ecore_main_fd_handler_add(p->fd_read,
+                                          ECORE_FD_READ,
+                                          _ecore_pipe_read,
+                                          p,
+                                          NULL, NULL);
+   return p;
+}
+
+/**
+ * Free an Ecore_Pipe object created with ecore_pipe_add().
+ *
+ * @param p The Ecore_Pipe object to be freed.
+ * @return The pointer to the private data
+ * @ingroup Ecore_Pipe_Group
+ */
+EAPI void *
+ecore_pipe_del(Ecore_Pipe *p)
+{
+   void *data;
+
+   if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+     {
+        ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_del");
+        return NULL;
+     }
+   if (p->fd_handler) ecore_main_fd_handler_del(p->fd_handler);
+   if (p->fd_read != PIPE_FD_INVALID) pipe_close(p->fd_read);
+   if (p->fd_write != PIPE_FD_INVALID) pipe_close(p->fd_write);
+   data = (void *)p->data;
+   free(p);
+   return data;
+}
+
+/**
+ * Close the read end of an Ecore_Pipe object created with ecore_pipe_add().
+ *
+ * @param p The Ecore_Pipe object.
+ * @ingroup Ecore_Pipe_Group
+ */
+EAPI void
+ecore_pipe_read_close(Ecore_Pipe *p)
+{
+   if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+     {
+        ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_read_close");
+        return;
+     }
+   ecore_main_fd_handler_del(p->fd_handler);
+   p->fd_handler = NULL;
+   pipe_close(p->fd_read);
+   p->fd_read = PIPE_FD_INVALID;
+}
+
+/**
+ * Close the write end of an Ecore_Pipe object created with ecore_pipe_add().
+ *
+ * @param p The Ecore_Pipe object.
+ * @ingroup Ecore_Pipe_Group
+ */
+EAPI void
+ecore_pipe_write_close(Ecore_Pipe *p)
+{
+   if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+     {
+        ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_write_close");
+        return;
+     }
+   pipe_close(p->fd_write);
+   p->fd_write = PIPE_FD_INVALID;
+}
+
+/**
+ * Write on the file descriptor the data passed as parameter.
+ *
+ * @param p      The Ecore_Pipe object.
+ * @param buffer The data to write into the pipe.
+ * @param nbytes The size of the @p buffer in bytes
+ * @return       Returns EINA_TRUE on a successful write, EINA_FALSE on an error
+ * @ingroup Ecore_Pipe_Group
+ */
+EAPI Eina_Bool
+ecore_pipe_write(Ecore_Pipe *p, const void *buffer, unsigned int nbytes)
+{
+   ssize_t ret;
+   size_t  already_written = 0;
+   int     retry = ECORE_PIPE_WRITE_RETRY;
+
+   if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+     {
+        ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_write");
+        return EINA_FALSE;
+     }
+
+   if (p->fd_write == PIPE_FD_INVALID) return EINA_FALSE;
+
+   /* First write the len into the pipe */
+   do
+     {
+        ret = pipe_write(p->fd_write, &nbytes, sizeof(nbytes));
+        if (ret == sizeof(nbytes))
+          {
+             retry = ECORE_PIPE_WRITE_RETRY;
+             break;
+          }
+        else if (ret > 0)
+          {
+             /* XXX What should we do here? */
+             ERR("The length of the data was not written complete"
+                 " to the pipe");
+             return EINA_FALSE;
+          }
+        else if (ret == PIPE_FD_ERROR && errno == EPIPE)
+          {
+             pipe_close(p->fd_write);
+             p->fd_write = PIPE_FD_INVALID;
+             return EINA_FALSE;
+          }
+        else if (ret == PIPE_FD_ERROR && errno == EINTR)
+          /* try it again */
+          ;
+        else
+          {
+             ERR("An unhandled error (ret: %zd errno: %d)"
+                 "occurred while writing to the pipe the length",
+                 ret, errno);
+          }
+     }
+   while (retry--);
+
+   if (retry != ECORE_PIPE_WRITE_RETRY) return EINA_FALSE;
+
+   /* and now pass the data to the pipe */
+   do
+     {
+        ret = pipe_write(p->fd_write,
+                         ((unsigned char *)buffer) + already_written,
+                         nbytes - already_written);
+        
+        if (ret == (ssize_t)(nbytes - already_written))
+          return EINA_TRUE;
+        else if (ret >= 0)
+          {
+             already_written -= ret;
+             continue;
+          }
+        else if (ret == PIPE_FD_ERROR && errno == EPIPE)
+          {
+             pipe_close(p->fd_write);
+             p->fd_write = PIPE_FD_INVALID;
+             return EINA_FALSE;
+          }
+        else if (ret == PIPE_FD_ERROR && errno == EINTR)
+          /* try it again */
+          ;
+        else
+          {
+             ERR("An unhandled error (ret: %zd errno: %d)"
+                 "occurred while writing to the pipe the length",
+                 ret, errno);
+          }
+     }
+   while (retry--);
+
+   return EINA_FALSE;
+}
+
+/* Private function */
+
+static Eina_Bool
+_ecore_pipe_read(void *data, Ecore_Fd_Handler *fd_handler __UNUSED__)
+{
+   Ecore_Pipe  *p;
+   double       start_time;
+
+   p = (Ecore_Pipe *)data;
+   start_time = ecore_time_get();
+
+   do
+     {
+        ssize_t       ret;
+
+        /* if we already have read some data we don't need to read the len
+         * but to finish the already started job
+         */
+        if (p->len == 0)
+          {
+             /* read the len of the passed data */
+             ret = pipe_read(p->fd_read, &p->len, sizeof(p->len));
+
+             /* catch the non error case first */
+             if (ret == sizeof(p->len))
+               ;
+             else if (ret > 0)
+               {
+                  /* XXX What should we do here? */
+                  ERR("Only read %zd bytes from the pipe, although"
+                      " we need to read %zd bytes.", ret, sizeof(p->len));
+               }
+             else if (ret == 0)
+               {
+                  p->handler((void *)p->data, NULL, 0);
+                  pipe_close(p->fd_read);
+                  p->fd_read = PIPE_FD_INVALID;
+                  p->fd_handler = NULL;
+                  return ECORE_CALLBACK_CANCEL;
+               }
+#ifndef _WIN32
+             else if ((ret == PIPE_FD_ERROR) && ((errno == EINTR) || (errno == EAGAIN)))
+               return ECORE_CALLBACK_RENEW;
+             else
+               {
+                  ERR("An unhandled error (ret: %zd errno: %d)"
+                      "occurred while reading from the pipe the length",
+                      ret, errno);
+                  return ECORE_CALLBACK_RENEW;
+               }
+#else
+             else /* ret == PIPE_FD_ERROR is the only other case on Windows */
+               {
+                  if (WSAGetLastError() != WSAEWOULDBLOCK)
+                    {
+                       p->handler((void *)p->data, NULL, 0);
+                       pipe_close(p->fd_read);
+                       p->fd_read = PIPE_FD_INVALID;
+                       p->fd_handler = NULL;
+                       return ECORE_CALLBACK_CANCEL;
+                    }
+               }
+#endif
+          }
+
+        if (!p->passed_data)
+          p->passed_data = malloc(p->len);
+
+        /* and read the passed data */
+        ret = pipe_read(p->fd_read,
+                        ((unsigned char *)p->passed_data) + p->already_read,
+                        p->len - p->already_read);
+        
+        /* catch the non error case first */
+        if (ret == (ssize_t)(p->len - p->already_read))
+          {
+             p->handler((void *)p->data, p->passed_data, p->len);
+             free(p->passed_data);
+             /* reset all values to 0 */
+             p->passed_data = NULL;
+             p->already_read = 0;
+             p->len = 0;
+          }
+        else if (ret >= 0)
+          {
+             p->already_read += ret;
+             return ECORE_CALLBACK_RENEW;
+          }
+        else if (ret == 0)
+          {
+             p->handler((void *)p->data, NULL, 0);
+             pipe_close(p->fd_read);
+             p->fd_read = PIPE_FD_INVALID;
+             p->fd_handler = NULL;
+             return ECORE_CALLBACK_CANCEL;
+          }
+#ifndef _WIN32
+        else if (ret == PIPE_FD_ERROR && (errno == EINTR || errno == EAGAIN))
+          return ECORE_CALLBACK_RENEW;
+        else
+          {
+             ERR("An unhandled error (ret: %zd errno: %d)"
+                 "occurred while reading from the pipe the data",
+                 ret, errno);
+             return ECORE_CALLBACK_RENEW;
+          }
+#else
+        else /* ret == PIPE_FD_ERROR is the only other case on Windows */
+          {
+             if (WSAGetLastError() != WSAEWOULDBLOCK)
+               {
+                  p->handler((void *)p->data, NULL, 0);
+                  pipe_close(p->fd_read);
+                  p->fd_read = PIPE_FD_INVALID;
+                  p->fd_handler = NULL;
+                  return ECORE_CALLBACK_CANCEL;
+               }
+             else
+               break;
+          }
+#endif
+     }
+   while (ecore_time_get() - start_time < ecore_animator_frametime_get());
+   
+   return ECORE_CALLBACK_RENEW;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_poll.c b/tests/suite/ecore/src/lib/ecore_poll.c
new file mode 100644 (file)
index 0000000..d5bc6ba
--- /dev/null
@@ -0,0 +1,442 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+struct _Ecore_Poller
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   int           ibit;
+   unsigned char delete_me : 1;
+   Ecore_Task_Cb func;
+   void          *data;
+};
+
+
+static Ecore_Timer    *timer = NULL;
+static int             min_interval = -1;
+static int             interval_incr = 0;
+static int             at_tick = 0;
+static int             just_added_poller = 0;
+static int             poller_delete_count = 0;
+static int             poller_walking = 0;
+static double          poll_interval = 0.125;
+static double          poll_cur_interval = 0.0;
+static double          last_tick = 0.0;
+static Ecore_Poller   *pollers[16] =
+{
+   NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+   NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
+};
+static unsigned short  poller_counters[16] =
+{
+   0,0,0,0,0,0,0,0,
+   0,0,0,0,0,0,0,0
+};
+
+static void _ecore_poller_next_tick_eval(void);
+static Eina_Bool _ecore_poller_cb_timer(void *data);
+
+static void
+_ecore_poller_next_tick_eval(void)
+{
+   int i;
+   double interval;
+
+   min_interval = -1;
+   for (i = 0; i < 15; i++)
+     {
+        if (pollers[i])
+          {
+             min_interval = i;
+             break;
+          }
+     }
+   if (min_interval < 0)
+     {
+        /* no pollers */
+        if (timer)
+          {
+             ecore_timer_del(timer);
+             timer = NULL;
+          }
+        return;
+     }
+   interval_incr = (1 << min_interval);
+   interval = interval_incr * poll_interval;
+   /* we are at the tick callback - so no need to do inter-tick adjustments
+    * so we can fasttrack this as t -= last_tick in theory is 0.0 (though
+    * in practice it will be a very very very small value. also the tick
+    * callback will adjust the timer interval at the end anyway */
+   if (at_tick)
+     {
+        if (!timer)
+          timer = ecore_timer_add(interval, _ecore_poller_cb_timer, NULL);
+     }
+   else
+     {
+        double t;
+
+        if (!timer)
+          timer = ecore_timer_add(interval, _ecore_poller_cb_timer, NULL);
+        else
+          {
+             t = ecore_time_get();
+             if (interval != poll_cur_interval)
+               {
+                  t -= last_tick; /* time since we last ticked */
+                  /* delete the timer and reset it to tick off in the new
+                   * time interval. at the tick this will be adjusted */
+                  ecore_timer_del(timer);
+                  timer = ecore_timer_add(interval - t,
+                                          _ecore_poller_cb_timer, NULL);
+               }
+          }
+     }
+   poll_cur_interval = interval;
+}
+
+static Eina_Bool
+_ecore_poller_cb_timer(void *data __UNUSED__)
+{
+   int i;
+   Ecore_Poller *poller, *l;
+   int changes = 0;
+
+   at_tick++;
+   last_tick = ecore_time_get();
+   /* we have 16 counters - each incriments every time the poller counter
+    * "ticks". it incriments by the minimum interval (which can be 1, 2, 4,
+    * 7, 16 etc. up to 32768) */
+   for (i = 0; i < 15; i++)
+     {
+        poller_counters[i] += interval_incr;
+        /* wrap back to 0 if we exceed out loop count for the counter */
+        if (poller_counters[i] >= (1 << i)) poller_counters[i] = 0;
+     }
+
+   just_added_poller = 0;
+   /* walk the pollers now */
+   poller_walking++;
+   for (i = 0; i < 15; i++)
+     {
+        /* if the counter is @ 0 - this means that counter "went off" this
+         * tick interval, so run all pollers hooked to that counter */
+        if (poller_counters[i] == 0)
+          {
+             EINA_INLIST_FOREACH(pollers[i], poller)
+               {
+                  if (!poller->delete_me)
+                    {
+                       if (!poller->func(poller->data))
+                         {
+                            if (!poller->delete_me)
+                              {
+                                 poller->delete_me = 1;
+                                 poller_delete_count++;
+                              }
+                         }
+                    }
+               }
+          }
+     }
+   poller_walking--;
+
+   /* handle deletes afterwards */
+   if (poller_delete_count > 0)
+     {
+        /* FIXME: walk all pollers and remove deleted ones */
+        for (i = 0; i < 15; i++)
+          {
+             for (l = pollers[i]; l;)
+               {
+                  poller = l;
+                  l = (Ecore_Poller *) EINA_INLIST_GET(l)->next;
+                  if (poller->delete_me)
+                    {
+                       pollers[i] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[i]), EINA_INLIST_GET(poller));
+                       free(poller);
+                       poller_delete_count--;
+                       changes++;
+                       if (poller_delete_count <= 0) break;
+                    }
+               }
+             if (poller_delete_count <= 0) break;
+          }
+     }
+   /* if we deleted or added any pollers, then we need to re-evaluate our
+    * minimum poll interval */
+   if ((changes > 0) || (just_added_poller > 0))
+     _ecore_poller_next_tick_eval();
+
+   just_added_poller = 0;
+   poller_delete_count = 0;
+
+   at_tick--;
+
+   /* if the timer was deleted then there is no point returning 1 - ambiguous
+    * if we do as it im plies "keep running me" but we have been deleted
+    * anyway */
+   if (!timer) return ECORE_CALLBACK_CANCEL;
+
+   /* adjust interval */
+   ecore_timer_interval_set(timer, poll_cur_interval);
+   return ECORE_CALLBACK_RENEW;
+}
+
+/**
+ * @defgroup Ecore_Poll_Group Ecore Poll Functions
+ *
+ * These functions are for the need to poll information, but provide a shared
+ * abstracted API to pool such polling to minimise wakeup and ensure all the
+ * polling happens in as few spots as possible areound a core poll interval.
+ * For now only 1 core poller type is supprted: ECORE_POLLER_CORE
+ */
+
+
+/**
+ * Sets the time between ticks (in seconds) for the given ticker clock.
+ * @param   type The ticker type to adjust
+ * @param   poll_time The time (in seconds) between ticks of the clock
+ * @ingroup Ecore_Poller_Group
+ *
+ * This will adjust the time between ticks of the given ticker type defined
+ * by @p type to the time period defined by @p poll_time.
+ */
+EAPI void
+ecore_poller_poll_interval_set(Ecore_Poller_Type type __UNUSED__, double poll_time)
+{
+   poll_interval = poll_time;
+   _ecore_poller_next_tick_eval();
+}
+
+/**
+ * Gets the time between ticks (in seconds) for the given ticker clock.
+ * @param   type The ticker type to query
+ * @return  The time in seconds between ticks of the ticker clock
+ * @ingroup Ecore_Poller_Group
+ *
+ * This will get the time between ticks of the specifider ticker clock.
+ */
+EAPI double
+ecore_poller_poll_interval_get(Ecore_Poller_Type type __UNUSED__)
+{
+   return poll_interval;
+}
+
+/**
+ * Creates a poller to call the given function at a particular tick interval.
+ * @param   type The ticker type to attach the poller to
+ * @param   interval The poll interval
+ * @param   func The given function.  If @p func returns 1, the poller is
+ *               rescheduled for the next tick interval.
+ * @param   data Data to pass to @p func when it is called.
+ * @return  A poller object on success.  @c NULL on failure.
+ * @ingroup Ecore_Poller_Group
+ *
+ * This function adds a poller callback that is to be called regularly
+ * along with all other poller callbacks so the pollers are synchronized with
+ * all other pollers running off the same poller type and at the same tick
+ * interval. This should be used for polling things when polling is desired
+ * or required, and you do not have specific requirements on the exact times
+ * to poll and want to avoid extra process wakeups for polling. This will
+ * save power as the CPU has more of a chance to go into a low power state
+ * the longer it is asleep for, so this should be used if you are at all
+ * power conscious.
+ *
+ * The @p type parameter defines the poller tick type (there is a virtual
+ * clock ticking all the time - though ecore avoids making it tick when
+ * there will not be any work to do at that tick point). There is only one
+ * ticker at the moment - that is ECORE_POLLER_CORE. This is here for future
+ * expansion if multiple clocks with different frequencies are really required.
+ * The default time between ticks for the ECORE_POLLER_CORE ticker is 0.125
+ * seconds.
+ *
+ * The @p interval is the number of ticker ticks that will pass by in between
+ * invocations of the @p func callback. This must be between 1 and 32768
+ * inclusive, and must be a power of 2 (i.e. 1, 2, 4, 8, 16, ... 16384, 32768).
+ * If it is 1, then the function will be called every tick. if it is 2, then it
+ * will be called every 2nd tick, if it is 8, then every 8th tick etc. Exactly
+ * which tick is undefined, as only the interval between calls can be defined.
+ * Ecore will endeavour to keep pollers synchronised and to call as many in
+ * 1 wakeup event as possible.
+ *
+ * This function adds a poller and returns its handle on success and NULL on
+ * failure. The function @p func will be called at tick intervals described
+ * above. The function will be passed the @p data pointer as its parameter.
+ *
+ * When the poller @p func is called, it must return a value of either
+ * 1 (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL). If it
+ * returns 1, it will be called again at the next tick, or if it returns
+ * 0 it will be deleted automatically making any references/handles for it
+ * invalid.
+ */
+EAPI Ecore_Poller *
+ecore_poller_add(Ecore_Poller_Type type __UNUSED__, int interval, Ecore_Task_Cb func, const void *data)
+{
+   Ecore_Poller *poller;
+   int ibit;
+
+   if (!func) return NULL;
+   if (interval < 1) interval = 1;
+
+   poller = calloc(1, sizeof(Ecore_Poller));
+   if (!poller) return NULL;
+   ECORE_MAGIC_SET(poller, ECORE_MAGIC_POLLER);
+   /* interval MUST be a power of 2, so enforce it */
+   if (interval < 1) interval = 1;
+   ibit = -1;
+   while (interval != 0)
+     {
+        ibit++;
+        interval >>= 1;
+     }
+   /* only allow up to 32768 - i.e. ibit == 15, so limit it */
+   if (ibit > 15) ibit = 15;
+
+   poller->ibit = ibit;
+   poller->func = func;
+   poller->data = (void *)data;
+   pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_prepend(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller));
+   if (poller_walking)
+     just_added_poller++;
+   else
+     _ecore_poller_next_tick_eval();
+   return poller;
+}
+
+/**
+ * Changes the polling interval rate of @p poller.
+ *
+ * @param poller The Ecore_Poller to change the interval of
+ * @param interval The tick interval to set; must be a power of 2 but <= 32768
+ * @return Returns true on success, false on failure
+ *
+ * This allows the changing of a poller's polling interval.  It is useful when you want to alter
+ * a poll rate without deleting and re-creating a poller.
+ * @ingroup Ecore_Poller_Group
+ */
+EAPI Eina_Bool
+ecore_poller_poller_interval_set(Ecore_Poller *poller, int interval)
+{
+   int ibit;
+
+   if (!ECORE_MAGIC_CHECK(poller, ECORE_MAGIC_POLLER))
+     {
+        ECORE_MAGIC_FAIL(poller, ECORE_MAGIC_POLLER,
+           "ecore_poller_poller_interval_set");
+        return EINA_FALSE;
+     }
+
+   /* interval MUST be a power of 2, so enforce it */
+   if (interval < 1) interval = 1;
+   ibit = -1;
+   while (interval != 0)
+     {
+       ibit++;
+       interval >>= 1;
+     }
+   /* only allow up to 32768 - i.e. ibit == 15, so limit it */
+   if (ibit > 15) ibit = 15;
+   /* if interval specified is the same as interval set, return true without wasting time */
+   if (poller->ibit == ibit)
+     return EINA_TRUE;
+   pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller));
+   poller->ibit = ibit;
+   pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_prepend(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller));
+   if (poller_walking)
+     just_added_poller++;
+   else
+     _ecore_poller_next_tick_eval();
+   return EINA_TRUE;
+}
+
+/**
+ * Gets the polling interval rate of @p poller.
+ *
+ * @param poller The Ecore_Poller to change the interval of
+ * @return Returns the interval, in ticks, that @p poller polls at
+ *
+ * This returns a poller's polling interval, or 0 on error.
+ * @ingroup Ecore_Poller_Group
+ */
+EAPI int
+ecore_poller_poller_interval_get(Ecore_Poller *poller)
+{
+   int ibit, interval = 1;
+
+   if (!ECORE_MAGIC_CHECK(poller, ECORE_MAGIC_POLLER))
+     {
+        ECORE_MAGIC_FAIL(poller, ECORE_MAGIC_POLLER,
+           "ecore_poller_poller_interval_get");
+        return 0;
+     }
+
+   ibit = poller->ibit;
+   while (ibit != 0)
+     {
+       ibit--;
+       interval <<= 1;
+     }
+   return interval;
+}
+
+/**
+ * Delete the specified poller from the timer list.
+ * @param   poller The poller to delete.
+ * @return  The data pointer set for the timer when @ref ecore_poller_add was
+ *          called.  @c NULL is returned if the function is unsuccessful.
+ * @ingroup Ecore_Poller_Group
+ *
+ * Note: @p poller must be a valid handle. If the poller function has already
+ * returned 0, the handle is no longer valid (and does not need to be delete).
+ */
+EAPI void *
+ecore_poller_del(Ecore_Poller *poller)
+{
+   void *data;
+
+   if (!ECORE_MAGIC_CHECK(poller, ECORE_MAGIC_POLLER))
+     {
+        ECORE_MAGIC_FAIL(poller, ECORE_MAGIC_POLLER,
+           "ecore_poller_del");
+        return NULL;
+     }
+   /* we are walking the poller list - a bad idea to remove from it while
+    * walking it, so just flag it as delete_me and come back to it after
+    * the loop has finished */
+   if (poller_walking > 0)
+     {
+        poller_delete_count++;
+        poller->delete_me = 1;
+        return poller->data;
+     }
+   /* not in loop so safe - delete immediately */
+   data = poller->data;
+   pollers[poller->ibit] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[poller->ibit]), EINA_INLIST_GET(poller));
+   free(poller);
+   _ecore_poller_next_tick_eval();
+   return data;
+}
+
+void
+_ecore_poller_shutdown(void)
+{
+   int i;
+   Ecore_Poller *poller;
+
+   for (i = 0; i < 15; i++)
+     {
+        while ((poller = pollers[i]))
+          {
+             pollers[i] = (Ecore_Poller *) eina_inlist_remove(EINA_INLIST_GET(pollers[i]), EINA_INLIST_GET(pollers[i]));
+             free(poller);
+          }
+     }
+}
diff --git a/tests/suite/ecore/src/lib/ecore_private.h b/tests/suite/ecore/src/lib/ecore_private.h
new file mode 100644 (file)
index 0000000..5f08f8a
--- /dev/null
@@ -0,0 +1,208 @@
+#ifndef _ECORE_PRIVATE_H
+#define _ECORE_PRIVATE_H
+
+extern int _ecore_log_dom ;
+#ifdef  _ECORE_DEFAULT_LOG_DOM
+# undef _ECORE_DEFAULT_LOG_DOM
+#endif
+#define _ECORE_DEFAULT_LOG_DOM _ecore_log_dom
+
+#ifdef ECORE_DEFAULT_LOG_COLOR
+# undef ECORE_DEFAULT_LOG_COLOR
+#endif
+#define ECORE_DEFAULT_LOG_COLOR EINA_COLOR_BLUE
+
+#ifdef ERR
+# undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__)
+
+#ifdef DBG
+# undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__)
+
+#ifdef INF
+# undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__)
+
+#ifdef WRN
+# undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__)
+
+#ifdef CRIT
+# undef CRIT
+#endif
+#define CRIT(...) EINA_LOG_DOM_CRIT(_ECORE_DEFAULT_LOG_DOM, __VA_ARGS__)
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+#ifndef MIN
+# define MIN(x, y) (((x) > (y)) ? (y) : (x))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef ABS
+# define ABS(x) ((x) < 0 ? -(x) : (x))
+#endif
+
+#ifndef CLAMP
+# define CLAMP(x, min, max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x)))
+#endif
+
+#define EVAS_FRAME_QUEUING 1 /* for test */
+
+#define READBUFSIZ 65536
+
+#define ECORE_MAGIC_NONE            0x1234fedc
+#define ECORE_MAGIC_EXE             0xf7e812f5
+#define ECORE_MAGIC_TIMER           0xf7d713f4
+#define ECORE_MAGIC_IDLER           0xf7c614f3
+#define ECORE_MAGIC_IDLE_ENTERER    0xf7b515f2
+#define ECORE_MAGIC_IDLE_EXITER     0xf7601afd
+#define ECORE_MAGIC_FD_HANDLER      0xf7a416f1
+#define ECORE_MAGIC_EVENT_HANDLER   0xf79317f0
+#define ECORE_MAGIC_EVENT_FILTER    0xf78218ff
+#define ECORE_MAGIC_EVENT           0xf77119fe
+#define ECORE_MAGIC_ANIMATOR        0xf7643ea5
+#define ECORE_MAGIC_POLLER          0xf7568127
+#define ECORE_MAGIC_PIPE            0xf7458226
+#define ECORE_MAGIC_WIN32_HANDLER   0xf7e8f1a3
+#define ECORE_MAGIC_JOB             0x76543210
+
+
+#define ECORE_MAGIC                 Ecore_Magic  __magic
+
+#define ECORE_MAGIC_SET(d, m)       (d)->__magic = (m)
+#define ECORE_MAGIC_CHECK(d, m)     ((d) && ((d)->__magic == (m)))
+#define ECORE_MAGIC_FAIL(d, m, fn)  _ecore_magic_fail((d), (d) ? (d)->__magic : 0, (m), (fn));
+
+/* undef the following, we want our version */
+#undef FREE
+#define FREE(ptr) free(ptr); ptr = NULL;
+
+#undef IF_FREE
+#define IF_FREE(ptr) if (ptr) free(ptr); ptr = NULL;
+
+#undef IF_FN_DEL
+#define IF_FN_DEL(_fn, ptr) if (ptr) { _fn(ptr); ptr = NULL; }
+
+EAPI void ecore_print_warning(const char *function, const char *sparam);
+
+/* convenience macros for checking pointer parameters for non-NULL */
+#undef CHECK_PARAM_POINTER_RETURN
+#define CHECK_PARAM_POINTER_RETURN(sparam, param, ret) \
+     if (!(param)) \
+         { \
+            ecore_print_warning(__FUNCTION__, sparam); \
+            return ret; \
+         }
+
+#undef CHECK_PARAM_POINTER
+#define CHECK_PARAM_POINTER(sparam, param) \
+     if (!(param)) \
+         { \
+            ecore_print_warning(__FUNCTION__, sparam); \
+            return; \
+         }
+
+typedef unsigned int              Ecore_Magic;
+
+EAPI void          _ecore_magic_fail(const void *d, Ecore_Magic m, Ecore_Magic req_m, const char *fname);
+
+void          _ecore_time_init(void);
+
+void          _ecore_timer_shutdown(void);
+void          _ecore_timer_cleanup(void);
+void          _ecore_timer_enable_new(void);
+double        _ecore_timer_next_get(void);
+int           _ecore_timers_exists(void);
+int           _ecore_timer_call(double when);
+
+void          _ecore_idler_shutdown(void);
+int           _ecore_idler_call(void);
+int           _ecore_idler_exist(void);
+
+void          _ecore_idle_enterer_shutdown(void);
+void          _ecore_idle_enterer_call(void);
+int           _ecore_idle_enterer_exist(void);
+
+void          _ecore_idle_exiter_shutdown(void);
+void          _ecore_idle_exiter_call(void);
+int           _ecore_idle_exiter_exist(void);
+
+void          _ecore_event_shutdown(void);
+int           _ecore_event_exist(void);
+Ecore_Event  *_ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data);
+void          _ecore_event_call(void);
+
+Ecore_Timer  *_ecore_exe_doomsday_clock_get(Ecore_Exe *exe);
+void          _ecore_exe_doomsday_clock_set(Ecore_Exe *exe, Ecore_Timer *dc);
+
+EAPI void    *_ecore_event_signal_user_new(void);
+void         *_ecore_event_signal_hup_new(void);
+void         *_ecore_event_signal_exit_new(void);
+void         *_ecore_event_signal_power_new(void);
+void         *_ecore_event_signal_realtime_new(void);
+
+void          _ecore_main_shutdown(void);
+
+#ifdef _WIN32
+static inline void _ecore_signal_shutdown(void) { }
+static inline void _ecore_signal_init(void) { }
+static inline int  _ecore_signal_count_get(void) { return 0; }
+static inline void _ecore_signal_call(void) { }
+#else
+void          _ecore_signal_shutdown(void);
+void          _ecore_signal_init(void);
+int           _ecore_signal_count_get(void);
+void          _ecore_signal_call(void);
+#endif
+
+void          _ecore_exe_init(void);
+void          _ecore_exe_shutdown(void);
+#ifndef _WIN32
+Ecore_Exe    *_ecore_exe_find(pid_t pid);
+void         *_ecore_exe_event_del_new(void);
+void          _ecore_exe_event_del_free(void *data, void *ev);
+#endif
+
+void          _ecore_animator_shutdown(void);
+
+void          _ecore_poller_shutdown(void);
+
+EAPI void         *_ecore_list2_append           (void *in_list, void *in_item);
+EAPI void         *_ecore_list2_prepend          (void *in_list, void *in_item);
+EAPI void         *_ecore_list2_append_relative  (void *in_list, void *in_item, void *in_relative);
+EAPI void         *_ecore_list2_prepend_relative (void *in_list, void *in_item, void *in_relative);
+EAPI void         *_ecore_list2_remove           (void *in_list, void *in_item);
+EAPI void         *_ecore_list2_find             (void *in_list, void *in_item);
+
+void          _ecore_fps_debug_init(void);
+void          _ecore_fps_debug_shutdown(void);
+void          _ecore_fps_debug_runtime_add(double t);
+
+void _ecore_thread_init(void);
+void _ecore_thread_shutdown(void);
+
+void _ecore_glib_init(void);
+void _ecore_glib_shutdown(void);
+
+void _ecore_job_init(void);
+void _ecore_job_shutdown(void);
+
+void _ecore_main_loop_init(void);
+void _ecore_main_loop_shutdown(void);
+
+extern int    _ecore_fps_debug;
+extern double _ecore_time_loop_time;
+extern Eina_Bool _ecore_glib_always_integrate;
+
+#endif
diff --git a/tests/suite/ecore/src/lib/ecore_signal.c b/tests/suite/ecore/src/lib/ecore_signal.c
new file mode 100644 (file)
index 0000000..10a4711
--- /dev/null
@@ -0,0 +1,620 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+/* make mono happy - this is evil though... */
+#undef SIGPWR
+/* valgrind in some versions/setups uses SIGRT's... hmmm */
+#undef SIGRTMIN
+
+typedef void (*Signal_Handler)(int sig, siginfo_t *si, void *foo);
+
+static void _ecore_signal_callback_set(int sig, Signal_Handler func);
+static void _ecore_signal_callback_ignore(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sigchld(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sigusr1(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sigusr2(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sighup(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sigquit(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sigint(int sig, siginfo_t *si, void *foo);
+static void _ecore_signal_callback_sigterm(int sig, siginfo_t *si, void *foo);
+#ifdef SIGPWR
+static void _ecore_signal_callback_sigpwr(int sig, siginfo_t *si, void *foo);
+#endif
+
+#ifdef SIGRTMIN
+static void _ecore_signal_callback_sigrt(int sig, siginfo_t *si, void *foo);
+#endif
+
+static Eina_Bool _ecore_signal_exe_exit_delay(void *data);
+
+//#define MAXSIGQ 256 // 32k
+#define MAXSIGQ 64 // 8k
+
+static volatile sig_atomic_t sig_count = 0;
+static volatile sig_atomic_t sigchld_count = 0;
+static volatile sig_atomic_t sigusr1_count = 0;
+static volatile sig_atomic_t sigusr2_count = 0;
+static volatile sig_atomic_t sighup_count = 0;
+static volatile sig_atomic_t sigquit_count = 0;
+static volatile sig_atomic_t sigint_count = 0;
+static volatile sig_atomic_t sigterm_count = 0;
+#ifdef SIGPWR
+static volatile sig_atomic_t sigpwr_count = 0;
+#endif
+#ifdef SIGRTMIN
+static volatile sig_atomic_t *sigrt_count = NULL;
+#endif
+
+static volatile siginfo_t sigchld_info[MAXSIGQ];
+static volatile siginfo_t sigusr1_info[MAXSIGQ];
+static volatile siginfo_t sigusr2_info[MAXSIGQ];
+static volatile siginfo_t sighup_info[MAXSIGQ];
+static volatile siginfo_t sigquit_info[MAXSIGQ];
+static volatile siginfo_t sigint_info[MAXSIGQ];
+static volatile siginfo_t sigterm_info[MAXSIGQ];
+#ifdef SIGPWR
+static volatile siginfo_t sigpwr_info[MAXSIGQ];
+#endif
+#ifdef SIGRTMIN
+static volatile siginfo_t *sigrt_info[MAXSIGQ];
+#endif
+
+void
+_ecore_signal_shutdown(void)
+{
+#ifdef SIGRTMIN
+   int i, num = SIGRTMAX - SIGRTMIN;
+#endif
+
+   _ecore_signal_callback_set(SIGPIPE, (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGALRM, (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGCHLD, (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGUSR1, (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGUSR2, (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGHUP,  (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGQUIT, (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGINT,  (Signal_Handler) SIG_DFL);
+   _ecore_signal_callback_set(SIGTERM, (Signal_Handler) SIG_DFL);
+#ifdef SIGPWR
+   _ecore_signal_callback_set(SIGPWR, (Signal_Handler) SIG_DFL);
+   sigpwr_count = 0;
+#endif
+   sigchld_count = 0;
+   sigusr1_count = 0;
+   sigusr2_count = 0;
+   sighup_count = 0;
+   sigquit_count = 0;
+   sigint_count = 0;
+   sigterm_count = 0;
+   sig_count = 0;
+
+#ifdef SIGRTMIN
+   for (i = 0; i < num; i++)
+     {
+        _ecore_signal_callback_set(SIGRTMIN + i, (Signal_Handler) SIG_DFL);
+        sigrt_count[i] = 0;
+     }
+
+   if (sigrt_count)
+     {
+        free((sig_atomic_t *) sigrt_count);
+        sigrt_count = NULL;
+     }
+
+   for (i = 0; i < MAXSIGQ; i++)
+     {
+        if (sigrt_info[i])
+          {
+             free((siginfo_t *) sigrt_info[i]);
+             sigrt_info[i] = NULL;
+          }
+     }
+#endif
+}
+
+void
+_ecore_signal_init(void)
+{
+#ifdef SIGRTMIN
+   int i, num = SIGRTMAX - SIGRTMIN;
+#endif
+
+   _ecore_signal_callback_set(SIGPIPE, _ecore_signal_callback_ignore);
+   _ecore_signal_callback_set(SIGALRM, _ecore_signal_callback_ignore);
+   _ecore_signal_callback_set(SIGCHLD, _ecore_signal_callback_sigchld);
+   _ecore_signal_callback_set(SIGUSR1, _ecore_signal_callback_sigusr1);
+   _ecore_signal_callback_set(SIGUSR2, _ecore_signal_callback_sigusr2);
+   _ecore_signal_callback_set(SIGHUP,  _ecore_signal_callback_sighup);
+   _ecore_signal_callback_set(SIGQUIT, _ecore_signal_callback_sigquit);
+   _ecore_signal_callback_set(SIGINT,  _ecore_signal_callback_sigint);
+   _ecore_signal_callback_set(SIGTERM, _ecore_signal_callback_sigterm);
+#ifdef SIGPWR
+   _ecore_signal_callback_set(SIGPWR,  _ecore_signal_callback_sigpwr);
+#endif
+
+#ifdef SIGRTMIN
+   sigrt_count = calloc(1, sizeof(sig_atomic_t) * num);
+   assert(sigrt_count);
+
+   for (i = 0; i < MAXSIGQ; i++)
+     {
+        sigrt_info[i] = calloc(1, sizeof(siginfo_t) * num);
+        assert(sigrt_info[i]);
+     }
+
+   for (i = 0; i < num; i++)
+      _ecore_signal_callback_set(SIGRTMIN + i, _ecore_signal_callback_sigrt);
+#endif
+}
+
+int
+_ecore_signal_count_get(void)
+{
+   return sig_count;
+}
+
+void
+_ecore_signal_call(void)
+{
+#ifdef SIGRTMIN
+   int i, num = SIGRTMAX - SIGRTMIN;
+#endif
+   volatile sig_atomic_t n;
+   sigset_t oldset, newset;
+
+   if (sig_count == 0) return;
+   sigemptyset(&newset);
+   sigaddset(&newset, SIGPIPE);
+   sigaddset(&newset, SIGALRM);
+   sigaddset(&newset, SIGCHLD);
+   sigaddset(&newset, SIGUSR1);
+   sigaddset(&newset, SIGUSR2);
+   sigaddset(&newset, SIGHUP);
+   sigaddset(&newset, SIGQUIT);
+   sigaddset(&newset, SIGINT);
+   sigaddset(&newset, SIGTERM);
+#ifdef SIGPWR
+   sigaddset(&newset, SIGPWR);
+#endif
+#ifdef SIGRTMIN
+   for (i = 0; i < num; i++)
+     sigaddset(&newset, SIGRTMIN + i);
+#endif
+   sigprocmask(SIG_BLOCK, &newset, &oldset);
+   if (sigchld_count > MAXSIGQ)
+     WRN("%i SIGCHLD in queue. max queue size %i. losing "
+          "siginfo for extra signals.", sigchld_count, MAXSIGQ);
+   for (n = 0; n < sigchld_count; n++)
+     {
+        pid_t pid;
+        int status;
+
+        while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+          {
+             Ecore_Exe_Event_Del *e;
+
+             /* FIXME: If this process is set respawn, respawn with a suitable backoff
+              * period for those that need too much respawning.
+              */
+             e = _ecore_exe_event_del_new();
+             if (e)
+               {
+                  if (WIFEXITED(status))
+                    {
+                       e->exit_code = WEXITSTATUS(status);
+                       e->exited = 1;
+                    }
+                  else if (WIFSIGNALED(status))
+                    {
+                       e->exit_signal = WTERMSIG(status);
+                       e->signalled = 1;
+                    }
+                  e->pid = pid;
+                  e->exe = _ecore_exe_find(pid);
+
+                  if ((n < MAXSIGQ) && (sigchld_info[n].si_signo))
+                    e->data = sigchld_info[n]; /* No need to clone this. */
+
+                  if ((e->exe) && (ecore_exe_flags_get(e->exe) & (ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR)))
+                     {
+                        /* We want to report the Last Words of the exe, so delay this event.
+                         * This is twice as relevant for stderr.
+                         * There are three possibilities here -
+                         *  1 There are no Last Words.
+                         *  2 There are Last Words, they are not ready to be read.
+                         *  3 There are Last Words, they are ready to be read.
+                         *
+                         * For 1 we don't want to delay, for 3 we want to delay.
+                         * 2 is the problem.  If we check for data now and there
+                         * is none, then there is no way to differentiate 1 and 2.
+                         * If we don't delay, we may loose data, but if we do delay,
+                         * there may not be data and the exit event never gets sent.
+                         *
+                         * Any way you look at it, there has to be some time passed
+                         * before the exit event gets sent.  So the strategy here is
+                         * to setup a timer event that will send the exit event after
+                         * an arbitrary, but brief, time.
+                         *
+                         * This is probably paranoid, for the less paraniod, we could
+                         * check to see for Last Words, and only delay if there are any.
+                         * This has it's own set of problems.
+                         */
+                        Ecore_Timer *doomsday_clock;
+
+                        doomsday_clock = _ecore_exe_doomsday_clock_get(e->exe);
+                        IF_FN_DEL(ecore_timer_del, doomsday_clock);
+                        _ecore_exe_doomsday_clock_set(e->exe, ecore_timer_add(0.1, _ecore_signal_exe_exit_delay, e));
+                     }
+                  else
+                    {
+                       _ecore_event_add(ECORE_EXE_EVENT_DEL, e,
+                                   _ecore_exe_event_del_free, NULL);
+                    }
+               }
+          }
+        sig_count--;
+     }
+   sigchld_count = 0;
+
+   if (sigusr1_count > MAXSIGQ)
+     WRN("%i SIGUSR1 in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sigusr1_count, MAXSIGQ);
+   for (n = 0; n < sigusr1_count; n++)
+     {
+        Ecore_Event_Signal_User *e;
+
+        e = _ecore_event_signal_user_new();
+        if (e)
+          {
+             e->number = 1;
+
+             if ((n < MAXSIGQ) && (sigusr1_info[n].si_signo))
+               e->data = sigusr1_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_USER, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sigusr1_count = 0;
+
+   if (sigusr2_count > MAXSIGQ)
+     WRN("%i SIGUSR2 in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sigusr2_count, MAXSIGQ);
+   for (n = 0; n < sigusr2_count; n++)
+     {
+        Ecore_Event_Signal_User *e;
+
+        e = _ecore_event_signal_user_new();
+        if (e)
+          {
+             e->number = 2;
+
+             if ((n < MAXSIGQ) && (sigusr2_info[n].si_signo))
+               e->data = sigusr2_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_USER, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sigusr2_count = 0;
+
+   if (sighup_count > MAXSIGQ)
+     WRN("%i SIGHUP in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sighup_count, MAXSIGQ);
+   for (n = 0; n < sighup_count; n++)
+     {
+        Ecore_Event_Signal_Hup *e;
+
+        e = _ecore_event_signal_hup_new();
+        if (e)
+          {
+             if ((n < MAXSIGQ) && (sighup_info[n].si_signo))
+               e->data = sighup_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_HUP, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sighup_count = 0;
+
+   if (sigquit_count > MAXSIGQ)
+     WRN("%i SIGQUIT in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sigquit_count, MAXSIGQ);
+   for (n = 0; n < sigquit_count; n++)
+     {
+        Ecore_Event_Signal_Exit *e;
+
+        e = _ecore_event_signal_exit_new();
+        if (e)
+          {
+             e->quit = 1;
+
+             if ((n < MAXSIGQ) && (sigquit_info[n].si_signo))
+               e->data = sigquit_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sigquit_count = 0;
+
+   if (sigint_count > MAXSIGQ)
+     WRN("%i SIGINT in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sigint_count, MAXSIGQ);
+   for (n = 0; n < sigint_count; n++)
+     {
+        Ecore_Event_Signal_Exit *e;
+
+        e = _ecore_event_signal_exit_new();
+        if (e)
+          {
+             e->interrupt = 1;
+
+             if ((n < MAXSIGQ) && (sigint_info[n].si_signo))
+               e->data = sigint_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sigint_count = 0;
+
+   if (sigterm_count > MAXSIGQ)
+     WRN("%i SIGTERM in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sigterm_count, MAXSIGQ);
+   for (n = 0; n < sigterm_count; n++)
+     {
+        Ecore_Event_Signal_Exit *e;
+
+        e = _ecore_event_signal_exit_new();
+        if (e)
+          {
+             e->terminate = 1;
+
+             if ((n < MAXSIGQ) && (sigterm_info[n].si_signo))
+               e->data = sigterm_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sigterm_count = 0;
+
+#ifdef SIGPWR
+   if (sigpwr_count > MAXSIGQ)
+     WRN("%i SIGPWR in queue. max queue size %i. losing "
+         "siginfo for extra signals.", sigpwr_count, MAXSIGQ);
+   for (n = 0; n < sigpwr_count; n++)
+     {
+        Ecore_Event_Signal_Power *e;
+
+        e = _ecore_event_signal_power_new();
+        if (e)
+          {
+             if ((n < MAXSIGQ) && (sigpwr_info[n].si_signo))
+               e->data = sigpwr_info[n];
+
+             ecore_event_add(ECORE_EVENT_SIGNAL_POWER, e, NULL, NULL);
+          }
+        sig_count--;
+     }
+   sigpwr_count = 0;
+#endif
+
+#ifdef SIGRTMIN
+   for (i = 0; i < num; i++)
+     {
+        if (sigrt_count[i] > MAXSIGQ)
+          WRN("%i SIGRT%i in queue. max queue size %i. losing "
+              "siginfo for extra signals.", i + 1, sigrt_count[i], MAXSIGQ);
+        for (n = 0; n < sigrt_count[i]; n++)
+          {
+             Ecore_Event_Signal_Realtime *e;
+
+             if ((e = _ecore_event_signal_realtime_new()))
+               {
+                  e->num = i;
+
+                  if ((n < MAXSIGQ) && (sigrt_info[n][i].si_signo))
+                    e->data = sigrt_info[n][i];
+
+                  ecore_event_add(ECORE_EVENT_SIGNAL_REALTIME, e, NULL, NULL);
+               }
+             sig_count--;
+          }
+        sigrt_count[i] = 0;
+     }
+#endif
+   sigprocmask(SIG_SETMASK, &oldset, NULL);
+}
+
+static void
+_ecore_signal_callback_set(int sig, Signal_Handler func)
+{
+   struct sigaction  sa;
+
+   sa.sa_sigaction = func;
+   sa.sa_flags = SA_RESTART | SA_SIGINFO;
+   sigemptyset(&sa.sa_mask);
+   sigaction(sig, &sa, NULL);
+}
+
+static void
+_ecore_signal_callback_ignore(int sig __UNUSED__, siginfo_t *si __UNUSED__, void *foo __UNUSED__)
+{
+}
+
+static void
+_ecore_signal_callback_sigchld(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigchld_info[n] = *si;
+        else
+          sigchld_info[n].si_signo = 0;
+     }
+
+   sigchld_count++;
+   sig_count++;
+}
+
+static void
+_ecore_signal_callback_sigusr1(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigusr1_info[n] = *si;
+        else
+          sigusr1_info[n].si_signo = 0;
+     }
+   sigusr1_count++;
+   sig_count++;
+}
+
+static void
+_ecore_signal_callback_sigusr2(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigusr2_info[n] = *si;
+        else
+          sigusr2_info[n].si_signo = 0;
+     }
+   sigusr2_count++;
+   sig_count++;
+}
+
+static void
+_ecore_signal_callback_sighup(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sighup_info[n] = *si;
+        else
+          sighup_info[n].si_signo = 0;
+     }
+   sighup_count++;
+   sig_count++;
+}
+
+static void
+_ecore_signal_callback_sigquit(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigquit_info[n] = *si;
+        else
+          sigquit_info[n].si_signo = 0;
+     }
+   sigquit_count++;
+   sig_count++;
+}
+
+static void
+_ecore_signal_callback_sigint(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigint_info[n] = *si;
+        else
+          sigint_info[n].si_signo = 0;
+     }
+   sigint_count++;
+   sig_count++;
+}
+
+static void
+_ecore_signal_callback_sigterm(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigterm_info[n] = *si;
+        else
+          sigterm_info[n].si_signo = 0;
+     }
+   sigterm_count++;
+   sig_count++;
+}
+
+#ifdef SIGPWR
+static void
+_ecore_signal_callback_sigpwr(int sig __UNUSED__, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigpwr_info[n] = *si;
+        else
+          sigpwr_info[n].si_signo = 0;
+     }
+   sigpwr_count++;
+   sig_count++;
+}
+#endif
+
+#ifdef SIGRTMIN
+static void
+_ecore_signal_callback_sigrt(int sig, siginfo_t *si, void *foo __UNUSED__)
+{
+   volatile sig_atomic_t n;
+   n = sigchld_count;
+   if (n < MAXSIGQ)
+     {
+        if (si)
+          sigrt_info[n][sig - SIGRTMIN] = *si;
+        else
+          sigrt_info[n][sig - SIGRTMIN].si_signo = 0;
+     }
+   sigrt_count[sig - SIGRTMIN]++;
+   sig_count++;
+}
+#endif
+
+static Eina_Bool
+_ecore_signal_exe_exit_delay(void *data)
+{
+   Ecore_Exe_Event_Del *e;
+
+   e = data;
+   if (e)
+     {
+        _ecore_exe_doomsday_clock_set(e->exe, NULL);
+        _ecore_event_add(ECORE_EXE_EVENT_DEL, e,
+                         _ecore_exe_event_del_free, NULL);
+     }
+   return ECORE_CALLBACK_CANCEL;
+}
diff --git a/tests/suite/ecore/src/lib/ecore_thread.c b/tests/suite/ecore/src/lib/ecore_thread.c
new file mode 100644 (file)
index 0000000..c8a5daf
--- /dev/null
@@ -0,0 +1,1226 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#ifdef EFL_HAVE_PTHREAD
+# include <pthread.h>
+# ifdef __linux__
+#  ifndef _GNU_SOURCE
+#   define _GNU_SOURCE 1
+#  endif
+#  include <sched.h>
+#  include <sys/time.h>
+#  include <sys/resource.h>
+#  include <unistd.h>
+#  include <sys/syscall.h>
+#  include <errno.h>
+# endif
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+typedef struct _Ecore_Pthread_Worker Ecore_Pthread_Worker;
+typedef struct _Ecore_Pthread Ecore_Pthread;
+typedef struct _Ecore_Thread_Data  Ecore_Thread_Data;
+
+struct _Ecore_Thread_Data
+{
+   void *data;
+   Eina_Free_Cb cb;
+};
+
+struct _Ecore_Pthread_Worker
+{
+   union {
+      struct {
+         Ecore_Cb func_blocking;
+      } short_run;
+      struct {
+         Ecore_Thread_Heavy_Cb func_heavy;
+         Ecore_Thread_Notify_Cb func_notify;
+         Ecore_Pipe *notify;
+      } feedback_run;
+   } u;
+   
+   Ecore_Cb func_cancel;
+   Ecore_Cb func_end;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_t self;
+   Eina_Hash *hash;
+   pthread_cond_t cond;
+   pthread_mutex_t mutex;
+#endif
+   
+   const void *data;
+   
+   Eina_Bool cancel : 1;
+   Eina_Bool feedback_run : 1;
+};
+
+#ifdef EFL_HAVE_PTHREAD
+typedef struct _Ecore_Pthread_Data Ecore_Pthread_Data;
+
+struct _Ecore_Pthread_Data
+{
+   Ecore_Pipe *p;
+   void *data;
+   pthread_t thread;
+};
+#endif
+
+static int _ecore_thread_count_max = 0;
+static int ECORE_THREAD_PIPE_DEL = 0;
+
+#ifdef EFL_HAVE_PTHREAD
+static int _ecore_thread_count = 0;
+
+static Eina_List *_ecore_active_job_threads = NULL;
+static Eina_List *_ecore_pending_job_threads = NULL;
+static Eina_List *_ecore_pending_job_threads_feedback = NULL;
+static Ecore_Event_Handler *del_handler = NULL;
+static pthread_mutex_t _ecore_pending_job_threads_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static Eina_Hash *_ecore_thread_global_hash = NULL;
+static pthread_rwlock_t _ecore_thread_global_hash_lock = PTHREAD_RWLOCK_INITIALIZER;
+static pthread_mutex_t _ecore_thread_global_hash_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t _ecore_thread_global_hash_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t main_loop_thread;
+static Eina_Bool have_main_loop_thread = 0;
+static void
+_ecore_thread_data_free(void *data)
+{
+   Ecore_Thread_Data *d = data;
+
+   if (d->cb) d->cb(d->data);
+   free(d);
+}
+
+static void
+_ecore_thread_pipe_free(void *data __UNUSED__, void *event)
+{
+   Ecore_Pipe *p = event;
+
+   ecore_pipe_del(p);
+}
+
+static Eina_Bool
+_ecore_thread_pipe_del(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
+{
+   /* This is a hack to delay pipe destruction until we are out of its internal loop. */
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+_ecore_thread_end(Ecore_Pthread_Data *pth)
+{
+   Ecore_Pipe *p;
+
+   if (pthread_join(pth->thread, (void **) &p) != 0)
+     return ;
+
+   _ecore_active_job_threads = eina_list_remove(_ecore_active_job_threads, pth);
+
+   ecore_event_add(ECORE_THREAD_PIPE_DEL, pth->p, _ecore_thread_pipe_free, NULL);
+   free(pth);
+}
+
+static void
+_ecore_thread_handler(void *data __UNUSED__, void *buffer, unsigned int nbyte)
+{
+   Ecore_Pthread_Worker *work;
+
+   if (nbyte != sizeof (Ecore_Pthread_Worker *)) return ;
+
+   work = *(Ecore_Pthread_Worker **)buffer;
+
+   if (work->cancel)
+     {
+        if (work->func_cancel)
+          work->func_cancel((void *) work->data);
+     }
+   else
+     {
+        if (work->func_end)
+          work->func_end((void *) work->data);
+     }
+
+   if (work->feedback_run)
+        ecore_pipe_del(work->u.feedback_run.notify);
+   pthread_cond_destroy(&work->cond);
+   pthread_mutex_destroy(&work->mutex);
+   if (work->hash)
+     eina_hash_free(work->hash);
+   free(work);
+}
+
+static void
+_ecore_notify_handler(void *data, void *buffer, unsigned int nbyte)
+{
+   Ecore_Pthread_Worker *work = data;
+   void *user_data;
+
+   if (nbyte != sizeof (Ecore_Pthread_Worker *)) return ;
+
+   user_data = *(void **)buffer;
+
+   if (work->u.feedback_run.func_notify)
+     work->u.feedback_run.func_notify((Ecore_Thread *) work, user_data, (void *) work->data);
+}
+
+static void
+_ecore_short_job(Ecore_Pipe *end_pipe)
+{
+   Ecore_Pthread_Worker *work;
+
+   while (_ecore_pending_job_threads)
+     {
+        pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+
+        if (!_ecore_pending_job_threads)
+          {
+             pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+             break;
+          }
+
+        work = eina_list_data_get(_ecore_pending_job_threads);
+        _ecore_pending_job_threads = eina_list_remove_list(_ecore_pending_job_threads, _ecore_pending_job_threads);
+
+        pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+        if (!work->cancel)
+          work->u.short_run.func_blocking((void *) work->data);
+
+        ecore_pipe_write(end_pipe, &work, sizeof (Ecore_Pthread_Worker *));
+     }
+}
+
+static void
+_ecore_feedback_job(Ecore_Pipe *end_pipe, pthread_t thread)
+{
+   Ecore_Pthread_Worker *work;
+
+   while (_ecore_pending_job_threads_feedback)
+     {
+        pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+
+        if (!_ecore_pending_job_threads_feedback)
+          {
+             pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+             break;
+          }
+
+        work = eina_list_data_get(_ecore_pending_job_threads_feedback);
+        _ecore_pending_job_threads_feedback = eina_list_remove_list(_ecore_pending_job_threads_feedback, _ecore_pending_job_threads_feedback);
+
+        pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+        work->self = thread;
+        if (!work->cancel)
+          work->u.feedback_run.func_heavy((Ecore_Thread *) work, (void *) work->data);
+
+        ecore_pipe_write(end_pipe, &work, sizeof (Ecore_Pthread_Worker *));
+     }
+}
+
+static void *
+_ecore_direct_worker(Ecore_Pthread_Worker *work)
+{
+   Ecore_Pthread_Data *pth;
+
+   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+   eina_sched_prio_drop();
+
+   pth = malloc(sizeof (Ecore_Pthread_Data));
+   if (!pth) return NULL;
+
+   pth->p = ecore_pipe_add(_ecore_thread_handler, NULL);
+   if (!pth->p)
+     {
+        free(pth);
+        return NULL;
+     }
+   pth->thread = pthread_self();
+
+   work->self = pth->thread;
+   work->u.feedback_run.func_heavy((Ecore_Thread *) work, (void *) work->data);
+
+   ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker *));
+
+   work = malloc(sizeof (Ecore_Pthread_Worker));
+   if (!work)
+     {
+        ecore_pipe_del(pth->p);
+        free(pth);
+        return NULL;
+     }
+
+   work->data = pth;
+   work->u.short_run.func_blocking = NULL;
+   work->func_end = (void *) _ecore_thread_end;
+   work->func_cancel = NULL;
+   work->cancel = EINA_FALSE;
+   work->feedback_run = EINA_FALSE;
+   work->hash = NULL;
+   pthread_cond_init(&work->cond, NULL);
+   pthread_mutex_init(&work->mutex, NULL);
+
+   ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker *));
+
+   return pth->p;
+}
+
+static void *
+_ecore_thread_worker(Ecore_Pthread_Data *pth)
+{
+   Ecore_Pthread_Worker *work;
+
+   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+   eina_sched_prio_drop();
+
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   _ecore_thread_count++;
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+ on_error:
+   if (_ecore_pending_job_threads) _ecore_short_job(pth->p);
+   if (_ecore_pending_job_threads_feedback) _ecore_feedback_job(pth->p, pth->thread);
+
+   /* FIXME: Check if there is feedback running task todo, and switch to feedback run handler. */
+
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   if (_ecore_pending_job_threads)
+     {
+        pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+        goto on_error;
+     }
+   if (_ecore_pending_job_threads_feedback)
+     {
+        pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+        goto on_error;
+     }
+
+   _ecore_thread_count--;
+
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+   work = malloc(sizeof (Ecore_Pthread_Worker));
+   if (!work) return NULL;
+
+   work->data = pth;
+   work->u.short_run.func_blocking = NULL;
+   work->func_end = (void *) _ecore_thread_end;
+   work->func_cancel = NULL;
+   work->cancel = EINA_FALSE;
+   work->feedback_run = EINA_FALSE;
+   work->hash = NULL;
+   pthread_cond_init(&work->cond, NULL);
+   pthread_mutex_init(&work->mutex, NULL);
+
+   ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker *));
+
+   return pth->p;
+}
+
+#endif
+
+void
+_ecore_thread_init(void)
+{
+   _ecore_thread_count_max = eina_cpu_count();
+   if (_ecore_thread_count_max <= 0)
+     _ecore_thread_count_max = 1;
+
+   ECORE_THREAD_PIPE_DEL = ecore_event_type_new();
+#ifdef EFL_HAVE_PTHREAD
+   del_handler = ecore_event_handler_add(ECORE_THREAD_PIPE_DEL, _ecore_thread_pipe_del, NULL);
+   main_loop_thread = pthread_self();
+   have_main_loop_thread = 1;
+#endif
+}
+
+void
+_ecore_thread_shutdown(void)
+{
+   /* FIXME: If function are still running in the background, should we kill them ? */
+#ifdef EFL_HAVE_PTHREAD
+   Ecore_Pthread_Worker *work;
+   Ecore_Pthread_Data *pth;
+
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+
+   EINA_LIST_FREE(_ecore_pending_job_threads, work)
+     {
+        if (work->func_cancel)
+          work->func_cancel((void *)work->data);
+        free(work);
+     }
+
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+   EINA_LIST_FREE(_ecore_active_job_threads, pth)
+     {
+        Ecore_Pipe *p;
+
+        pthread_cancel(pth->thread);
+        pthread_join(pth->thread, (void **) &p);
+
+        ecore_pipe_del(pth->p);
+     }
+   if (_ecore_thread_global_hash)
+     eina_hash_free(_ecore_thread_global_hash);
+   ecore_event_handler_del(del_handler);
+   have_main_loop_thread = 0;
+   del_handler = NULL;
+#endif
+}
+/**
+ * @addtogroup Ecore_Thread Ecore Thread Functions
+ * These functions allow for ecore-managed threads which integrate with ecore's main loop.
+ * @{
+ */
+
+/**
+ * @brief Run some blocking code in a parallel thread to avoid locking the main loop.
+ * @param func_blocking The function that should run in another thread.
+ * @param func_end The function that will be called in the main loop if the thread terminate correctly.
+ * @param func_cancel The function that will be called in the main loop if the thread is cancelled.
+ * @param data User context data to pass to all callback.
+ * @return A reference to the newly created thread instance, or NULL if it failed.
+ *
+ * ecore_thread_run provide a facility for easily managing blocking task in a
+ * parallel thread. You should provide three function. The first one, func_blocking,
+ * that will do the blocking work in another thread (so you should not use the
+ * EFL in it except Eina if you are careful). The second one, func_end,
+ * that will be called in Ecore main loop when func_blocking is done. So you
+ * can use all the EFL inside this function. The last one, func_cancel, will
+ * be called in the main loop if the thread is cancelled or could not run at all.
+ *
+ * Be aware, that you can't make assumption on the result order of func_end
+ * after many call to ecore_thread_run, as we start as much thread as the
+ * host CPU can handle.
+ */
+EAPI Ecore_Thread *
+ecore_thread_run(Ecore_Cb func_blocking,
+                 Ecore_Cb func_end,
+                 Ecore_Cb func_cancel,
+                 const void *data)
+{
+#ifdef EFL_HAVE_PTHREAD
+   Ecore_Pthread_Worker *work;
+   Ecore_Pthread_Data *pth = NULL;
+
+   if (!func_blocking) return NULL;
+
+   work = malloc(sizeof (Ecore_Pthread_Worker));
+   if (!work)
+     {
+        func_cancel((void *) data);
+        return NULL;
+     }
+
+   work->u.short_run.func_blocking = func_blocking;
+   work->hash = NULL;
+   pthread_cond_init(&work->cond, NULL);
+   pthread_mutex_init(&work->mutex, NULL);
+   work->func_end = func_end;
+   work->func_cancel = func_cancel;
+   work->cancel = EINA_FALSE;
+   work->feedback_run = EINA_FALSE;
+   work->data = data;
+
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   _ecore_pending_job_threads = eina_list_append(_ecore_pending_job_threads, work);
+
+   if (_ecore_thread_count == _ecore_thread_count_max)
+     {
+        pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+        return (Ecore_Thread *) work;
+     }
+
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+   /* One more thread could be created. */
+   pth = malloc(sizeof (Ecore_Pthread_Data));
+   if (!pth) goto on_error;
+
+   pth->p = ecore_pipe_add(_ecore_thread_handler, NULL);
+   if (!pth->p) goto on_error;
+
+   if (pthread_create(&pth->thread, NULL, (void *) _ecore_thread_worker, pth) == 0)
+      return (Ecore_Thread *) work;
+
+ on_error:
+   if (pth)
+     {
+        if (pth->p) ecore_pipe_del(pth->p);
+        free(pth);
+     }
+
+   if (_ecore_thread_count == 0)
+     {
+        if (work->func_cancel)
+          work->func_cancel((void *) work->data);
+        free(work);
+        work = NULL;
+     }
+   return (Ecore_Thread *) work;
+#else
+   /*
+     If no thread and as we don't want to break app that rely on this
+     facility, we will lock the interface until we are done.
+    */
+   func_blocking((void *)data);
+   func_end((void *)data);
+
+   return NULL;
+#endif
+}
+
+/**
+ * @brief Cancel a running thread.
+ * @param thread The thread to cancel.
+ * @return Will return EINA_TRUE if the thread has been cancelled,
+ *         EINA_FALSE if it is pending.
+ *
+ * ecore_thread_cancel give the possibility to cancel a task still running. It
+ * will return EINA_FALSE, if the destruction is delayed or EINA_TRUE if it is
+ * cancelled after this call.
+ *
+ * You should use this function only in the main loop.
+ *
+ * func_end, func_cancel will destroy the handler, so don't use it after.
+ * And if ecore_thread_cancel return EINA_TRUE, you should not use Ecore_Thread also.
+ */
+EAPI Eina_Bool
+ecore_thread_cancel(Ecore_Thread *thread)
+{
+#ifdef EFL_HAVE_PTHREAD
+   Ecore_Pthread_Worker *work = (Ecore_Pthread_Worker *)thread;
+   Eina_List *l;
+   
+   if (!work)
+      return EINA_TRUE;
+   
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   
+   if ((have_main_loop_thread) &&
+       (pthread_equal(main_loop_thread, pthread_self())))
+     {
+        EINA_LIST_FOREACH(_ecore_pending_job_threads, l, work)
+          {
+             if ((void *) work == (void *) thread)
+               {
+                  _ecore_pending_job_threads = eina_list_remove_list(_ecore_pending_job_threads, l);
+                  
+                  pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+                  
+                  if (work->func_cancel)
+                     work->func_cancel((void *) work->data);
+                  free(work);
+                  
+                  return EINA_TRUE;
+               }
+          }
+     }
+
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+   /* Delay the destruction */
+   ((Ecore_Pthread_Worker *)thread)->cancel = EINA_TRUE;
+   return EINA_FALSE;
+#else
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Tell if a thread was canceled or not.
+ * @param thread The thread to test.
+ * @return EINA_TRUE if the thread is cancelled,
+ *         EINA_FALSE if it is not.
+ *
+ * You can use this function in main loop and in the thread.
+ */
+EAPI Eina_Bool
+ecore_thread_check(Ecore_Thread *thread)
+{
+   Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread;
+
+   if (!worker) return EINA_TRUE;
+   return worker->cancel;
+}
+
+/**
+ * @brief Run some heavy code in a parallel thread to avoid locking the main loop.
+ * @param func_heavy The function that should run in another thread.
+ * @param func_notify The function that will receive the data send by func_heavy in the main loop.
+ * @param func_end The function that will be called in the main loop if the thread terminate correctly.
+ * @param func_cancel The function that will be called in the main loop if the thread is cancelled.
+ * @param data User context data to pass to all callback.
+ * @param try_no_queue If you wan't to run outside of the thread pool.
+ * @return A reference to the newly created thread instance, or NULL if it failed.
+ *
+ * ecore_thread_feedback_run provide a facility for easily managing heavy task in a
+ * parallel thread. You should provide four functions. The first one, func_heavy,
+ * that will do the heavy work in another thread (so you should not use the
+ * EFL in it except Eina and Eet if you are careful). The second one, func_notify,
+ * will receive the data send from the thread function (func_heavy) by ecore_thread_notify
+ * in the main loop (and so, can use all the EFL). Tje third, func_end,
+ * that will be called in Ecore main loop when func_heavy is done. So you
+ * can use all the EFL inside this function. The last one, func_cancel, will
+ * be called in the main loop also, if the thread is cancelled or could not run at all.
+ *
+ * Be aware, that you can't make assumption on the result order of func_end
+ * after many call to ecore_feedback_run, as we start as much thread as the
+ * host CPU can handle.
+ *
+ * If you set try_no_queue, it will try to run outside of the thread pool, this can bring
+ * the CPU down, so be careful with that. Of course if it can't start a new thread, it will
+ * try to use one from the pool.
+ */
+EAPI Ecore_Thread *ecore_thread_feedback_run(Ecore_Thread_Heavy_Cb func_heavy,
+                                             Ecore_Thread_Notify_Cb func_notify,
+                                             Ecore_Cb func_end,
+                                             Ecore_Cb func_cancel,
+                                             const void *data,
+                                             Eina_Bool try_no_queue)
+{
+
+#ifdef EFL_HAVE_PTHREAD
+   Ecore_Pthread_Worker *worker;
+   Ecore_Pthread_Data *pth = NULL;
+
+   if (!func_heavy) return NULL;
+
+   worker = malloc(sizeof (Ecore_Pthread_Worker));
+   if (!worker) goto on_error;
+
+   worker->u.feedback_run.func_heavy = func_heavy;
+   worker->u.feedback_run.func_notify = func_notify;
+   worker->hash = NULL;
+   pthread_cond_init(&worker->cond, NULL);
+   pthread_mutex_init(&worker->mutex, NULL);
+   worker->func_cancel = func_cancel;
+   worker->func_end = func_end;
+   worker->data = data;
+   worker->cancel = EINA_FALSE;
+   worker->feedback_run = EINA_TRUE;
+
+   worker->u.feedback_run.notify = ecore_pipe_add(_ecore_notify_handler, worker);
+
+   if (!try_no_queue)
+     {
+        pthread_t t;
+
+        if (pthread_create(&t, NULL, (void *) _ecore_direct_worker, worker) == 0)
+           return (Ecore_Thread *) worker;
+     }
+
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   _ecore_pending_job_threads_feedback = eina_list_append(_ecore_pending_job_threads_feedback, worker);
+
+   if (_ecore_thread_count == _ecore_thread_count_max)
+     {
+        pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+        return (Ecore_Thread *) worker;
+     }
+
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+
+   /* One more thread could be created. */
+   pth = malloc(sizeof (Ecore_Pthread_Data));
+   if (!pth) goto on_error;
+
+   pth->p = ecore_pipe_add(_ecore_thread_handler, NULL);
+   if (!pth->p) goto on_error;
+
+   if (pthread_create(&pth->thread, NULL, (void *) _ecore_thread_worker, pth) == 0)
+      return (Ecore_Thread *) worker;
+
+ on_error:
+   if (pth)
+     {
+        if (pth->p) ecore_pipe_del(pth->p);
+        free(pth);
+     }
+
+   if (_ecore_thread_count == 0)
+     {
+        if (func_cancel) func_cancel((void *) data);
+
+        if (worker)
+          {
+             ecore_pipe_del(worker->u.feedback_run.notify);
+             free(worker);
+             worker = NULL;
+          }
+     }
+
+   return (Ecore_Thread *) worker;
+#else
+   Ecore_Pthread_Worker worker;
+
+   (void) try_no_queue;
+
+   /*
+     If no thread and as we don't want to break app that rely on this
+     facility, we will lock the interface until we are done.
+    */
+   worker.u.feedback_run.func_heavy = func_heavy;
+   worker.u.feedback_run.func_notify = func_notify;
+   worker.u.feedback_run.notify = NULL;
+   worker.func_cancel = func_cancel;
+   worker.func_end = func_end;
+   worker.data = data;
+   worker.cancel = EINA_FALSE;
+   worker.feedback_run = EINA_TRUE;
+
+   func_heavy((Ecore_Thread *) &worker, (void *)data);
+
+   if (worker.cancel) func_cancel((void *)data);
+   else func_end((void *)data);
+
+   return NULL;
+#endif
+}
+
+/**
+ * @brief Send data to main loop from worker thread.
+ * @param thread The current Ecore_Thread context to send data from
+ * @param data Data to be transmitted to the main loop
+ * @return EINA_TRUE if data was successfully send to main loop,
+ *         EINA_FALSE if anything goes wrong.
+ *
+ * After a succesfull call, the data should be considered owned
+ * by the main loop.
+ *
+ * You should use this function only in the func_heavy call.
+ */
+EAPI Eina_Bool
+ecore_thread_feedback(Ecore_Thread *thread, const void *data)
+{
+   Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread;
+
+   if (!worker) return EINA_FALSE;
+   if (!worker->feedback_run) return EINA_FALSE;
+
+#ifdef EFL_HAVE_PTHREAD
+   if (!pthread_equal(worker->self, pthread_self())) return EINA_FALSE;
+
+   ecore_pipe_write(worker->u.feedback_run.notify, &data, sizeof (void *));
+
+   return EINA_TRUE;
+#else
+   worker->u.feedback_run.func_notify(thread, (void*) data, (void*) worker->data);
+
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Get number of active thread jobs
+ * @return Number of active threads running jobs
+ * This returns the number of threads currently running jobs through the
+ * ecore_thread api.
+ */
+EAPI int
+ecore_thread_active_get(void)
+{
+#ifdef EFL_HAVE_PTHREAD
+   return _ecore_thread_count;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @brief Get number of pending (short) thread jobs
+ * @return Number of pending threads running "short" jobs
+ * This returns the number of threads currently running jobs through the
+ * ecore_thread_run api call.
+ */
+EAPI int
+ecore_thread_pending_get(void)
+{
+   int ret;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   ret = eina_list_count(_ecore_pending_job_threads);
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+   return ret;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @brief Get number of pending feedback thread jobs
+ * @return Number of pending threads running "feedback" jobs
+ * This returns the number of threads currently running jobs through the
+ * ecore_thread_feedback_run api call.
+ */
+EAPI int
+ecore_thread_pending_feedback_get(void)
+{
+   int ret;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   ret = eina_list_count(_ecore_pending_job_threads_feedback);
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+   return ret;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @brief Get number of pending thread jobs
+ * @return Number of pending threads running jobs
+ * This returns the number of threads currently running jobs through the
+ * ecore_thread_run and ecore_thread_feedback_run api calls combined.
+ */
+EAPI int
+ecore_thread_pending_total_get(void)
+{
+   int ret;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   ret = eina_list_count(_ecore_pending_job_threads) + eina_list_count(_ecore_pending_job_threads_feedback);
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+   return ret;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @brief Get the max number of threads that can run simultaneously
+ * @return Max number of threads ecore will run
+ * This returns the total number of threads that ecore will attempt to run
+ * simultaneously.
+ */
+EAPI int
+ecore_thread_max_get(void)
+{
+   return _ecore_thread_count_max;
+}
+
+/**
+ * @brief Set the max number of threads that can run simultaneously
+ * @param num The new maximum
+ * This sets the maximum number of threads that ecore will try to run
+ * simultaneously.  This number cannot be < 1 or >= 2x the number of active cpus.
+ */
+EAPI void
+ecore_thread_max_set(int num)
+{
+   if (num < 1) return;
+   /* avoid doing something hilarious by blocking dumb users */
+   if (num >= (2 * eina_cpu_count())) return;
+
+   _ecore_thread_count_max = num;
+}
+
+/**
+ * @brief Reset the max number of threads that can run simultaneously
+ * This resets the maximum number of threads that ecore will try to run
+ * simultaneously to the number of active cpus.
+ */
+EAPI void
+ecore_thread_max_reset(void)
+{
+   _ecore_thread_count_max = eina_cpu_count();
+}
+
+/**
+ * @brief Get the number of threads which are available to be used
+ * @return The number of available threads
+ * This returns the number of threads slots that ecore has currently available.
+ * Assuming that you haven't changed the max number of threads with @ref ecore_thread_max_set
+ * this should be equal to (num_cpus - (active_running + active_feedback_running))
+ */
+EAPI int
+ecore_thread_available_get(void)
+{
+   int ret;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_mutex_lock(&_ecore_pending_job_threads_mutex);
+   ret = _ecore_thread_count_max - _ecore_thread_count;
+   pthread_mutex_unlock(&_ecore_pending_job_threads_mutex);
+   return ret;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @brief Add data to the thread for subsequent use
+ * @param thread The thread context to add to
+ * @param key The name string to add the data with
+ * @param value The data to add
+ * @param cb The callback to free the data with
+ * @param direct If true, this will not copy the key string (like eina_hash_direct_add)
+ * @return EINA_TRUE on success, EINA_FALSE on failure
+ * This adds data to the thread context, allowing the thread
+ * to retrieve and use it without complicated mutexing.  This function can only be called by a
+ * *_run thread INSIDE the thread and will return EINA_FALSE in any case but success.
+ * All data added to the thread will be freed with its associated callback (if present)
+ * upon thread termination.  If no callback is specified, it is expected that the user will free the
+ * data, but this is most likely not what you want.
+ */
+EAPI Eina_Bool
+ecore_thread_local_data_add(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct)
+{
+   Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread;
+   Ecore_Thread_Data *d;
+   Eina_Bool ret;
+
+   if ((!thread) || (!key) || (!value))
+     return EINA_FALSE;
+#ifdef EFL_HAVE_PTHREAD
+   if (!pthread_equal(worker->self, pthread_self())) return EINA_FALSE;
+
+   if (!worker->hash)
+     worker->hash = eina_hash_string_small_new(_ecore_thread_data_free);
+
+   if (!worker->hash)
+     return EINA_FALSE;
+
+   if (!(d = malloc(sizeof(Ecore_Thread_Data))))
+     return EINA_FALSE;
+
+   d->data = value;
+   d->cb = cb;
+
+   if (direct)
+     ret = eina_hash_direct_add(worker->hash, key, d);
+   else
+     ret = eina_hash_add(worker->hash, key, d);
+   pthread_cond_broadcast(&worker->cond);
+   return ret;
+#else
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Modify data in the thread, or add if not found
+ * @param thread The thread context
+ * @param key The name string to add the data with
+ * @param value The data to add
+ * @param cb The callback to free the data with
+ * @return The old data associated with @p key on success if modified, NULL if added
+ * This adds/modifies data in the thread context, adding only if modify fails.
+ * This function can only be called by a *_run thread INSIDE the thread.
+ * All data added to the thread pool will be freed with its associated callback (if present)
+ * upon thread termination.  If no callback is specified, it is expected that the user will free the
+ * data, but this is most likely not what you want.
+ */
+EAPI void *
+ecore_thread_local_data_set(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb)
+{
+   Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread;
+   Ecore_Thread_Data *d, *r;
+   void *ret;
+   if ((!thread) || (!key) || (!value))
+     return NULL;
+#ifdef EFL_HAVE_PTHREAD
+   if (!pthread_equal(worker->self, pthread_self())) return NULL;
+
+   if (!worker->hash)
+     worker->hash = eina_hash_string_small_new(_ecore_thread_data_free);
+
+   if (!worker->hash)
+     return NULL;
+
+   if (!(d = malloc(sizeof(Ecore_Thread_Data))))
+     return NULL;
+
+   d->data = value;
+   d->cb = cb;
+
+   r = eina_hash_set(worker->hash, key, d);
+   pthread_cond_broadcast(&worker->cond);
+   ret = r->data;
+   free(r);
+   return ret;
+#else
+   return NULL;
+#endif
+}
+
+/**
+ * @brief Find data in the thread's data
+ * @param thread The thread context
+ * @param key The name string the data is associated with
+ * @return The value, or NULL on error
+ * This finds data in the thread context that has been previously added with @ref ecore_thread_local_data_add
+ * This function can only be called by a *_run thread INSIDE the thread, and will return NULL
+ * in any case but success.
+ */
+
+EAPI void *
+ecore_thread_local_data_find(Ecore_Thread *thread, const char *key)
+{
+   Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread;
+   Ecore_Thread_Data *d;
+
+   if ((!thread) || (!key))
+     return NULL;
+#ifdef EFL_HAVE_PTHREAD
+   if (!pthread_equal(worker->self, pthread_self())) return NULL;
+
+   if (!worker->hash)
+     return NULL;
+
+   d = eina_hash_find(worker->hash, key);
+   return d->data;
+#else
+   return NULL;
+#endif
+}
+
+/**
+ * @brief Delete data from the thread's data
+ * @param thread The thread context
+ * @param key The name string the data is associated with
+ * @return EINA_TRUE on success, EINA_FALSE on failure
+ * This deletes the data pointer from the thread context which was previously added with @ref ecore_thread_local_data_add
+ * This function can only be called by a *_run thread INSIDE the thread, and will return EINA_FALSE
+ * in any case but success.  Note that this WILL free the data if a callback was specified.
+ */
+EAPI Eina_Bool
+ecore_thread_local_data_del(Ecore_Thread *thread, const char *key)
+{
+   Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker *) thread;
+   Ecore_Thread_Data *d;
+   if ((!thread) || (!key))
+     return EINA_FALSE;
+#ifdef EFL_HAVE_PTHREAD
+   if (!pthread_equal(worker->self, pthread_self())) return EINA_FALSE;
+
+   if (!worker->hash)
+     return EINA_FALSE;
+   if ((d = eina_hash_find(worker->hash, key)))
+     _ecore_thread_data_free(d);
+   return eina_hash_del_by_key(worker->hash, key);
+#else
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Add data to the global data
+ * @param key The name string to add the data with
+ * @param value The data to add
+ * @param cb The optional callback to free the data with once ecore is shut down
+ * @param direct If true, this will not copy the key string (like eina_hash_direct_add)
+ * @return EINA_TRUE on success, EINA_FALSE on failure
+ * This adds data to the global thread data, and will return EINA_FALSE in any case but success.
+ * All data added to global can be manually freed, or a callback can be provided with @p cb which will
+ * be called upon ecore_thread shutting down.  Note that if you have manually freed data that a callback
+ * was specified for, you will most likely encounter a segv later on.
+ */
+EAPI Eina_Bool
+ecore_thread_global_data_add(const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct)
+{
+   Eina_Bool ret;
+   Ecore_Thread_Data *d;
+
+   if ((!key) || (!value))
+     return EINA_FALSE;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock);
+   if (!_ecore_thread_global_hash)
+     _ecore_thread_global_hash = eina_hash_string_small_new(_ecore_thread_data_free);
+   pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+
+   if (!(d = malloc(sizeof(Ecore_Thread_Data))))
+     return EINA_FALSE;
+
+   d->data = value;
+   d->cb = cb;
+
+   if (!_ecore_thread_global_hash)
+     return EINA_FALSE;
+   pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock);
+   if (direct)
+     ret = eina_hash_direct_add(_ecore_thread_global_hash, key, d);
+   else
+     ret = eina_hash_add(_ecore_thread_global_hash, key, d);
+   pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+   pthread_cond_broadcast(&_ecore_thread_global_hash_cond);
+   return ret;
+#else
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Add data to the global data
+ * @param key The name string to add the data with
+ * @param value The data to add
+ * @param cb The optional callback to free the data with once ecore is shut down
+ * @return An Ecore_Thread_Data on success, NULL on failure
+ * This adds data to the global thread data and returns NULL, or replaces the previous data
+ * associated with @p key and returning the previous data if it existed.  To see if an error occurred,
+ * one must use eina_error_get.
+ * All data added to global can be manually freed, or a callback can be provided with @p cb which will
+ * be called upon ecore_thread shutting down.  Note that if you have manually freed data that a callback
+ * was specified for, you will most likely encounter a segv later on.
+ */
+EAPI void *
+ecore_thread_global_data_set(const char *key, void *value, Eina_Free_Cb cb)
+{
+   Ecore_Thread_Data *d, *r;
+   void *ret;
+
+   if ((!key) || (!value))
+     return NULL;
+#ifdef EFL_HAVE_PTHREAD
+   pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock);
+   if (!_ecore_thread_global_hash)
+     _ecore_thread_global_hash = eina_hash_string_small_new(_ecore_thread_data_free);
+   pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+
+   if (!_ecore_thread_global_hash)
+     return NULL;
+
+   if (!(d = malloc(sizeof(Ecore_Thread_Data))))
+     return NULL;
+
+   d->data = value;
+   d->cb = cb;
+
+   pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock);
+   r = eina_hash_set(_ecore_thread_global_hash, key, d);
+   pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+   pthread_cond_broadcast(&_ecore_thread_global_hash_cond);
+
+   ret = r->data;
+   free(r);
+   return ret;
+#else
+   return NULL;
+#endif
+}
+
+/**
+ * @brief Find data in the global data
+ * @param key The name string the data is associated with
+ * @return The value, or NULL on error
+ * This finds data in the global data that has been previously added with @ref ecore_thread_global_data_add
+ * This function will return NULL in any case but success.
+ * All data added to global can be manually freed, or a callback can be provided with @p cb which will
+ * be called upon ecore_thread shutting down.  Note that if you have manually freed data that a callback
+ * was specified for, you will most likely encounter a segv later on.
+ * @note Keep in mind that the data returned can be used by multiple threads at a time, so you will most likely want to mutex
+ * if you will be doing anything with it.
+ */
+
+EAPI void *
+ecore_thread_global_data_find(const char *key)
+{
+   Ecore_Thread_Data *ret;
+   if (!key)
+     return NULL;
+#ifdef EFL_HAVE_PTHREAD
+   if (!_ecore_thread_global_hash) return NULL;
+
+   pthread_rwlock_rdlock(&_ecore_thread_global_hash_lock);
+   ret = eina_hash_find(_ecore_thread_global_hash, key);
+   pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+   return ret->data;
+#else
+   return NULL;
+#endif
+}
+
+/**
+ * @brief Delete data from the global data
+ * @param key The name string the data is associated with
+ * @return EINA_TRUE on success, EINA_FALSE on failure
+ * This deletes the data pointer from the global data which was previously added with @ref ecore_thread_global_data_add
+ * This function will return EINA_FALSE in any case but success.
+ * Note that this WILL free the data if an @c Eina_Free_Cb was specified when the data was added.
+ */
+EAPI Eina_Bool
+ecore_thread_global_data_del(const char *key)
+{
+   Eina_Bool ret;
+   Ecore_Thread_Data *d;
+
+   if (!key)
+     return EINA_FALSE;
+#ifdef EFL_HAVE_PTHREAD
+   if (!_ecore_thread_global_hash)
+     return EINA_FALSE;
+
+   pthread_rwlock_wrlock(&_ecore_thread_global_hash_lock);
+   if ((d = eina_hash_find(_ecore_thread_global_hash, key)))
+     _ecore_thread_data_free(d);
+   ret = eina_hash_del_by_key(_ecore_thread_global_hash, key);
+   pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+   return ret;
+#else
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Find data in the global data and optionally wait for the data if not found
+ * @param key The name string the data is associated with
+ * @param seconds The amount of time in seconds to wait for the data.  If 0, the call will be async and not wait for data.
+ * If < 0 the call will wait indefinitely for the data.
+ * @return The value, or NULL on failure
+ * This finds data in the global data that has been previously added with @ref ecore_thread_global_data_add
+ * This function will return NULL in any case but success.
+ * Use @p seconds to specify the amount of time to wait.  Use > 0 for an actual wait time, 0 to not wait, and < 0 to wait indefinitely.
+ * @note Keep in mind that the data returned can be used by multiple threads at a time, so you will most likely want to mutex
+ * if you will be doing anything with it.
+ */
+EAPI void *
+ecore_thread_global_data_wait(const char *key, double seconds)
+{
+   double time = 0;
+   Ecore_Thread_Data *ret = NULL;
+   if (!key)
+     return NULL;
+#ifdef EFL_HAVE_PTHREAD
+   if (!_ecore_thread_global_hash)
+     return NULL;
+   if (seconds > 0)
+     time = ecore_time_get() + seconds;
+
+   while (1)
+     {
+        struct timespec t = { 0, 0 };
+
+        t.tv_sec = (long int)time;
+        t.tv_nsec = (long int)((time - (double)t.tv_sec) * 1000000000);
+        pthread_rwlock_rdlock(&_ecore_thread_global_hash_lock);
+        ret = eina_hash_find(_ecore_thread_global_hash, key);
+        pthread_rwlock_unlock(&_ecore_thread_global_hash_lock);
+        if ((ret) || (!seconds) || ((seconds > 0) && (time <= ecore_time_get())))
+          break;
+        pthread_mutex_lock(&_ecore_thread_global_hash_mutex);
+        pthread_cond_timedwait(&_ecore_thread_global_hash_cond, &_ecore_thread_global_hash_mutex, &t);
+        pthread_mutex_unlock(&_ecore_thread_global_hash_mutex);
+     }
+   if (ret) return ret->data;
+   return NULL;
+#else
+   return NULL;
+#endif
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/ecore_time.c b/tests/suite/ecore/src/lib/ecore_time.c
new file mode 100644 (file)
index 0000000..1fbb478
--- /dev/null
@@ -0,0 +1,156 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#include <time.h>
+
+#ifdef HAVE_CLOCK_GETTIME
+static clockid_t _ecore_time_clock_id = -1;
+#endif
+double _ecore_time_loop_time = -1.0;
+
+/**
+ * Retrieves the current system time as a floating point value in seconds.
+ *
+ * This uses a monotonic clock and thus never goes back in time while
+ * machine is live (even if user changes time or timezone changes,
+ * however it may be reset whenever the machine is restarted).
+ *
+ * @see ecore_loop_time_get().
+ * @see ecore_time_unix_get().
+ *
+ * @return The number of seconds. Start time is not defined (it may be
+ *         when the machine was booted, unix time, etc), all it is
+ *         defined is that it never goes backwards (unless you got big critical
+ *         messages when the application started).
+ * @ingroup Ecore_Time_Group
+ */
+EAPI double
+ecore_time_get(void)
+{
+#ifdef HAVE_CLOCK_GETTIME
+   struct timespec t;
+
+   if (EINA_UNLIKELY(_ecore_time_clock_id < 0))
+      return ecore_time_unix_get();
+
+   if (EINA_UNLIKELY(clock_gettime(_ecore_time_clock_id, &t)))
+     {
+        CRIT("Cannot get current time.");
+        /* Try to at least return the latest value retrieved*/
+        return _ecore_time_loop_time;
+     }
+
+   return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
+#else
+   return ecore_time_unix_get();
+#endif
+}
+
+/**
+ * Retrieves the current UNIX time as a floating point value in seconds.
+ *
+ * @see ecore_time_get().
+ * @see ecore_loop_time_get().
+ *
+ * @return  The number of seconds since 12.00AM 1st January 1970.
+ * @ingroup Ecore_Time_Group
+ */
+EAPI double
+ecore_time_unix_get(void)
+{
+#ifdef HAVE_EVIL
+  return evil_time_get();
+#else
+# ifdef HAVE_GETTIMEOFDAY
+   struct timeval timev;
+
+   gettimeofday(&timev, NULL);
+   return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000);
+# else
+#  error "Your platform isn't supported yet"
+# endif
+#endif
+}
+
+/**
+ * Retrieves the time at which the last loop stopped waiting for timeouts or
+ * events.
+ *
+ * This gets the time that the main loop ceased waiting for timouts and/or
+ * events to come in or for signals or any other interrupt source. This should
+ * be considered a reference point for all time based activity that should
+ * calculate its timepoint from the return of ecore_loop_time_get(). Use this
+ * UNLESS you absolutely must get the current actual timepoint - then use
+ * ecore_time_get(). Note that this time is meant to be used as relative to
+ * other times obtained on this run. If you need absolute time references, use
+ * ecore_time_unix_get() instead.
+ *
+ * This function can be called before any loop has ever been run, but either
+ * ecore_init() or ecore_time_get() must have been called once.
+ *
+ * @return The number of seconds. Start time is not defined (it may be
+ *         when the machine was booted, unix time, etc), all it is
+ *         defined is that it never goes backwards (unless you got big critical
+ *         messages when the application started).
+ * @ingroup Ecore_Time_Group
+ */
+EAPI double
+ecore_loop_time_get(void)
+{
+   return _ecore_time_loop_time;
+}
+
+
+/**********************   Internal methods   ********************************/
+
+/* TODO: Documentation says "All  implementations  support  the  system-wide
+ * real-time clock, which is identified by CLOCK_REALTIME. Check if the fallback
+ * to unix time (without specifying the resolution) might be removed
+ */
+void
+_ecore_time_init(void)
+{
+#ifdef HAVE_CLOCK_GETTIME
+   struct timespec t;
+
+   if (_ecore_time_clock_id != -1) return;
+
+   if (!clock_gettime(CLOCK_MONOTONIC, &t))
+     {
+        _ecore_time_clock_id = CLOCK_MONOTONIC;
+        DBG("using CLOCK_MONOTONIC.");
+     }
+   else if (!clock_gettime(CLOCK_REALTIME, &t))
+     {
+        /* may go backwards */
+        _ecore_time_clock_id = CLOCK_REALTIME;
+        WRN("CLOCK_MONOTONIC not available. Fallback to CLOCK_REALTIME.");
+     }
+   else
+     {
+        _ecore_time_clock_id = -2;
+        CRIT("Cannot get a valid clock_gettime() clock id! "
+             "Fallback to unix time.");
+     }
+#else
+# warning "Your platform isn't supported yet"
+   CRIT("Platform does not support clock_gettime. "
+        "Fallback to unix time.");
+#endif
+
+   _ecore_time_loop_time = ecore_time_get();
+}
diff --git a/tests/suite/ecore/src/lib/ecore_timer.c b/tests/suite/ecore/src/lib/ecore_timer.c
new file mode 100644 (file)
index 0000000..fd6c64e
--- /dev/null
@@ -0,0 +1,591 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+
+struct _Ecore_Timer
+{
+   EINA_INLIST;
+   ECORE_MAGIC;
+   double          in;
+   double          at;
+   double          pending;
+   Ecore_Task_Cb   func;
+   void           *data;
+
+   int             references;
+   unsigned char   delete_me : 1;
+   unsigned char   just_added : 1;
+   unsigned char   frozen : 1;
+};
+
+
+static void _ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data);
+
+static int          timers_added = 0;
+static int          timers_delete_me = 0;
+static Ecore_Timer *timers = NULL;
+static Ecore_Timer *timer_current = NULL;
+static Ecore_Timer *suspended = NULL;
+static double       last_check = 0.0;
+static double       precision = 10.0 / 1000000.0;
+
+/**
+ * @defgroup Ecore_Time_Group Ecore Time Functions
+ *
+ * Functions that deal with time.  These functions include those that simply
+ * retrieve it in a given format, and those that create events based on it.
+ */
+
+/**
+ * Retrieves the current precision used by timer infrastructure.
+ *
+ * @see ecore_timer_precision_set()
+ */
+EAPI double
+ecore_timer_precision_get(void)
+{
+   return precision;
+}
+
+/**
+ * Sets the precision to be used by timer infrastructure.
+ *
+ * When system calculates time to expire the next timer we'll be able
+ * to delay the timer by the given amount so more timers will fit in
+ * the same dispatch, waking up the system less often and thus being
+ * able to save power.
+ *
+ * Be aware that kernel may delay delivery even further, these delays
+ * are always possible due other tasks having higher priorities or
+ * other scheduler policies.
+ *
+ * Example:
+ *  We have 2 timers, one that expires in a 2.0s and another that
+ *  expires in 2.1s, if precision is 0.1s, then the Ecore will request
+ *  for the next expire to happen in 2.1s and not 2.0s and another one
+ *  of 0.1 as it would before.
+ *
+ * @note Ecore is smart enough to see if there are timers in the
+ * precision range, if it does not, in our example if no second timer
+ * in (T + precision) existed, then it would use the minimum timeout.
+ *
+ * @param value allowed introduced timeout delay, in seconds.
+ */
+EAPI void
+ecore_timer_precision_set(double value)
+{
+   if (value < 0.0)
+     {
+        ERR("Precision %f less than zero, ignored", value);
+        return;
+     }
+   precision = value;
+}
+
+/**
+ * Creates a timer to call the given function in the given period of time.
+ * @param   in   The interval in seconds.
+ * @param   func The given function.  If @p func returns 1, the timer is
+ *               rescheduled for the next interval @p in.
+ * @param   data Data to pass to @p func when it is called.
+ * @return  A timer object on success.  @c NULL on failure.
+ * @ingroup Ecore_Time_Group
+ *
+ * This function adds a timer and returns its handle on success and NULL on
+ * failure. The function @p func will be called every @p in seconds. The
+ * function will be passed the @p data pointer as its parameter.
+ *
+ * When the timer @p func is called, it must return a value of either 1
+ * (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL).
+ * If it returns 1, it will be called again at the next tick, or if it returns
+ * 0 it will be deleted automatically making any references/handles for it
+ * invalid.
+ */
+EAPI Ecore_Timer *
+ecore_timer_add(double in, Ecore_Task_Cb func, const void *data)
+{
+   double now;
+   Ecore_Timer *timer;
+
+   if (!func) return NULL;
+   if (in < 0.0) in = 0.0;
+   timer = calloc(1, sizeof(Ecore_Timer));
+   if (!timer) return NULL;
+   ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER);
+   now = ecore_time_get();
+   _ecore_timer_set(timer, now + in, in, func, (void *)data);
+   return timer;
+}
+
+/**
+ * Creates a timer to call the given function in the given period of time.
+ * @param   in   The interval in seconds from current loop time.
+ * @param   func The given function.  If @p func returns 1, the timer is
+ *               rescheduled for the next interval @p in.
+ * @param   data Data to pass to @p func when it is called.
+ * @return  A timer object on success.  @c NULL on failure.
+ * @ingroup Ecore_Time_Group
+ *
+ * This is the same as ecore_timer_add(), but "now" is the time from
+ * ecore_loop_time_get() not ecore_time_get() as ecore_timer_add() uses. See
+ * ecore_timer_add() for more details.
+ */
+EAPI Ecore_Timer *
+ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data)
+{
+   double now;
+   Ecore_Timer *timer;
+
+   if (!func) return NULL;
+   if (in < 0.0) in = 0.0;
+   timer = calloc(1, sizeof(Ecore_Timer));
+   if (!timer) return NULL;
+   ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER);
+   now = ecore_loop_time_get();
+   _ecore_timer_set(timer, now + in, in, func, (void *)data);
+   return timer;
+}
+
+/**
+ * Delete the specified timer from the timer list.
+ * @param   timer The timer to delete.
+ * @return  The data pointer set for the timer when @ref ecore_timer_add was
+ *          called.  @c NULL is returned if the function is unsuccessful.
+ * @ingroup Ecore_Time_Group
+ *
+ * Note: @p timer must be a valid handle. If the timer function has already
+ * returned 0, the handle is no longer valid (and does not need to be delete).
+ */
+EAPI void *
+ecore_timer_del(Ecore_Timer *timer)
+{
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_del");
+        return NULL;
+     }
+
+   if (timer->frozen && !timer->references)
+     {
+        void *data = timer->data;
+
+        suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
+
+        if (timer->delete_me)
+          timers_delete_me--;
+
+        free(timer);
+        return data;
+     }
+
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(timer->delete_me, NULL);
+   timer->delete_me = 1;
+   timers_delete_me++;
+   return timer->data;
+}
+
+/**
+ * Change the interval the timer ticks of. If set during
+ * a timer call, this will affect the next interval.
+ *
+ * @param   timer The timer to change.
+ * @param   in    The interval in seconds.
+ * @ingroup Ecore_Time_Group
+ */
+EAPI void
+ecore_timer_interval_set(Ecore_Timer *timer, double in)
+{
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_interval_set");
+        return;
+     }
+   timer->in = in;
+}
+
+/**
+ * Get the interval the timer ticks on.
+ *
+ * @param   timer The timer to retrieve the interval from
+ * @return  The interval on success. -1 on failure.
+ * @ingroup Ecore_Time_Group
+ */
+EAPI double
+ecore_timer_interval_get(Ecore_Timer *timer)
+{
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_interval_get");
+        return -1.0;
+     }
+
+   return timer->in;
+}
+
+/**
+ * Add some delay for the next occurrence of a timer.
+ * This doesn't affect the interval of a timer.
+ *
+ * @param   timer The timer to change.
+ * @param   add   The dalay to add to the next iteration.
+ * @ingroup Ecore_Time_Group
+ */
+EAPI void
+ecore_timer_delay(Ecore_Timer *timer, double add)
+{
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_delay");
+        return;
+     }
+
+   if (timer->frozen)
+     {
+        timer->pending += add;
+     }
+   else
+     {
+        timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
+        _ecore_timer_set(timer, timer->at + add, timer->in, timer->func, timer->data);
+     }
+}
+
+/**
+ * Get the pending time regarding a timer.
+ *
+ * @param        timer The timer to learn from.
+ * @ingroup        Ecore_Time_Group
+ */
+EAPI double
+ecore_timer_pending_get(Ecore_Timer *timer)
+{
+   double        now;
+
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_pending_get");
+        return 0;
+     }
+
+   now = ecore_time_get();
+
+   if (timer->frozen)
+     return timer->pending;
+   return timer->at - now;
+}
+
+/**
+ *
+ *
+ */
+EAPI void
+ecore_timer_freeze(Ecore_Timer *timer)
+{
+   double now;
+
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_freeze");
+        return ;
+     }
+
+   /* Timer already frozen */
+   if (timer->frozen)
+     return ;
+
+   timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
+   suspended = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
+
+   now = ecore_time_get();
+
+   timer->pending = timer->at - now;
+   timer->at = 0.0;
+   timer->frozen = 1;
+}
+
+EAPI void
+ecore_timer_thaw(Ecore_Timer *timer)
+{
+   double now;
+
+   if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
+     {
+        ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
+                         "ecore_timer_thaw");
+        return ;
+     }
+
+   /* Timer not frozen */
+   if (!timer->frozen)
+     return ;
+
+   suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
+   now = ecore_time_get();
+
+   _ecore_timer_set(timer, timer->pending + now, timer->in, timer->func, timer->data);
+}
+
+void
+_ecore_timer_shutdown(void)
+{
+   Ecore_Timer *timer;
+
+   while ((timer = timers))
+     {
+        timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timers));
+        ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
+        free(timer);
+     }
+
+   while ((timer = suspended))
+     {
+        suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(suspended));
+        ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
+        free(timer);
+     }
+
+   timer_current = NULL;
+}
+
+void
+_ecore_timer_cleanup(void)
+{
+   Ecore_Timer *l;
+   int in_use = 0, todo = timers_delete_me, done = 0;
+
+   if (!timers_delete_me) return;
+   for (l = timers; l;)
+     {
+        Ecore_Timer *timer = l;
+
+        l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
+        if (timer->delete_me)
+          {
+             if (timer->references)
+               {
+                  in_use++;
+                  continue;
+               }
+             timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
+             ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
+             free(timer);
+             timers_delete_me--;
+             done++;
+             if (timers_delete_me == 0) return;
+          }
+     }
+   for (l = suspended; l;)
+     {
+        Ecore_Timer *timer = l;
+
+        l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
+        if (timer->delete_me)
+          {
+             if (timer->references)
+               {
+                  in_use++;
+                  continue;
+               }
+             suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
+             ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
+             free(timer);
+             timers_delete_me--;
+             done++;
+             if (timers_delete_me == 0) return;
+          }
+     }
+
+   if ((!in_use) && (timers_delete_me))
+     {
+        ERR("%d timers to delete, but they were not found!"
+            "Stats: todo=%d, done=%d, pending=%d, in_use=%d. "
+            "reset counter.",
+            timers_delete_me, todo, done, todo - done, in_use);
+        timers_delete_me = 0;
+     }
+}
+
+void
+_ecore_timer_enable_new(void)
+{
+   Ecore_Timer *timer;
+
+   if (!timers_added) return;
+   timers_added = 0;
+   EINA_INLIST_FOREACH(timers, timer) timer->just_added = 0;
+}
+
+int
+_ecore_timers_exists(void)
+{
+   Ecore_Timer *timer = timers;
+
+   while ((timer) && (timer->delete_me))
+     timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next;
+
+   return !!timer;
+}
+
+static inline Ecore_Timer *
+_ecore_timer_first_get(void)
+{
+   Ecore_Timer *timer = timers;
+
+   while ((timer) && ((timer->delete_me) || (timer->just_added)))
+     timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next;
+
+   return timer;
+}
+
+static inline Ecore_Timer *
+_ecore_timer_after_get(Ecore_Timer *base)
+{
+   Ecore_Timer *timer = (Ecore_Timer *) EINA_INLIST_GET(base)->next;
+   double maxtime = base->at + precision;
+
+   while ((timer) && ((timer->delete_me) || (timer->just_added)) && (timer->at <= maxtime))
+     timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next;
+
+   if ((!timer) || (timer->at > maxtime))
+     return NULL;
+
+   return timer;
+}
+
+double
+_ecore_timer_next_get(void)
+{
+   double now;
+   double in;
+   Ecore_Timer *first, *second;
+
+   first = _ecore_timer_first_get();
+   if (!first) return -1;
+
+   second = _ecore_timer_after_get(first);
+   if (second)
+     first = second;
+
+   now = ecore_loop_time_get();
+   in = first->at - now;
+   if (in < 0) in = 0;
+   return in;
+}
+
+static inline void
+_ecore_timer_reschedule(Ecore_Timer *timer, double when)
+{
+   if ((timer->delete_me) || (timer->frozen)) return;
+
+   timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
+
+   /* if the timer would have gone off more than 15 seconds ago,
+    * assume that the system hung and set the timer to go off
+    * timer->in from now. this handles system hangs, suspends
+    * and more, so ecore will only "replay" the timers while
+    * the system is suspended if it is suspended for less than
+    * 15 seconds (basically). this also handles if the process
+    * is stopped in a debugger or IO and other handling gets
+    * really slow within the main loop.
+    */
+   if ((timer->at + timer->in) < (when - 15.0))
+     _ecore_timer_set(timer, when + timer->in, timer->in, timer->func, timer->data);
+   else
+     _ecore_timer_set(timer, timer->at + timer->in, timer->in, timer->func, timer->data);
+}
+
+int
+_ecore_timer_call(double when)
+{
+   if (!timers) return 0;
+   if (last_check > when)
+     {
+        Ecore_Timer *timer;
+        /* User set time backwards */
+        EINA_INLIST_FOREACH(timers, timer) timer->at -= (last_check - when);
+     }
+   last_check = when;
+
+   if (!timer_current)
+     {
+        /* regular main loop, start from head */
+        timer_current = timers;
+     }
+   else
+     {
+        /* recursive main loop, continue from where we were */
+        Ecore_Timer *timer_old = timer_current;
+        timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
+        _ecore_timer_reschedule(timer_old, when);
+     }
+
+   while (timer_current)
+     {
+        Ecore_Timer *timer = timer_current;
+
+        if (timer->at > when)
+          {
+             timer_current = NULL; /* ended walk, next should restart. */
+             return 0;
+          }
+
+        if ((timer->just_added) || (timer->delete_me))
+          {
+             timer_current = (Ecore_Timer*)EINA_INLIST_GET(timer_current)->next;
+             continue;
+          }
+
+        timer->references++;
+        if (!timer->func(timer->data))
+          {
+             if (!timer->delete_me) ecore_timer_del(timer);
+          }
+        timer->references--;
+
+        if (timer_current) /* may have changed in recursive main loops */
+          timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
+
+        _ecore_timer_reschedule(timer, when);
+     }
+   return 0;
+}
+
+static void
+_ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data)
+{
+   Ecore_Timer *t2;
+
+   timers_added = 1;
+   timer->at = at;
+   timer->in = in;
+   timer->func = func;
+   timer->data = data;
+   timer->just_added = 1;
+   timer->frozen = 0;
+   timer->pending = 0.0;
+   if (timers)
+     {
+        EINA_INLIST_REVERSE_FOREACH(EINA_INLIST_GET(timers), t2)
+          {
+             if (timer->at > t2->at)
+               {
+                  timers = (Ecore_Timer *) eina_inlist_append_relative(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer), EINA_INLIST_GET(t2));
+                  return;
+               }
+          }
+     }
+   timers = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
+}
diff --git a/tests/suite/ecore/src/lib/eina_accessor.c b/tests/suite/ecore/src/lib/eina_accessor.c
new file mode 100644 (file)
index 0000000..cb20cab
--- /dev/null
@@ -0,0 +1,267 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_accessor.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char EINA_MAGIC_ACCESSOR_STR[] = "Eina Accessor";
+
+#define EINA_MAGIC_CHECK_ACCESSOR(d)                            \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ACCESSOR)) {              \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_ACCESSOR); }                  \
+     } while(0)
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the accessor module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the accessor module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_accessor_init(void)
+{
+   return eina_magic_string_set(EINA_MAGIC_ACCESSOR, EINA_MAGIC_ACCESSOR_STR);
+}
+
+/**
+ * @internal
+ * @brief Shut down the accessor module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the accessor module set up by
+ * eina_accessor_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_accessor_shutdown(void)
+{
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Accessor_Group Accessor Functions
+ *
+ * @brief These functions manage accessor on containers.
+ *
+ * These functions allow to access elements of a container in a
+ * generic way, without knowing which container is used (a bit like
+ * iterators in the C++ STL). Accessors allows random access (that is, any
+ * element in the container). For sequential access, see
+ * @ref Eina_Iterator_Group.
+ *
+ * An accessor is created from container data types, so no creation
+ * function is available here. An accessor is deleted with
+ * eina_accessor_free(). To get the data of an element at a given
+ * position, use eina_accessor_data_get(). To call a function on
+ * chosen elements of a container, use eina_accessor_over().
+ *
+ * @{
+ */
+
+/**
+ * @brief Free an accessor.
+ *
+ * @param accessor The accessor to free.
+ *
+ * This function frees @p accessor if it is not @c NULL;
+ */
+EAPI void
+eina_accessor_free(Eina_Accessor *accessor)
+{
+   EINA_MAGIC_CHECK_ACCESSOR(accessor);
+   EINA_SAFETY_ON_NULL_RETURN(accessor);
+   EINA_SAFETY_ON_NULL_RETURN(accessor->free);
+   accessor->free(accessor);
+}
+
+/**
+ * @brief Return the container of an accessor.
+ *
+ * @param accessor The accessor.
+ * @return The container which created the accessor.
+ *
+ * This function returns the container which created @p accessor. If
+ * @p accessor is @c NULL, this function returns @c NULL.
+ */
+EAPI void *
+eina_accessor_container_get(Eina_Accessor *accessor)
+{
+   EINA_MAGIC_CHECK_ACCESSOR(accessor);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(accessor,                NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(accessor->get_container, NULL);
+   return accessor->get_container(accessor);
+}
+
+/**
+ * @brief Retrieve the data of an accessor at a given position.
+ *
+ * @param accessor The accessor.
+ * @param position The position of the element.
+ * @param data The pointer that stores the data to retrieve.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function retrieves the data of the element pointed by
+ * @p accessor at the porition @p position, and stores it in
+ * @p data. If @p accessor is @c NULL or if an error occurred,
+ * #EINA_FALSE is returned, otherwise EINA_TRUE is returned.
+ */
+EAPI Eina_Bool
+eina_accessor_data_get(Eina_Accessor *accessor,
+                       unsigned int position,
+                       void **data)
+{
+   EINA_MAGIC_CHECK_ACCESSOR(accessor);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(accessor,         EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(accessor->get_at, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data,             EINA_FALSE);
+   return accessor->get_at(accessor, position, data);
+}
+
+/**
+ * @brief Iterate over the container and execute a callback on chosen elements.
+ *
+ * @param accessor The accessor.
+ * @param cb The callback called on the chosen elements.
+ * @param start The position of the first element.
+ * @param end The position of the last element.
+ * @param fdata The data passed to the callback.
+ *
+ * This function iterates over the elements pointed by @p accessor,
+ * starting from the element at position @p start and ending to the
+ * element at position @p end. For Each element, the callback
+ * @p cb is called with the data @p fdata. If @p accessor is @c NULL
+ * or if @p start is greter or equal than @p end, the function returns
+ * immediately.
+ */
+EAPI void
+eina_accessor_over(Eina_Accessor *accessor,
+                   Eina_Each_Cb cb,
+                   unsigned int start,
+                   unsigned int end,
+                   const void *fdata)
+{
+   const void *container;
+   void *data;
+   unsigned int i;
+
+   EINA_MAGIC_CHECK_ACCESSOR(accessor);
+   EINA_SAFETY_ON_NULL_RETURN(accessor);
+   EINA_SAFETY_ON_NULL_RETURN(accessor->get_container);
+   EINA_SAFETY_ON_NULL_RETURN(accessor->get_at);
+   EINA_SAFETY_ON_NULL_RETURN(cb);
+   EINA_SAFETY_ON_FALSE_RETURN(start < end);
+
+   if (!eina_accessor_lock(accessor))
+      return ;
+
+   container = accessor->get_container(accessor);
+   for (i = start; i < end && accessor->get_at(accessor, i, &data) == EINA_TRUE;
+        ++i)
+      if (cb(container, data, (void *)fdata) != EINA_TRUE)
+        goto on_exit;
+
+ on_exit:
+   (void) eina_accessor_unlock(accessor);
+}
+
+/**
+ * @brief Lock the container of the accessor.
+ *
+ * @param accessor The accessor.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * If the container of the @p accessor permit it, it will be locked.
+ * If @p accessor is @c NULL or if a problem occurred, #EINA_FALSE is
+ * returned, otherwise #EINA_TRUE is returned. If the container
+ * is not lockable, it will return EINA_TRUE.
+ */
+EAPI Eina_Bool
+eina_accessor_lock(Eina_Accessor *accessor)
+{
+   EINA_MAGIC_CHECK_ACCESSOR(accessor);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(accessor, EINA_FALSE);
+
+   if (accessor->lock)
+      return accessor->lock(accessor);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Unlock the container of the accessor.
+ *
+ * @param accessor The accessor.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * If the container of the @p accessor permit it and was previously
+ * locked, it will be unlocked. If @p accessor is @c NULL or if a
+ * problem occurred, #EINA_FALSE is returned, otherwise #EINA_TRUE
+ * is returned. If the container is not lockable, it will return
+ * EINA_TRUE.
+ */
+EAPI Eina_Bool
+eina_accessor_unlock(Eina_Accessor *accessor)
+{
+   EINA_MAGIC_CHECK_ACCESSOR(accessor);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(accessor, EINA_FALSE);
+
+   if (accessor->unlock)
+      return accessor->unlock(accessor);
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_array.c b/tests/suite/ecore/src/lib/eina_array.c
new file mode 100644 (file)
index 0000000..bd71fc9
--- /dev/null
@@ -0,0 +1,727 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * @page tutorial_array_page Array Tutorial
+ *
+ * The Array data type is allow the storage of data like a C array.
+ * It is designed such that the access to its element is very fast.
+ * But the addition or removal can be done only at the end of the
+ * array. To add or remove an element at any location, the Eina
+ * @ref Eina_List_Group is the correct container is the correct one.
+ *
+ * @section tutorial_error_basic_usage Basic Usage
+ *
+ * An array must created with eina_array_new(). That function
+ * takes an integer as parameter, which is the count of pointers to
+ * add when increasing the array size. Once the array is not used
+ * anymore, it must be destroyed with eina_array_free().
+ *
+ * To append data at the end of the array, the function
+ * eina_array_push() must be used. To remove the data at the end of
+ * the array, eina_array_pop() must be used. Once the array is filled,
+ * one can check its elements by iterating over it. A while loop and
+ * eina_array_data_get() can be used, or else one can use the
+ * predefined macro EINA_ARRAY_ITER_NEXT(). To free all the elements,
+ * a while loop can be used with eina_array_count_get(). Here is an
+ * example of use:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ * #include <string.h>
+ *
+ * #include <eina_array.h>
+ *
+ * int main(void)
+ * {
+ *     const char *strings[] = {
+ *         "first string",
+ *         "second string",
+ *         "third string",
+ *         "fourth string"
+ *     };
+ *     Eina_Array         *array;
+ *     char               *item;
+ *     Eina_Array_Iterator iterator;
+ *     unsigned int        i;
+ *
+ *     if (!eina_init())
+ *     {
+ *         printf ("Error during the initialization of eina\n");
+ *         return EXIT_FAILURE;
+ *     }
+ *
+ *     array = eina_array_new(16);
+ *     if (!array)
+ *         goto shutdown;
+ *
+ *     for (i = 0; i < 4; i++)
+ *     {
+ *         eina_array_push(array, strdup(strings[i]));
+ *     }
+ *
+ *     printf("array count: %d\n", eina_array_count_get(array));
+ *     EINA_ARRAY_ITER_NEXT(array, i, item, iterator)
+ *     {
+ *         printf("item #%d: %s\n", i, item);
+ *     }
+ *
+ *     while (eina_array_count_get(array))
+ *     {
+ *         void *data;
+ *
+ *         data = eina_array_pop(array);
+ *         free(data);
+ *     }
+ *
+ *     eina_array_free(array);
+ *     eina_shutdown();
+ *
+ *     return EXIT_SUCCESS;
+ *
+ *   shutdown:
+ *     eina_shutdown();
+ *
+ *     return EXIT_FAILURE;
+ * }
+ * @endcode
+ *
+ * To be continued
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_error.h"
+#include "eina_log.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_array.h"
+
+/*============================================================================*
+ *                                  Local                                     *
+ *============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char EINA_MAGIC_ARRAY_STR[] = "Eina Array";
+static const char EINA_MAGIC_ARRAY_ITERATOR_STR[] = "Eina Array Iterator";
+static const char EINA_MAGIC_ARRAY_ACCESSOR_STR[] = "Eina Array Accessor";
+
+#define EINA_MAGIC_CHECK_ARRAY(d)                       \
+   do {                                                  \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ARRAY)) {        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_ARRAY); }            \
+     } while (0)
+
+#define EINA_MAGIC_CHECK_ARRAY_ITERATOR(d, ...)                 \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ARRAY_ITERATOR))       \
+          {                                                        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_ARRAY_ITERATOR);        \
+             return __VA_ARGS__;                                   \
+          }                                                        \
+     } while (0)
+
+#define EINA_MAGIC_CHECK_ARRAY_ACCESSOR(d, ...)                 \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ARRAY_ACCESSOR))       \
+          {                                                        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_ACCESSOR);              \
+             return __VA_ARGS__;                                   \
+          }                                                        \
+     } while (0)
+
+
+typedef struct _Eina_Iterator_Array Eina_Iterator_Array;
+struct _Eina_Iterator_Array
+{
+   Eina_Iterator iterator;
+
+   const Eina_Array *array;
+   unsigned int index;
+
+   EINA_MAGIC
+};
+
+typedef struct _Eina_Accessor_Array Eina_Accessor_Array;
+struct _Eina_Accessor_Array
+{
+   Eina_Accessor accessor;
+   const Eina_Array *array;
+   EINA_MAGIC
+};
+
+static int _eina_array_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_array_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_array_log_dom, __VA_ARGS__)
+
+static void        eina_array_iterator_free(Eina_Iterator_Array *it) EINA_ARG_NONNULL(1);
+static Eina_Array *eina_array_iterator_get_container(Eina_Iterator_Array *it) EINA_ARG_NONNULL(1);
+static Eina_Bool   eina_array_iterator_next(Eina_Iterator_Array *it,
+                                            void **data) EINA_ARG_NONNULL(1);
+
+static Eina_Bool   eina_array_accessor_get_at(Eina_Accessor_Array *it,
+                                              unsigned int idx,
+                                              void **data) EINA_ARG_NONNULL(1);
+static Eina_Array *eina_array_accessor_get_container(Eina_Accessor_Array *it) EINA_ARG_NONNULL(1);
+static void        eina_array_accessor_free(Eina_Accessor_Array *it) EINA_ARG_NONNULL(1);
+
+static Eina_Bool
+eina_array_iterator_next(Eina_Iterator_Array *it, void **data)
+{
+   EINA_MAGIC_CHECK_ARRAY_ITERATOR(it, EINA_FALSE);
+
+   if (!(it->index < eina_array_count_get(it->array)))
+      return EINA_FALSE;
+
+   if (data)
+      *data = eina_array_data_get(it->array, it->index);
+
+   it->index++;
+   return EINA_TRUE;
+}
+
+static Eina_Array *
+eina_array_iterator_get_container(Eina_Iterator_Array *it)
+{
+   EINA_MAGIC_CHECK_ARRAY_ITERATOR(it, NULL);
+   return (Eina_Array *)it->array;
+}
+
+static void
+eina_array_iterator_free(Eina_Iterator_Array *it)
+{
+   EINA_MAGIC_CHECK_ARRAY_ITERATOR(it);
+   MAGIC_FREE(it);
+}
+
+static Eina_Bool
+eina_array_accessor_get_at(Eina_Accessor_Array *it,
+                           unsigned int idx,
+                           void **data)
+{
+   EINA_MAGIC_CHECK_ARRAY_ACCESSOR(it, EINA_FALSE);
+
+   if (!(idx < eina_array_count_get(it->array)))
+      return EINA_FALSE;
+
+   if (data)
+      *data = eina_array_data_get(it->array, idx);
+
+   return EINA_TRUE;
+}
+
+static Eina_Array *
+eina_array_accessor_get_container(Eina_Accessor_Array *it)
+{
+   EINA_MAGIC_CHECK_ARRAY_ACCESSOR(it, NULL);
+   return (Eina_Array *)it->array;
+}
+
+static void
+eina_array_accessor_free(Eina_Accessor_Array *it)
+{
+   EINA_MAGIC_CHECK_ARRAY_ACCESSOR(it);
+   MAGIC_FREE(it);
+}
+
+EAPI Eina_Bool
+eina_array_grow(Eina_Array *array)
+{
+   void **tmp;
+   unsigned int total;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE);
+
+   EINA_MAGIC_CHECK_ARRAY(array);
+
+   total = array->total + array->step;
+        eina_error_set(0);
+   tmp = realloc(array->data, sizeof (void *) * total);
+   if (EINA_UNLIKELY(!tmp))
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return 0;
+     }
+
+   array->total = total;
+   array->data = tmp;
+
+   return 1;
+}
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+ *                                 Global                                     *
+ *============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the array module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the error and magic modules or Eina. It is
+ * called by eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_array_init(void)
+{
+   _eina_array_log_dom = eina_log_domain_register("eina_array",
+                                                  EINA_LOG_COLOR_DEFAULT);
+   if (_eina_array_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_array");
+        return EINA_FALSE;
+     }
+
+#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
+   EMS(EINA_MAGIC_ARRAY);
+   EMS(EINA_MAGIC_ARRAY_ITERATOR);
+   EMS(EINA_MAGIC_ARRAY_ACCESSOR);
+#undef EMS
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the array module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the array module set up by
+ * eina_array_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_array_shutdown(void)
+{
+   eina_log_domain_unregister(_eina_array_log_dom);
+   _eina_array_log_dom = -1;
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+ *                                   API                                      *
+ *============================================================================*/
+
+/**
+ * @addtogroup Eina_Array_Group Array
+ *
+ * @brief These functions provide array management.
+ *
+ * The Array data type in Eina is designed to have a very fast access to
+ * its data (compared to the Eina @ref Eina_List_Group). On the other hand,
+ * data can be added or removed only at the end of the array. To insert
+ * data at any place, the Eina @ref Eina_List_Group is the correct container
+ * to use.
+ *
+ * To use the array data type, eina_init() must be called before any
+ * other array functions. When eina is no more array function is used,
+ * eina_shutdown() must be called to free all the resources.
+ *
+ * An array must be created with eina_array_new(). It allocated all
+ * the necessary data for an array. When not needed anymore, an array
+ * is freed with eina_array_free(). This function does not free any
+ * allocated memory used to store the data of each element. For that,
+ * just iterate over the array to free them. A convenient way to do
+ * that is by using #EINA_ARRAY_ITER_NEXT. An example of code is given
+ * in the description of this macro.
+ *
+ * @warning All the other functions do not check if the used array is
+ * valid or not. It's up to the user to be sure of that. It is
+ * designed like that for performance reasons.
+ *
+ * The usual features of an array are classic ones: to append an
+ * element, use eina_array_push() and to remove the last element, use
+ * eina_array_pop(). To retrieve the element at a given positin, use
+ * eina_array_data_get(). The number of elements can be retrieved with
+ * eina_array_count_get().
+ *
+ * For more information, you can look at the @ref tutorial_array_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new array.
+ *
+ * @param step The count of pointers to add when increasing the array size.
+ * @return @c NULL on failure, non @c NULL otherwise.
+ *
+ * This function creates a new array. When adding an element, the array
+ * allocates @p step elements. When that buffer is full, then adding
+ * another element will increase the buffer of @p step elements again.
+ *
+ * This function return a valid array on success, or @c NULL if memory
+ * allocation fails. In that case, the error is set to
+ * #EINA_ERROR_OUT_OF_MEMORY.
+ */
+EAPI Eina_Array *
+eina_array_new(unsigned int step)
+{
+   Eina_Array *array;
+
+   eina_error_set(0);
+   array = malloc(sizeof (Eina_Array));
+   if (!array)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(array, EINA_MAGIC_ARRAY);
+
+   array->version = EINA_ARRAY_VERSION;
+   array->data = NULL;
+   array->total = 0;
+   array->count = 0;
+   array->step = step;
+
+   return array;
+}
+
+/**
+ * @brief Free an array.
+ *
+ * @param array The array to free.
+ *
+ * This function frees @p array. It calls first eina_array_flush() then
+ * free the memory of the pointer. It does not free the memory
+ * allocated for the elements of @p array. To free them, use
+ * #EINA_ARRAY_ITER_NEXT. For performance reasons, there is no check
+ * of @p array.
+ */
+EAPI void
+eina_array_free(Eina_Array *array)
+{
+   eina_array_flush(array);
+
+   EINA_SAFETY_ON_NULL_RETURN(array);
+   EINA_MAGIC_CHECK_ARRAY(array);
+   MAGIC_FREE(array);
+}
+
+/**
+ * @brief Set the step of an array.
+ *
+ * @param array The array.
+ * @param sizeof_eina_array Should be the value returned by sizeof(Eina_Array).
+ * @param step The count of pointers to add when increasing the array size.
+ *
+ * This function sets the step of @p array to @p step. For performance
+ * reasons, there is no check of @p array. If it is @c NULL or
+ * invalid, the program may crash. This function should be called when
+ * the array is not initialized.
+ */
+EAPI void
+eina_array_step_set(Eina_Array *array,
+                   unsigned int sizeof_eina_array,
+                   unsigned int step)
+{
+   EINA_SAFETY_ON_NULL_RETURN(array);
+
+   if (sizeof (Eina_Array) != sizeof_eina_array)
+     {
+       ERR("Unknow Eina_Array size ! Got %i, expected %i !\n",
+           sizeof_eina_array,
+           (int) sizeof (Eina_Array));
+       /* Force memory to zero to provide a small layer of security */
+       memset(array, 0, sizeof_eina_array);
+       return ;
+     }
+
+   array->version = EINA_ARRAY_VERSION;
+   array->data = NULL;
+   array->total = 0;
+   array->count = 0;
+   array->step = step;
+   EINA_MAGIC_SET(array, EINA_MAGIC_ARRAY);
+}
+
+/**
+ * @brief Clean an array.
+ *
+ * @param array The array to clean.
+ *
+ * This function sets the count member of @p array to 0. For
+ * performance reasons, there is no check of @p array. If it is
+ * @c NULL or invalid, the program may crash.
+ */
+EAPI void
+eina_array_clean(Eina_Array *array)
+{
+   EINA_SAFETY_ON_NULL_RETURN(array);
+   EINA_MAGIC_CHECK_ARRAY(array);
+
+   array->count = 0;
+}
+
+/**
+ * @brief Flush an array.
+ *
+ * @param array The array to flush.
+ *
+ * This function sets the count and total members of @p array to 0,
+ * frees and set to NULL its data member. For performance reasons,
+ * there is no check of @p array. If it is @c NULL or invalid, the
+ * program may crash.
+ */
+EAPI void
+eina_array_flush(Eina_Array *array)
+{
+   EINA_SAFETY_ON_NULL_RETURN(array);
+   EINA_MAGIC_CHECK_ARRAY(array);
+
+   array->count = 0;
+   array->total = 0;
+
+   if (!array->data)
+      return;
+
+   free(array->data);
+   array->data = NULL;
+}
+
+/**
+ * @brief Rebuild an array by specifying the data to keep.
+ *
+ * @param array The array.
+ * @param keep The functions which selects the data to keep.
+ * @param gdata The data to pass to the function keep.
+ * @return #EINA_TRUE on success, #EINA_FALSE oterwise.
+ *
+ * This function rebuilds @p array be specifying the elements to keep
+ * with the function @p keep. @p gdata is an additional data to pass
+ * to @p keep. For performance reasons, there is no check of @p
+ * array. If it is @c NULL or invalid, the program may crash.
+ *
+ * This function always return a valid array. If it wasn't able to
+ * remove items due to an allocation failure, it will return #EINA_FALSE
+ * and the error is set to #EINA_ERROR_OUT_OF_MEMORY.
+ */
+EAPI Eina_Bool
+eina_array_remove(Eina_Array *array, Eina_Bool (*keep)(void *data,
+                                                       void *gdata),
+                  void *gdata)
+{
+   void **tmp;
+   /* WARNING:
+      The algorithm does exit before using unitialized data. So compiler is
+      giving you a false positiv here too.
+    */
+   void *data = NULL;
+   unsigned int total = 0;
+   unsigned int limit;
+   unsigned int i;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(keep,  EINA_FALSE);
+   EINA_MAGIC_CHECK_ARRAY(array);
+
+   if (array->total == 0)
+      return EINA_TRUE;
+
+   for (i = 0; i < array->count; ++i)
+     {
+        data = eina_array_data_get(array, i);
+
+        if (keep(data, gdata) == EINA_FALSE)
+           break;
+     }
+   limit = i;
+   if (i < array->count)
+      ++i;
+
+   for (; i < array->count; ++i)
+     {
+        data = eina_array_data_get(array, i);
+
+        if (keep(data, gdata) == EINA_TRUE)
+           break;
+     }
+   /* Special case all objects that need to stay are at the beginning of the array. */
+   if (i == array->count)
+     {
+        array->count = limit;
+        if (array->count == 0)
+          {
+             free(array->data);
+             array->total = 0;
+             array->data = NULL;
+          }
+
+        return EINA_TRUE;
+     }
+
+        eina_error_set(0);
+   tmp = malloc(sizeof (void *) * array->total);
+   if (!tmp)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return EINA_FALSE;
+     }
+
+   memcpy(tmp, array->data, limit * sizeof(void *));
+   total = limit;
+
+   if (i < array->count)
+     {
+        tmp[total] = data;
+        total++;
+        ++i;
+     }
+
+   for (; i < array->count; ++i)
+     {
+        data = eina_array_data_get(array, i);
+
+        if (keep(data, gdata))
+          {
+             tmp[total] = data;
+             total++;
+          }
+     }
+
+   free(array->data);
+
+   /* If we do not keep any object in the array, we should have exited
+      earlier in test (i == array->count). */
+   assert(total != 0);
+
+   array->data = tmp;
+   array->count = total;
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Returned a new iterator associated to an array.
+ *
+ * @param array The array.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to
+ * @p array. If @p array is @c NULL or the count member of @p array is
+ * less or equal than 0, this function returns NULL. If the memory can
+ * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is
+ * set. Otherwise, a valid iterator is returned.
+ */
+EAPI Eina_Iterator *
+eina_array_iterator_new(const Eina_Array *array)
+{
+   Eina_Iterator_Array *it;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL);
+   EINA_MAGIC_CHECK_ARRAY(array);
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_Array));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(it,            EINA_MAGIC_ARRAY_ITERATOR);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->array = array;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(eina_array_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         eina_array_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(eina_array_iterator_free);
+
+   return &it->iterator;
+}
+
+/**
+ * @brief Returned a new accessor associated to an array.
+ *
+ * @param array The array.
+ * @return A new accessor.
+ *
+ * This function returns a newly allocated accessor associated to
+ * @p array. If @p array is @c NULL or the count member of @p array is
+ * less or equal than 0, this function returns NULL. If the memory can
+ * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is
+ * set. Otherwise, a valid accessor is returned.
+ */
+EAPI Eina_Accessor *
+eina_array_accessor_new(const Eina_Array *array)
+{
+   Eina_Accessor_Array *ac;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL);
+   EINA_MAGIC_CHECK_ARRAY(array);
+
+   eina_error_set(0);
+   ac = calloc(1, sizeof (Eina_Accessor_Array));
+   if (!ac)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(ac,            EINA_MAGIC_ARRAY_ACCESSOR);
+   EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR);
+
+   ac->array = array;
+
+   ac->accessor.version = EINA_ACCESSOR_VERSION;
+   ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_array_accessor_get_at);
+   ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(
+         eina_array_accessor_get_container);
+   ac->accessor.free = FUNC_ACCESSOR_FREE(eina_array_accessor_free);
+
+   return &ac->accessor;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_benchmark.c b/tests/suite/ecore/src/lib/eina_benchmark.c
new file mode 100644 (file)
index 0000000..5cd3fd3
--- /dev/null
@@ -0,0 +1,742 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * @page tutorial_benchmark_page Benchmark Tutorial
+ *
+ * The Benchmark module allows you to write easily benchmarks
+ * framework in a project for timing critical part and detect slow
+ * parts of code. In addition it automatically creates data files of
+ * these benchmark, as well as a gnuplot file which can display the
+ * comparison curves of the benchmarks.
+ *
+ * @section tutorial_benchmark_basic_usage Basic Usage
+ *
+ * To create a basic benchmark, you have to follow these steps:
+ *
+ * @li Create a new bechmark
+ * @li Write the functions that wraps the the functions you want to
+ * bechmark.
+ * @li Register these wrappers functions.
+ * @li Run the benchmark.
+ * @li Free the memory.
+ *
+ * Here is a basic example of bechmark which creates two functions
+ * that will be run. These functions just print a message.
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <Eina.h>
+ *
+ * static
+ * void work1(int request)
+ * {
+ *   printf ("work1 in progress... Request: %d\n", request);
+ * }
+ *
+ * static
+ * void work2(int request)
+ * {
+ *   printf ("work2 in progress... Request: %d\n", request);
+ * }
+ *
+ * int main()
+ * {
+ *   Eina_Benchmark *test;
+ *   Eina_Array     *ea;
+ *
+ *   if (!eina_init())
+ *     return EXIT_FAILURE;
+ *
+ *   test = eina_benchmark_new("test", "run");
+ *   if (!test)
+ *     goto shutdown_eina;
+ *
+ *   eina_benchmark_register(test, "work-1", EINA_BENCHMARK(work1), 200, 300, 10);
+ *   eina_benchmark_register(test, "work-2", EINA_BENCHMARK(work2), 100, 150, 5);
+ *
+ *   ea = eina_benchmark_run(test);
+ *
+ *   eina_benchmark_free(test);
+ *   eina_shutdown();
+ *
+ *   return EXIT_SUCCESS;
+ *
+ *  shutdown_eina:
+ *   eina_shutdown();
+ *
+ *   return EXIT_FAILURE;
+ * }
+ * @endcode
+ *
+ * As "test", "run" are passed to eina_benchmark_new() and as the tests
+ * "work-1" and "work-2" are registered, the data files
+ * bench_test_run.work-1.data and bench_test_run.work-2.data will be
+ * created after the eina_benchmark_run() call. They contain four
+ * columns. The file bench_test_run.work-1.data contains for example:
+ *
+ * @code
+ * # specimen      experiment time starting time   ending time
+ * 200     23632   2852446 2876078
+ * 210     6924    2883046 2889970
+ * 220     6467    2895962 2902429
+ * 230     6508    2908271 2914779
+ * 240     6278    2920610 2926888
+ * 250     6342    2932830 2939172
+ * 260     6252    2944954 2951206
+ * 270     6463    2956978 2963441
+ * 280     6347    2969548 2975895
+ * 290     6457    2981702 2988159
+ * @endcode
+ *
+ * The first column (specimen) is the integer passed to the work1()
+ * function when the test is run. The second column (experiment time)
+ * is the time, in nanosecond, that work1() takes. The third and
+ * fourth columnd are self-explicit.
+ *
+ * You can see that the integer passed work1() starts from 200 and
+ * finishes at 290, with a step of 10. These values are computed withe
+ * last 3 values passed to eina_benchmark_register(). See the document
+ * of that function for the detailed behavior.
+ *
+ * The gnuplot file will be named bench_test_run.gnuplot. Just run:
+ *
+ * @code
+ * gnuplot bench_test_run.gnuplot
+ * @endcode
+ *
+ * to create the graphic of the comparison curves. The image file is
+ * named output_test_run.png.
+ *
+ * @section tutorial_benchmark_advanced_usage More Advanced Usage
+ *
+ * In this section, several test will be created and run. The idea is
+ * exactly the same than in the previous section, but with some basic
+ * automatic way to run all the benchmarks. The following code
+ * benchmarks some Eina converts functions, and some Eina containers
+ * types:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ * #include <time.h>
+ *
+ * #include <Eina.h>
+ *
+ * static void bench_convert(Eina_Benchmark *bench);
+ * static void bench_container(Eina_Benchmark *bench);
+ *
+ * typedef struct _Benchmark_Case Benchmark_Case;
+ *
+ * struct _Benchmark_Case
+ * {
+ *    const char *bench_case;
+ *    void (*build)(Eina_Benchmark *bench);
+ * };
+ *
+ * static const Benchmark_Case benchmarks[] = {
+ *   { "Bench 1", bench_convert },
+ *   { "Bench 2", bench_container },
+ *   { NULL,      NULL }
+ * };
+ *
+ * static
+ * void convert1(int request)
+ * {
+ *   char tmp[128];
+ *   int i;
+ *
+ *   srand(time(NULL));
+ *
+ *   for (i = 0; i < request; ++i)
+ *     eina_convert_itoa(rand(), tmp);
+ * }
+ *
+ * static
+ * void convert2(int request)
+ * {
+ *   char tmp[128];
+ *   int i;
+ *
+ *   srand(time(NULL));
+ *
+ *   for (i = 0; i < request; ++i)
+ *     eina_convert_xtoa(rand(), tmp);
+ * }
+ *
+ * static void
+ * bench_convert(Eina_Benchmark *bench)
+ * {
+ *   eina_benchmark_register(bench, "convert-1", EINA_BENCHMARK(convert1), 200, 400, 10);
+ *   eina_benchmark_register(bench, "convert-2", EINA_BENCHMARK(convert2), 200, 400, 10);
+ * }
+ *
+ * static
+ * void array(int request)
+ * {
+ *   Eina_Array *array;
+ *   Eina_Array_Iterator it;
+ *   int *data;
+ *   int i;
+ *
+ *   srand(time(NULL));
+ *
+ *   array = eina_array_new(64);
+ *
+ *   for (i = 0; i < request; ++i)
+ *     {
+ *       data = (int *)malloc(sizeof(int));
+ *       if (!data) continue;
+ *       *data = rand();
+ *       eina_array_push(array, data);
+ *     }
+ *
+ *   EINA_ARRAY_ITER_NEXT(array, i, data, it)
+ *     free(data);
+ *
+ *   eina_array_free(array);
+ * }
+ *
+ * static
+ * void list(int request)
+ * {
+ *   Eina_List *l = NULL;
+ *   int *data;
+ *   int i;
+ *
+ *   srand(time(NULL));
+ *
+ *   for (i = 0; i < request; ++i)
+ *     {
+ *       data = (int *)malloc(sizeof(int));
+ *       if (!data) continue;
+ *       *data = rand();
+ *       l = eina_list_prepend(l, data);
+ *     }
+ *
+ *   while (l)
+ *     {
+ *       free(eina_list_data_get(l));
+ *       l = eina_list_remove_list(l, l);
+ *     }
+ * }
+ *
+ * static void
+ * bench_container(Eina_Benchmark *bench)
+ * {
+ *   eina_benchmark_register(bench, "array", EINA_BENCHMARK(array), 200, 300, 10);
+ *   eina_benchmark_register(bench, "list", EINA_BENCHMARK(list), 200, 300, 10);
+ * }
+ *
+ * int main()
+ * {
+ *   Eina_Benchmark *test;
+ *   Eina_Array     *ea;
+ *   unsigned int    i;
+ *
+ *   if (!eina_init())
+ *     return EXIT_FAILURE;
+ *
+ *   for (i = 0; benchmarks[i].bench_case != NULL; ++i)
+ *     {
+ *       test = eina_benchmark_new(benchmarks[i].bench_case, "Benchmark example");
+ *       if (!test)
+ *         continue;
+ *
+ *       benchmarks[i].build(test);
+ *
+ *       ea = eina_benchmark_run(test);
+ *
+ *       eina_benchmark_free(test);
+ *     }
+ *
+ *   eina_shutdown();
+ *
+ *   return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * gnuplot can be used to see how are performed the convert functions
+ * together, as well as how are performed the containers. So it is now
+ * easy to see that the hexadecimal convert function is faster than
+ * the decimal one, and that arrays are faster than lists.
+ *
+ * You can improve all that by executing automatically gnuplot in your
+ * program, or integrate the Eina benchmark framework in an autotooled
+ * project. See that
+ * <a href="http://trac.enlightenment.org/e/wiki/AutotoolsIntegration#Benchmark">page</a>
+ * for more informations.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_log.h"
+#include "eina_benchmark.h"
+#include "eina_inlist.h"
+#include "eina_list.h"
+#include "eina_counter.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_BENCHMARK_FILENAME_MASK "bench_%s_%s.gnuplot"
+#define EINA_BENCHMARK_DATA_MASK "bench_%s_%s.%s.data"
+
+typedef struct _Eina_Run Eina_Run;
+struct _Eina_Run
+{
+   EINA_INLIST;
+
+   Eina_Benchmark_Specimens cb;
+   const char *name;
+   int start;
+   int end;
+   int step;
+};
+
+struct _Eina_Benchmark
+{
+   const char *name;
+   const char *run;
+
+   Eina_Inlist *runs;
+   Eina_List *names;
+};
+
+static int _eina_benchmark_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_benchmark_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_benchmark_log_dom, __VA_ARGS__)
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the benchmark module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the benchmark module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_benchmark_init(void)
+{
+   _eina_benchmark_log_dom = eina_log_domain_register("eina_benchmark",
+                                                      EINA_LOG_COLOR_DEFAULT);
+   if (_eina_benchmark_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_benchmark");
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the benchmark module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the benchmark module set up by
+ * eina_benchmark_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_benchmark_shutdown(void)
+{
+   eina_log_domain_unregister(_eina_benchmark_log_dom);
+   _eina_benchmark_log_dom = -1;
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Benchmark_Group Benchmark
+ *
+ * These functions allow you to add benchmark framework in a project
+ * for timing critical part and detect slow parts of code. It is used
+ * in Eina to compare the time used by eina, glib, evas and ecore data
+ * types.
+ *
+ * To use the benchmark module, Eina must be initialized with
+ * eina_init() and later shut down with eina_shutdown(). A benchmark
+ * is created with eina_benchmark_new() and freed with
+ * eina_benchmark_free().
+ *
+ * eina_benchmark_register() adds a test to a benchmark. That test can
+ * be run a certain amount of times. Adding more than one test to be
+ * executed allows the comparison between several parts of a program,
+ * or different implementations.
+ *
+ * eina_benchmark_run() runs all the tests registered with
+ * eina_benchmark_register(). The amount of time of each test is
+ * written in a gnuplot file.
+ *
+ * For more information, you can look at the @ref tutorial_benchmark_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new array.
+ *
+ * @param name The name of the benchmark.
+ * @param run The name of the run.
+ * @return @c NULL on failure, non @c NULL otherwise.
+ *
+ * This function creates a new benchmark. @p name and @p run are used
+ * to name the gnuplot file that eina_benchmark_run() will create.
+ *
+ * This function return a valid benchmark on success, or @c NULL if
+ * memory allocation fails. In that case, the error is set to
+ * #EINA_ERROR_OUT_OF_MEMORY.
+ *
+ * When the new module is not needed anymore, use
+ * eina_benchmark_free() to free the allocated memory.
+ */
+EAPI Eina_Benchmark *
+eina_benchmark_new(const char *name, const char *run)
+{
+   Eina_Benchmark *new;
+
+        eina_error_set(0);
+   new = calloc(1, sizeof (Eina_Benchmark));
+   if (!new)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   new->name = name;
+   new->run = run;
+
+   return new;
+}
+
+/**
+ * @brief Free a benchmark object.
+ *
+ * @param bench The benchmark to free.
+ *
+ * This function removes all the benchmark tests that have been
+ * registered and frees @p bench. If @p bench is @c NULL, this
+ * function returns immediately.
+ */
+EAPI void
+eina_benchmark_free(Eina_Benchmark *bench)
+{
+   Eina_Array *names;
+
+   if (!bench)
+      return;
+
+   while (bench->runs)
+     {
+        Eina_Run *run = (Eina_Run *)bench->runs;
+
+        bench->runs = eina_inlist_remove(bench->runs, bench->runs);
+        free(run);
+     }
+
+   EINA_LIST_FREE(bench->names, names)
+   {
+      Eina_Array_Iterator it;
+      char *tmp;
+      unsigned int i;
+
+      EINA_ARRAY_ITER_NEXT(names, i, tmp, it)
+       free(tmp);
+
+      eina_array_free(names);
+   }
+
+   free(bench);
+}
+
+/**
+ * @brief Add a test to a benchmark.
+ *
+ * @param bench The benchmark.
+ * @param name The name of the test.
+ * @param bench_cb The test function to be called.
+ * @param count_start The start data to be passed to @p bench_cb.
+ * @param count_end The end data to be passed to @p bench_cb.
+ * @param count_step The step data to be passed to @p bench_cb.
+ *
+ * This function adds the test named @p name to @p benchmark. @p
+ * bench_cb is the function called when the test is executed. That
+ * test can be executed a certain amount of time. @p start, @p end and
+ * @p step define a loop with a step increment. The integer that is
+ * increasing by @p step from @p start to @p end is passed to @p
+ * bench_cb when eina_benchmark_run() is called.
+ *
+ * If @p bench is @c NULL, this function returns imediatly. If the
+ * allocation of the memory of the test to add fails, the error is set
+ * to #EINA_ERROR_OUT_OF_MEMORY.
+ */
+EAPI Eina_Bool
+eina_benchmark_register(Eina_Benchmark *bench,
+                        const char *name,
+                        Eina_Benchmark_Specimens bench_cb,
+                        int count_start,
+                        int count_end,
+                        int count_step)
+{
+   Eina_Run *run;
+
+   if (!bench)
+      return EINA_FALSE;
+
+   if (count_step == 0)
+      return EINA_FALSE;
+
+        eina_error_set(0);
+   run = calloc(1, sizeof (Eina_Run));
+   if (!run)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return EINA_FALSE;
+     }
+
+   run->cb = bench_cb;
+   run->name = name;
+   run->start = count_start;
+   run->end = count_end;
+   run->step = count_step;
+
+   bench->runs = eina_inlist_append(bench->runs, EINA_INLIST_GET(run));
+
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Run the benchmark tests that have been registered.
+ *
+ * @param bench The benchmark.
+ * @return The list of names of the test files.
+ *
+ * This function runs all the tests that as been registered with
+ * eina_benchmark_register() and save the result in a gnuplot
+ * file. The name of the file has the following format:
+ *
+ * @code
+ * bench_[name]_[run]%s.gnuplot
+ * @endcode
+ *
+ * where [name] and [run] are the values passed to
+ * eina_benchmark_new().
+ *
+ * Each registered test is executed and timed. The time is written to
+ * the gnuplot file. The number of times each test is executed is
+ * controlled by the parameters passed to eina_benchmark_register().
+ *
+ * If @p bench is @c NULL, this functions returns @c NULL
+ * immediately. Otherwise, it returns the list of the names of each
+ * test.
+ */
+EAPI Eina_Array *
+eina_benchmark_run(Eina_Benchmark *bench)
+{
+   FILE *main_script;
+   FILE *current_data;
+   Eina_Array *ea;
+   Eina_Run *run;
+   char *buffer;
+   Eina_Bool first = EINA_FALSE;
+   size_t length;
+
+   if (!bench)
+      return NULL;
+
+   length = strlen(EINA_BENCHMARK_FILENAME_MASK) + strlen(bench->name) + strlen(
+         bench->run);
+
+   buffer = alloca(sizeof (char) * length);
+   if (!buffer)
+      return NULL;
+
+   snprintf(buffer,
+            length,
+            EINA_BENCHMARK_FILENAME_MASK,
+            bench->name,
+            bench->run);
+
+   main_script = fopen(buffer, "w");
+   if (!main_script)
+      return NULL;
+
+   ea = eina_array_new(16);
+   if (!ea)
+     {
+        fclose(main_script);
+        return NULL;
+     }
+
+   eina_array_push(ea, strdup(buffer));
+
+   fprintf(
+      main_script,
+      "set   autoscale                        # scale axes automatically\n"
+      "unset log                              # remove any log-scaling\n"
+      "unset label                            # remove any previous labels\n"
+      "set xtic auto                          # set xtics automatically\n"
+      "set ytic auto                          # set ytics automatically\n"
+/*     "set logscale y\n" */
+      "set terminal png size 1024,768\n"
+      "set output \"output_%s_%s.png\"\n"
+      "set title \"%s %s\n"
+      "set xlabel \"tests\"\n"
+      "set ylabel \"time\"\n"
+      "plot ",
+      bench->name,
+      bench->run,
+      bench->name,
+      bench->run);
+
+   EINA_INLIST_FOREACH(bench->runs, run)
+   {
+      Eina_Counter *counter;
+      char *result;
+      size_t tmp;
+      int i;
+
+      tmp = strlen(EINA_BENCHMARK_DATA_MASK) + strlen(bench->name) + strlen(
+            bench->run) + strlen(run->name);
+      if (tmp > length)
+        {
+           buffer = alloca(sizeof (char) * tmp);
+           length = tmp;
+        }
+
+      snprintf(buffer,
+               length,
+               EINA_BENCHMARK_DATA_MASK,
+               bench->name,
+               bench->run,
+               run->name);
+
+      current_data = fopen(buffer, "w");
+      if (!current_data)
+         continue;
+
+      eina_array_push(ea, strdup(buffer));
+
+      counter = eina_counter_new(run->name);
+
+      for (i = run->start; i < run->end; i += run->step)
+        {
+           fprintf(stderr, "Run %s: %i\n", run->name, i);
+           eina_counter_start(counter);
+
+           run->cb(i);
+
+           eina_counter_stop(counter, i);
+        }
+
+      result = eina_counter_dump(counter);
+      if (result)
+        {
+           fprintf(current_data, "%s", result);
+           free(result);
+        }
+
+      eina_counter_free(counter);
+
+      fclose(current_data);
+
+      if (first == EINA_FALSE)
+         first = EINA_TRUE;
+      else
+         fprintf(main_script, ", \\\n");
+
+         fprintf(main_script,
+              "\"%s\" using 1:2 title \'%s\' with line",
+              buffer, run->name);
+   }
+
+         fprintf(main_script, "\n");
+
+         fclose(main_script);
+
+   bench->names = eina_list_append(bench->names, ea);
+
+   return ea;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_binshare.c b/tests/suite/ecore/src/lib/eina_binshare.c
new file mode 100644 (file)
index 0000000..3973357
--- /dev/null
@@ -0,0 +1,202 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler,
+ *                         Jorge Luis Zapata Muga,
+ *                         Cedric Bail,
+ *                         Gustavo Sverzut Barbieri
+ *                         Tom Hacohen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+
+ */
+/**
+ * @page tutorial_binshare_page Binary Share Tutorial
+ *
+ * Should call eina_binshare_init() before usage and eina_binshare_shutdown() after.
+ * to be written...
+ *
+ */
+
+#include "eina_share_common.h"
+#include "eina_unicode.h"
+#include "eina_private.h"
+#include "eina_binshare.h"
+
+/* The actual share */
+static Eina_Share *binshare_share;
+static const char EINA_MAGIC_BINSHARE_NODE_STR[] = "Eina Binshare Node";
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the share_common module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+EAPI Eina_Bool
+eina_binshare_init(void)
+{
+   return eina_share_common_init(&binshare_share,
+                                 EINA_MAGIC_BINSHARE_NODE,
+                                 EINA_MAGIC_BINSHARE_NODE_STR);
+}
+
+/**
+ * @internal
+ * @brief Shut down the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the share_common module set up by
+ * eina_share_common_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+EAPI Eina_Bool
+eina_binshare_shutdown(void)
+{
+   Eina_Bool ret;
+   ret = eina_share_common_shutdown(&binshare_share);
+   return ret;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+/**
+ * @addtogroup Eina_Binshare_Group Binary Share
+ *
+ * These functions allow you to store one copy of an object, and use it
+ * throughout your program.
+ *
+ * This is a method to reduce the number of duplicated objects kept in
+ * memory.
+ *
+ * For more information, you can look at the @ref tutorial_binshare_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Note that the given object has lost an instance.
+ *
+ * @param obj object The given object.
+ *
+ * This function decreases the reference counter associated to @p obj
+ * if it exists. If that counter reaches 0, the memory associated to
+ * @p obj is freed. If @p obj is NULL, the function returns
+ * immediately.
+ *
+ * Note that if the given pointer is not shared or NULL, bad things
+ * will happen, likely a segmentation fault.
+ */
+EAPI void
+eina_binshare_del(const void *obj)
+{
+   if (!obj)
+      return;
+
+   eina_share_common_del(binshare_share, obj);
+}
+
+/**
+ * @brief Retrieve an instance of an object for use in a program.
+ *
+ * @param   obj The binary object to retrieve an instance of.
+ * @param   olen The byte size
+ * @return  A pointer to an instance of the object on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p obj. If @p obj is
+ * @c NULL, then @c NULL is returned. If @p obj is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the objects to be searched and a duplicated object
+ * of @p obj is returned.
+ *
+ * This function does not check object size, but uses the
+ * exact given size. This can be used to share part of a larger
+ * object or subobject.
+ *
+ * @see eina_binshare_add()
+ */
+EAPI const void *
+eina_binshare_add_length(const void *obj, unsigned int olen)
+{
+   return eina_share_common_add_length(binshare_share,
+                                                     obj,
+                                                     (olen) * sizeof(char),
+                                                     0);
+}
+
+/**
+ * Increment references of the given shared object.
+ *
+ * @param obj The shared object.
+ * @return    A pointer to an instance of the object on success.
+ *            @c NULL on failure.
+ *
+ * This is similar to eina_share_common_add(), but it's faster since it will
+ * avoid lookups if possible, but on the down side it requires the parameter
+ * to be shared before, in other words, it must be the return of a previous
+ * eina_binshare_add().
+ *
+ * There is no unref since this is the work of eina_binshare_del().
+ */
+EAPI const void *
+eina_binshare_ref(const void *obj)
+{
+   return eina_share_common_ref(binshare_share, obj);
+}
+
+/**
+ * @brief Note that the given object @b must be shared.
+ *
+ * @param obj the shared object to know the length. It is safe to
+ *        give NULL, in that case -1 is returned.
+ *
+ * This function is a cheap way to known the length of a shared
+ * object. Note that if the given pointer is not shared, bad
+ * things will happen, likely a segmentation fault. If in doubt, try
+ * strlen().
+ */
+EAPI int
+eina_binshare_length(const void *obj)
+{
+   return eina_share_common_length(binshare_share, obj);
+}
+
+/**
+ * @brief Dump the contents of the share_common.
+ *
+ * This function dumps all objects in the share_common to stdout with a
+ * DDD: prefix per line and a memory usage summary.
+ */
+EAPI void
+eina_binshare_dump(void)
+{
+   eina_share_common_dump(binshare_share, NULL, 0);
+}
+
+/**
+ * @}
+ */
+
diff --git a/tests/suite/ecore/src/lib/eina_chained_mempool.c b/tests/suite/ecore/src/lib/eina_chained_mempool.c
new file mode 100644 (file)
index 0000000..f178cac
--- /dev/null
@@ -0,0 +1,351 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008-2010 Cedric BAIL, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef EFL_HAVE_POSIX_THREADS
+#include <pthread.h>
+#endif
+
+#ifdef EFL_HAVE_WIN32_THREADS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+#include "eina_inlist.h"
+#include "eina_error.h"
+#include "eina_module.h"
+#include "eina_mempool.h"
+#include "eina_trash.h"
+
+#include "eina_private.h"
+
+#ifdef DEBUG
+#include "eina_log.h"
+
+static int _eina_mempool_log_dom = -1;
+
+#ifdef INF
+#undef INF
+#endif
+#define INF(...) EINA_LOG_DOM_INFO(_eina_mempool_log_dom, __VA_ARGS__)
+#endif
+
+typedef struct _Chained_Mempool Chained_Mempool;
+struct _Chained_Mempool
+{
+   Eina_Inlist *first;
+   const char *name;
+   int item_alloc;
+   int pool_size;
+   int alloc_size;
+   int group_size;
+   int usage;
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_t mutex;
+# else
+   HANDLE mutex;
+# endif
+#endif
+};
+
+typedef struct _Chained_Pool Chained_Pool;
+struct _Chained_Pool
+{
+   EINA_INLIST;
+   Eina_Trash *base;
+   int usage;
+};
+
+static inline Chained_Pool *
+_eina_chained_mp_pool_new(Chained_Mempool *pool)
+{
+   Chained_Pool *p;
+   unsigned char *ptr;
+   int i;
+
+   eina_error_set(0);
+   p = malloc(pool->alloc_size);
+   if (!p)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   ptr = (unsigned char *)p + eina_mempool_alignof(sizeof(Chained_Pool));
+   p->usage = 0;
+   p->base = NULL;
+   for (i = 0; i < pool->pool_size; ++i, ptr += pool->item_alloc)
+      eina_trash_push(&p->base, ptr);
+   return p;
+}
+
+static inline void
+_eina_chained_mp_pool_free(Chained_Pool *p)
+{
+   free(p);
+}
+
+static void *
+eina_chained_mempool_malloc(void *data, __UNUSED__ unsigned int size)
+{
+   Chained_Mempool *pool = data;
+   Chained_Pool *p = NULL;
+   void *mem;
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_lock(&pool->mutex);
+# else
+   WaitForSingleObject(pool->mutex, INFINITE);
+# endif
+#endif
+
+   // look 4 pool from 2nd bucket on
+   EINA_INLIST_FOREACH(pool->first, p)
+   {
+      // base is not NULL - has a free slot
+      if (p->base)
+        {
+           pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p));
+           break;
+        }
+   }
+
+   // we have reached the end of the list - no free pools
+   if (!p)
+     {
+        p = _eina_chained_mp_pool_new(pool);
+        if (!p)
+          {
+#ifdef EFL_HAVE_PTHREAD
+# ifdef EFL_HAVE_POSIX_THREADS
+             pthread_mutex_unlock(&pool->mutex);
+# else
+             ReleaseMutex(pool->mutex);
+# endif
+#endif
+             return NULL;
+          }
+
+        pool->first = eina_inlist_prepend(pool->first, EINA_INLIST_GET(p));
+     }
+
+   // Request a free pointer
+   mem = eina_trash_pop(&p->base);
+   // move to end - it just filled up
+   if (!p->base)
+      pool->first = eina_inlist_demote(pool->first, EINA_INLIST_GET(p));
+
+   p->usage++;
+   pool->usage++;
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_unlock(&pool->mutex);
+# else
+   ReleaseMutex(pool->mutex);
+# endif
+#endif
+
+   return mem;
+}
+
+static void
+eina_chained_mempool_free(void *data, void *ptr)
+{
+   Chained_Mempool *pool = data;
+   Chained_Pool *p;
+   void *pmem;
+   int psize;
+
+   psize = pool->group_size;
+   // look 4 pool
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_lock(&pool->mutex);
+# else
+   WaitForSingleObject(pool->mutex, INFINITE);
+# endif
+#endif
+
+   EINA_INLIST_FOREACH(pool->first, p)
+   {
+      // pool mem base
+      pmem = (void *)(((unsigned char *)p) + sizeof(Chained_Pool));
+      // is it in pool mem?
+      if ((ptr >= pmem) &&
+          ((unsigned char *)ptr < (((unsigned char *)pmem) + psize)))
+        {
+           // freed node points to prev free node
+           eina_trash_push(&p->base, ptr);
+           // next free node is now the one we freed
+           p->usage--;
+           pool->usage--;
+           if (p->usage == 0)
+             {
+                // free bucket
+                pool->first = eina_inlist_remove(pool->first, EINA_INLIST_GET(p));
+                _eina_chained_mp_pool_free(p);
+             }
+           else
+              // move to front
+              pool->first = eina_inlist_promote(pool->first, EINA_INLIST_GET(p));
+
+           break;
+        }
+   }
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_unlock(&pool->mutex);
+# else
+   ReleaseMutex(pool->mutex);
+# endif
+#endif
+}
+
+static void *
+eina_chained_mempool_realloc(__UNUSED__ void *data,
+                             __UNUSED__ void *element,
+                             __UNUSED__ unsigned int size)
+{
+   return NULL;
+}
+
+static void *
+eina_chained_mempool_init(const char *context,
+                          __UNUSED__ const char *option,
+                          va_list args)
+{
+   Chained_Mempool *mp;
+   int item_size;
+   size_t length;
+
+   length = context ? strlen(context) + 1 : 0;
+
+   mp = calloc(1, sizeof(Chained_Mempool) + length);
+   if (!mp)
+      return NULL;
+
+   item_size = va_arg(args, int);
+   mp->pool_size = va_arg(args, int);
+
+   if (length)
+     {
+        mp->name = (const char *)(mp + 1);
+        memcpy((char *)mp->name, context, length);
+     }
+
+   mp->item_alloc = eina_mempool_alignof(item_size);
+   mp->group_size = mp->item_alloc * mp->pool_size;
+   mp->alloc_size = mp->group_size + eina_mempool_alignof(sizeof(Chained_Pool));
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_init(&mp->mutex, NULL);
+# else
+   mp->mutex = CreateMutex(NULL, FALSE, NULL);
+# endif
+#endif
+
+   return mp;
+}
+
+static void
+eina_chained_mempool_shutdown(void *data)
+{
+   Chained_Mempool *mp;
+
+   mp = (Chained_Mempool *)data;
+
+   while (mp->first)
+     {
+        Chained_Pool *p = (Chained_Pool *)mp->first;
+
+#ifdef DEBUG
+        if (p->usage > 0)
+           INF("Bad news we are destroying not an empty mempool [%s]\n",
+               mp->name);
+
+#endif
+
+        mp->first = eina_inlist_remove(mp->first, mp->first);
+        _eina_chained_mp_pool_free(p);
+     }
+
+#ifdef EFL_HAVE_THREADS
+# ifdef EFL_HAVE_POSIX_THREADS
+   pthread_mutex_destroy(&mp->mutex);
+# else
+   CloseHandle(mp->mutex);
+# endif
+#endif
+
+   free(mp);
+}
+
+static Eina_Mempool_Backend _eina_chained_mp_backend = {
+   "chained_mempool",
+   &eina_chained_mempool_init,
+   &eina_chained_mempool_free,
+   &eina_chained_mempool_malloc,
+   &eina_chained_mempool_realloc,
+   NULL,
+   NULL,
+   &eina_chained_mempool_shutdown
+};
+
+Eina_Bool chained_init(void)
+{
+#ifdef DEBUG
+   _eina_mempool_log_dom = eina_log_domain_register("eina_mempool",
+                                                    EINA_LOG_COLOR_DEFAULT);
+   if (_eina_mempool_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_mempool");
+        return EINA_FALSE;
+     }
+
+#endif
+   return eina_mempool_register(&_eina_chained_mp_backend);
+}
+
+void chained_shutdown(void)
+{
+   eina_mempool_unregister(&_eina_chained_mp_backend);
+#ifdef DEBUG
+   eina_log_domain_unregister(_eina_mempool_log_dom);
+   _eina_mempool_log_dom = -1;
+#endif
+}
+
+#ifndef EINA_STATIC_BUILD_CHAINED_POOL
+
+EINA_MODULE_INIT(chained_init);
+EINA_MODULE_SHUTDOWN(chained_shutdown);
+
+#endif /* ! EINA_STATIC_BUILD_CHAINED_POOL */
diff --git a/tests/suite/ecore/src/lib/eina_convert.c b/tests/suite/ecore/src/lib/eina_convert.c
new file mode 100644 (file)
index 0000000..0d75469
--- /dev/null
@@ -0,0 +1,773 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric BAIL, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_log.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_convert.h"
+#include "eina_fp.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char look_up_table[] = {'0', '1', '2', '3', '4',
+                                     '5', '6', '7', '8', '9',
+                                     'a', 'b', 'c', 'd', 'e', 'f'};
+static int _eina_convert_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_convert_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_convert_log_dom, __VA_ARGS__)
+
+#define HEXA_TO_INT(Hexa) (Hexa >= 'a') ? Hexa - 'a' + 10 : Hexa - '0'
+
+static inline void reverse(char s[], int length)
+{
+   int i, j;
+   char c;
+
+   for (i = 0, j = length - 1; i < j; i++, j--)
+     {
+        c = s[i];
+        s[i] = s[j];
+        s[j] = c;
+     }
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+EAPI Eina_Error EINA_ERROR_CONVERT_P_NOT_FOUND = 0;
+EAPI Eina_Error EINA_ERROR_CONVERT_0X_NOT_FOUND = 0;
+EAPI Eina_Error EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH = 0;
+
+static const char EINA_ERROR_CONVERT_0X_NOT_FOUND_STR[] =
+   "Error during string conversion to float, First '0x' was not found.";
+static const char EINA_ERROR_CONVERT_P_NOT_FOUND_STR[] =
+   "Error during string conversion to float, First 'p' was not found.";
+static const char EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH_STR[] =
+   "Error outrun string limit during conversion string conversion to float.";
+
+/**
+ * @endcond
+ */
+
+/**
+ * @internal
+ * @brief Initialize the convert module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the convert module of Eina. It is called by
+ * eina_init().
+ *
+ * This function sets up the error module of Eina and registers the
+ * errors #EINA_ERROR_CONVERT_0X_NOT_FOUND,
+ * #EINA_ERROR_CONVERT_P_NOT_FOUND and
+ * #EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH.
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_convert_init(void)
+{
+   _eina_convert_log_dom = eina_log_domain_register("eina_convert",
+                                                    EINA_LOG_COLOR_DEFAULT);
+   if (_eina_convert_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_convert");
+        return EINA_FALSE;
+     }
+
+#define EEMR(n) n = eina_error_msg_static_register(n ## _STR)
+   EEMR(EINA_ERROR_CONVERT_0X_NOT_FOUND);
+   EEMR(EINA_ERROR_CONVERT_P_NOT_FOUND);
+   EEMR(EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH);
+#undef EEMR
+
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the convert module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the convert module set up by
+ * eina_convert_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_convert_shutdown(void)
+{
+   eina_log_domain_unregister(_eina_convert_log_dom);
+   _eina_convert_log_dom = -1;
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Convert_Group Convert
+ *
+ * These functions allow you to convert integer or real numbers to
+ * string or conversely.
+ *
+ * To use these functions, you have to call eina_init()
+ * first, and eina_shutdown() when eina is not used anymore.
+ *
+ * @section Eina_Convert_From_Integer_To_Sring Conversion from integer to string
+ *
+ * To convert an integer to a string in the decimal base,
+ * eina_convert_itoa() should be used. If the hexadecimal base is
+ * wanted, eina_convert_xtoa() should be used. They all need a bufffer
+ * sufficiently large to store all the cyphers.
+ *
+ * Here is an example of use:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <Eina.h>
+ *
+ * int main(void)
+ * {
+ *    char tmp[128];
+ *
+ *    if (!eina_init())
+ *    {
+ *        printf ("Error during the initialization of eina.\n");
+ *        return EXIT_FAILURE;
+ *    }
+ *
+ *    eina_convert_itoa(45, tmp);
+ *    printf("value: %s\n", tmp);
+
+ *    eina_convert_xtoa(0xA1, tmp);
+ *    printf("value: %s\n", tmp);
+ *
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * Compile this code with the following command:
+ *
+ * @code
+ * gcc -Wall -o test_eina_convert test_eina.c `pkg-config --cflags --libs eina`
+ * @endcode
+ *
+ * @note
+ * The alphabetical cyphers are in lower case.
+ *
+ * @section Eina_Convert_Double Conversion double / string
+ *
+ * To convert a double to a string, eina_convert_dtoa() should be
+ * used. Like with the integer functions, a buffer must be used. The
+ * resulting string ghas the following format (which is the result
+ * obtained with snprintf() and the @%a modifier):
+ *
+ * @code
+ * [-]0xh.hhhhhp[+-]e
+ * @endcode
+ *
+ * To convert a string to a double, eina_convert_atod() should be
+ * used. The format of the string must be as above. Then, the double
+ * has the following mantiss and exponent:
+ *
+ * @code
+ * mantiss  : [-]hhhhhh
+ * exponent : 2^([+-]e - 4 * n)
+ * @endcode
+ *
+ * with n being number of cypers after the point in the string
+ * format. To obtain the double number from the mantiss and exponent,
+ * use ldexp().
+ *
+ * Here is an example of use:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ * #include <math.h>
+ *
+ * #include <Eina.h>
+ *
+ * int main(void)
+ * {
+ *    char      tmp[128];
+ *    long long int m = 0;
+ *    long int  e = 0;
+ *    double    r;
+ *
+ *    if (!eina_init())
+ *    {
+ *        printf ("Error during the initialization of eina.\n");
+ *        return EXIT_FAILURE;
+ *    }
+ *
+ *    printf("initial value : 40.56\n");
+ *    eina_convert_dtoa(40.56, tmp);
+ *    printf("result dtoa   : %s\n", tmp);
+
+ *    eina_convert_atod(tmp, 128, &m, &e);
+ *    r = ldexp((double)m, e);
+ *    printf("result atod   : %f\n", r);
+ *
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * Compile this code with the following command:
+ *
+ * @code
+ * gcc -Wall -o test_eina_convert test_eina.c `pkg-config --cflags --libs eina` -lm
+ * @endcode
+ *
+ * @{
+ */
+
+/*
+ * Come from the second edition of The C Programming Language ("K&R2") on page 64
+ */
+
+/**
+ * @brief Convert an integer number to a string in decimal base.
+ *
+ * @param n The integer to convert.
+ * @param s The buffer to store the converted integer.
+ * @return The length of the string, including the nul terminated
+ * character.
+ *
+ * This function converts @p n to a nul terminated string. The
+ * converted string is in decimal base. As no check is done, @p s must
+ * be a buffer that is sufficiently large to store the integer.
+ *
+ * The returned value is the length of the string, including the nul
+ * terminated character.
+ */
+EAPI int
+eina_convert_itoa(int n, char *s)
+{
+   int i = 0;
+   int r = 0;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(s, 0);
+
+   if (n < 0)
+     {
+        n = -n;
+        *s++ = '-';
+        r = 1;
+     }
+
+   do {
+        s[i++] = n % 10 + '0';
+     } while ((n /= 10) > 0);
+
+   s[i] = '\0';
+
+   reverse(s, i);
+
+   return i + r;
+}
+
+/**
+ * @brief Convert an integer number to a string in hexadecimal base.
+ *
+ * @param n The integer to convert.
+ * @param s The buffer to store the converted integer.
+ * @return The length of the string, including the nul terminated
+ * character.
+ *
+ * This function converts @p n to a nul terminated string. The
+ * converted string is in hexadecimal base and the alphabetical
+ * cyphers are in lower case. As no check is done, @p s must be a
+ * buffer that is sufficiently large to store the integer.
+ *
+ * The returned value is the length of the string, including the nul
+ * terminated character.
+ */
+EAPI int
+eina_convert_xtoa(unsigned int n, char *s)
+{
+   int i;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(s, 0);
+
+   i = 0;
+   do {
+        s[i++] = look_up_table[n & 0xF];
+     } while ((n >>= 4) > 0);
+
+   s[i] = '\0';
+
+   reverse(s, i);
+
+   return i;
+}
+
+/**
+ * @brief Convert a string to a double.
+ *
+ * @param src The string to convert.
+ * @param length The length of the string.
+ * @param m The mantisse.
+ * @param e The exponent.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function converts the string @p s of length @p length that
+ * represent a double in hexadecimal base to a double. It is used to
+ * replace the use of snprintf() with the \%a modifier, which is
+ * missing on some platform (like Windows (tm) or OpenBSD).
+ *
+ * The string must have the following format:
+ *
+ * @code
+ * [-]0xh.hhhhhp[+-]e
+ * @endcode
+ *
+ * where the h are the hexadecimal cyphers of the mantiss and e the
+ * exponent (a decimal number). If n is the number of cypers after the
+ * point, the returned mantiss and exponents are:
+ *
+ * @code
+ * mantiss  : [-]hhhhhh
+ * exponent : 2^([+-]e - 4 * n)
+ * @endcode
+ *
+ * The mantiss and exponent are stored in the buffers pointed
+ * respectively by @p m and @p e.
+ *
+ * If the string is invalid, the error is set to:
+ *
+ * @li #EINA_ERROR_CONVERT_0X_NOT_FOUND if no 0x is found,
+ * @li #EINA_ERROR_CONVERT_P_NOT_FOUND if no p is found,
+ * @li #EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH if @p length is not
+ * correct.
+ *
+ * In those cases, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+EAPI Eina_Bool
+eina_convert_atod(const char *src, int length, long long *m, long *e)
+{
+   const char *str = src;
+   long long mantisse;
+   long exponent;
+   int nbr_decimals = 0;
+   int sign = 1;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m,   EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(e,   EINA_FALSE);
+
+   if (length <= 0)
+      goto on_length_error;
+
+   /* Compute the mantisse. */
+   if (*str == '-')
+     {
+        sign = -1;
+        str++;
+        length--;
+     }
+
+   if (length <= 2)
+      goto on_length_error;
+
+   if (strncmp(str, "0x", 2))
+     {
+        eina_error_set(EINA_ERROR_CONVERT_0X_NOT_FOUND);
+        DBG("'0x' not found in '%s'", src);
+        return EINA_FALSE;
+     }
+
+   str += 2;
+   length -= 2;
+
+   mantisse = HEXA_TO_INT(*str);
+
+   str++;
+   length--; if (length <= 0)
+      goto on_length_error;
+
+   if (*str == '.')
+      for (str++, length--;
+           length > 0 && *str != 'p';
+           ++str, --length, ++nbr_decimals)
+        {
+           mantisse <<= 4;
+           mantisse += HEXA_TO_INT(*str);
+        }
+
+   if (sign < 0)
+      mantisse = -mantisse;
+
+   /* Compute the exponent. */
+   if (*str != 'p')
+     {
+        eina_error_set(EINA_ERROR_CONVERT_P_NOT_FOUND);
+        DBG("'p' not found in '%s'", src);
+        return EINA_FALSE;
+     }
+
+   sign = +1;
+
+   str++;
+   length--; if (length <= 0)
+      goto on_length_error;
+
+   if (strchr("-+", *str))
+     {
+        sign = (*str == '-') ? -1 : +1;
+
+        str++; length--;
+     }
+
+   for (exponent = 0; length > 0 && *str != '\0'; ++str, --length)
+     {
+        exponent *= 10;
+        exponent += *str - '0';
+     }
+
+   if (length < 0)
+      goto on_length_error;
+
+   if (sign < 0)
+      exponent = -exponent;
+
+   *m = mantisse;
+   *e = exponent - (nbr_decimals << 2);
+
+   return EINA_TRUE;
+
+on_length_error:
+   eina_error_set(EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH);
+   return EINA_FALSE;
+}
+
+/**
+ * @brief Convert a double to a string.
+ *
+ * @param d The double to convert.
+ * @param des The destination buffer to store the converted double.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function converts the double @p d to a string. The string is
+ * stored in the buffer pointed by @p des and must be sufficiently
+ * large to contain the converted double. The returned string is nul
+ * terminated and has the following format:
+ *
+ * @code
+ * [-]0xh.hhhhhp[+-]e
+ * @endcode
+ *
+ * where the h are the hexadecimal cyphers of the mantiss and e the
+ * exponent (a decimal number).
+ *
+ * The returned value is the length of the string, including the nul
+ * character.
+ */
+EAPI int
+eina_convert_dtoa(double d, char *des)
+{
+   int length = 0;
+   int p;
+   int i;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(des, EINA_FALSE);
+
+   if (d < 0.0)
+     {
+        *(des++) = '-';
+        d = -d;
+        length++;
+     }
+
+   d = frexp(d, &p);
+
+   if (p)
+     {
+        d *= 2;
+        p -= 1;
+     }
+
+   *(des++) = '0';
+   *(des++) = 'x';
+   *(des++) = look_up_table[(size_t)d];
+   *(des++) = '.';
+   length += 4;
+
+   for (i = 0; i < 16; i++, length++)
+     {
+        d -= floor(d);
+        d *= 16;
+        *(des++) = look_up_table[(size_t)d];
+     }
+
+   while (*(des - 1) == '0')
+     {
+        des--;
+        length--;
+     }
+
+   if (*(des - 1) == '.')
+     {
+        des--;
+        length--;
+     }
+
+   *(des++) = 'p';
+   if (p < 0)
+     {
+        *(des++) = '-';
+        p = -p;
+     }
+   else
+      *(des++) = '+';
+
+   length += 2;
+
+   return length + eina_convert_itoa(p, des);
+}
+
+/**
+ * @brief Convert a 32.32 fixed point number to a string.
+ *
+ * @param fp The fixed point number to convert.
+ * @param des The destination buffer to store the converted fixed point number.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function converts the 32.32 fixed point number @p fp to a
+ * string. The string is stored in the buffer pointed by @p des and
+ * must be sufficiently large to contain the converted fixed point
+ * number. The returned string is terminated and has the following
+ * format:
+ *
+ * @code
+ * [-]0xh.hhhhhp[+-]e
+ * @endcode
+ *
+ * where the h are the hexadecimal cyphers of the mantiss and e the
+ * exponent (a decimal number).
+ *
+ * The returned value is the length of the string, including the nul
+ * character.
+ *
+ * @note The code is the same than eina_convert_dtoa() except that it
+ * implements the frexp() function for fixed point numbers and does
+ * some optimisations.
+ */
+EAPI int
+eina_convert_fptoa(Eina_F32p32 fp, char *des)
+{
+   int length = 0;
+   int p = 0;
+   int i;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(des, EINA_FALSE);
+
+   if (fp == 0)
+     {
+        memcpy(des, "0x0p+0", 7);
+        return 7;
+     }
+
+   if (fp < 0)
+     {
+        *(des++) = '-';
+        fp = -fp;
+        length++;
+     }
+
+   /* fp >= 1 */
+   if (fp >= 0x0000000100000000LL)
+      while (fp >= 0x0000000100000000LL)
+        {
+           p++;
+           /* fp /= 2 */
+           fp >>= 1;
+        } /* fp < 0.5 */
+   else if (fp < 0x80000000)
+      while (fp < 0x80000000)
+        {
+           p--;
+           /* fp *= 2 */
+           fp <<= 1;
+        }
+
+   if (p)
+     {
+        p--;
+        /* fp *= 2 */
+        fp <<= 1;
+     }
+
+   *(des++) = '0';
+   *(des++) = 'x';
+   *(des++) = look_up_table[fp >> 32];
+   *(des++) = '.';
+   length += 4;
+
+   for (i = 0; i < 16; i++, length++)
+     {
+        fp &= 0x00000000ffffffffLL;
+        fp <<= 4; /* fp *= 16 */
+        *(des++) = look_up_table[fp >> 32];
+     }
+
+   while (*(des - 1) == '0')
+     {
+        des--;
+        length--;
+     }
+
+   if (*(des - 1) == '.')
+     {
+        des--;
+        length--;
+     }
+
+   *(des++) = 'p';
+   if (p < 0)
+     {
+        *(des++) = '-';
+        p = -p;
+     }
+   else
+      *(des++) = '+';
+
+   length += 2;
+
+   return length + eina_convert_itoa(p, des);
+}
+
+/**
+ * @brief Convert a string to a 32.32 fixed point number.
+ *
+ * @param src The string to convert.
+ * @param length The length of the string.
+ * @param fp The fixed point number.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function converts the string @p src of length @p length that
+ * represent a double in hexadecimal base to a 32.32 fixed point
+ * number stored in @p fp. The function always tries to convert the
+ * string with eina_convert_atod().
+ *
+ * The string must have the following format:
+ *
+ * @code
+ * [-]0xh.hhhhhp[+-]e
+ * @endcode
+ *
+ * where the h are the hexadecimal cyphers of the mantiss and e the
+ * exponent (a decimal number). If n is the number of cypers after the
+ * point, the returned mantiss and exponents are:
+ *
+ * @code
+ * mantiss  : [-]hhhhhh
+ * exponent : 2^([+-]e - 4 * n)
+ * @endcode
+ *
+ * The mantiss and exponent are stored in the buffers pointed
+ * respectively by @p m and @p e.
+ *
+ * If the string is invalid, the error is set to:
+ *
+ * @li #EINA_ERROR_CONVERT_0X_NOT_FOUND if no 0x is found,
+ * @li #EINA_ERROR_CONVERT_P_NOT_FOUND if no p is found,
+ * @li #EINA_ERROR_CONVERT_OUTRUN_STRING_LENGTH if @p length is not
+ * correct.
+ *
+ * In those cases, or if @p fp is @c NULL, #EINA_FALSE is returned,
+ * otherwise @p fp is computed and #EINA_TRUE is returned.
+ *
+ * @note The code uses eina_convert_atod() and do the correct bit
+ * shift to compute the fixed point number.
+ */
+EAPI Eina_Bool
+eina_convert_atofp(const char *src, int length, Eina_F32p32 *fp)
+{
+   long long m;
+   long e;
+
+   if (!eina_convert_atod(src, length, &m, &e))
+      return EINA_FALSE;
+
+   if (!fp)
+      return EINA_TRUE;
+
+   e += 32;
+
+   if (e > 0)
+      *fp = m << e;
+   else
+      *fp = m >> -e;
+
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_counter.c b/tests/suite/ecore/src/lib/eina_counter.c
new file mode 100644 (file)
index 0000000..8c430dc
--- /dev/null
@@ -0,0 +1,505 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifndef _WIN32
+# include <time.h>
+#else
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+#endif /* _WIN2 */
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_inlist.h"
+#include "eina_error.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_counter.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#ifndef _WIN32
+typedef struct timespec Eina_Nano_Time;
+#else
+typedef LARGE_INTEGER Eina_Nano_Time;
+#endif
+
+typedef struct _Eina_Clock Eina_Clock;
+
+struct _Eina_Counter
+{
+   EINA_INLIST;
+
+   Eina_Inlist *clocks;
+   const char *name;
+};
+
+struct _Eina_Clock
+{
+   EINA_INLIST;
+
+   Eina_Nano_Time start;
+   Eina_Nano_Time end;
+   int specimen;
+
+   Eina_Bool valid;
+};
+
+#ifndef _WIN32
+static inline int
+_eina_counter_time_get(Eina_Nano_Time *tp)
+{
+# if defined(CLOCK_PROCESS_CPUTIME_ID)
+   return clock_gettime(CLOCK_PROCESS_CPUTIME_ID, tp);
+# elif defined(CLOCK_PROF)
+   return clock_gettime(CLOCK_PROF, tp);
+# elif defined(CLOCK_REALTIME)
+   return clock_gettime(CLOCK_REALTIME, tp);
+# else
+   return gettimeofday(tp, NULL);
+# endif
+}
+#else
+static const char EINA_ERROR_COUNTER_WINDOWS_STR[] =
+   "Change your OS, you moron !";
+static int EINA_ERROR_COUNTER_WINDOWS = 0;
+static LARGE_INTEGER _eina_counter_frequency;
+
+static inline int
+_eina_counter_time_get(Eina_Nano_Time *tp)
+{
+   return QueryPerformanceCounter(tp);
+}
+#endif /* _WIN2 */
+
+static char *
+_eina_counter_asiprintf(char *base, int *position, const char *format, ...)
+{
+   char *tmp, *result;
+   int size = 32;
+   int n;
+   va_list ap;
+
+   tmp = realloc(base, sizeof (char) * (*position + size));
+   if (!tmp)
+      return base;
+
+   result = tmp;
+
+   while (1)
+     {
+        va_start(ap, format);
+        n = vsnprintf(result + *position, size, format, ap);
+        va_end(ap);
+
+        if (n > -1 && n < size)
+          {
+             /* If we always have glibc > 2.2, we could just return *position += n. */
+             *position += strlen(result + *position);
+             return result;
+          }
+
+        if (n > -1)
+           size = n + 1;
+        else
+           size <<= 1;
+
+        tmp = realloc(result, sizeof (char) * (*position + size));
+        if (!tmp)
+           return result;
+
+        result = tmp;
+     }
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the eina counter internal structure.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the counter module set up by
+ * eina_counter_init(). It is called by eina_init().
+ *
+ * This function sets up the error module of Eina and only on Windows,
+ * it initializes the high precision timer. It also registers, only on
+ * Windows, the error #EINA_ERROR_COUNTER_WINDOWS. It is also called
+ * by eina_init(). It returns 0 on failure, otherwise it returns the
+ * number of times it has already been called.
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_counter_init(void)
+{
+#ifdef _WIN32
+   EINA_ERROR_COUNTER_WINDOWS = eina_error_msg_static_register(
+         EINA_ERROR_COUNTER_WINDOWS_STR);
+   if (!QueryPerformanceFrequency(&_eina_counter_frequency))
+     {
+        eina_error_set(EINA_ERROR_COUNTER_WINDOWS);
+        return EINA_FALSE;
+     }
+
+#endif /* _WIN2 */
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the counter module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the counter module set up by
+ * eina_counter_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_counter_shutdown(void)
+{
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Counter_Group Counter
+ *
+ * @brief These functions allow you to get the time spent in a part of a code.
+ *
+ * Before using the counter system, Eina must be initialized with
+ * eina_init() and later shut down with eina_shutdown(). The create a
+ * counter, use eina_counter_new(). To free it, use
+ * eina_counter_free().
+ *
+ * To time a part of a code, call eina_counter_start() just before it,
+ * and eina_counter_stop() just after it. Each time you start to time
+ * a code, a clock is added to a list. You can give a number of that
+ * clock with the second argument of eina_counter_stop(). To send all
+ * the registered clocks to a stream (like stdout, ofr a file), use
+ * eina_counter_dump().
+ *
+ * Here is a straightforward example:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <eina_counter.h>
+ *
+ * void test_malloc(void)
+ * {
+ *    int i;
+ *
+ *    for (i = 0; i < 100000; ++i)
+ *    {
+ *       void *buf;
+ *
+ *       buf = malloc(100);
+ *       free(buf);
+ *    }
+ * }
+ *
+ * int main(void)
+ * {
+ *    Eina_Counter *counter;
+ *
+ *    if (!eina_init())
+ *    {
+ *        printf("Error during the initialization of eina\n");
+ *        return EXIT_FAILURE;
+ *    }
+ *
+ *    counter = eina_counter_new("malloc");
+ *
+ *    eina_counter_start(counter);
+ *    test_malloc();
+ *    eina_counter_stop(counter, 1);
+ *
+ *    char* result = eina_counter_dump(counter);
+ *    printf("%s", result);
+ *    free(result);
+ *
+ *    eina_counter_free(counter);
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * Compile this code with the following commant:
+ *
+ * @verbatim
+ * gcc -Wall -o test_eina_counter test_eina.c `pkg-config --cflags --libs eina`
+ * @endverbatim
+ *
+ * The result should be something like that:
+ *
+ * @verbatim
+ * \# specimen    experiment time    starting time    ending time
+ * 1              9794125            783816           10577941
+ * @endverbatim
+ *
+ * Note that the displayed time is in nanosecond.
+ *
+ * @{
+ */
+
+/**
+ * @brief Return a counter.
+ *
+ * @param name The name of the counter.
+ *
+ * This function returns a new counter. It is characterized by @p
+ * name. If @p name is @c NULL, the function returns @c NULL
+ * immediately. If memory allocation fails, @c NULL is returned and the
+ * error is set to #EINA_ERROR_OUT_OF_MEMORY.
+ *
+ * Whe the new counter is not needed anymore, use eina_counter_free() to
+ * free the allocated memory.
+ */
+EAPI Eina_Counter *
+eina_counter_new(const char *name)
+{
+   Eina_Counter *counter;
+   size_t length;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
+
+   length = strlen(name) + 1;
+
+        eina_error_set(0);
+   counter = calloc(1, sizeof (Eina_Counter) + length);
+   if (!counter)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   counter->name = (char *)(counter + 1);
+   memcpy((char *)counter->name, name, length);
+
+   return counter;
+}
+
+/**
+ * @brief Delete a counter.
+ *
+ * @param counter The counter to delete.
+ *
+ * This function remove the clock of @p counter from the used clocks
+ * (see eina_counter_start()) and frees the memory allocated for
+ * @p counter. If @p counter is @c NULL, the function returns
+ * immediately.
+ */
+EAPI void
+eina_counter_free(Eina_Counter *counter)
+{
+   EINA_SAFETY_ON_NULL_RETURN(counter);
+
+   while (counter->clocks)
+     {
+        Eina_Clock *clk = (Eina_Clock *)counter->clocks;
+
+        counter->clocks = eina_inlist_remove(counter->clocks, counter->clocks);
+        free(clk);
+     }
+
+        free(counter);
+}
+
+/**
+ * @brief Start the time count.
+ *
+ * @param counter The counter.
+ *
+ * This function specifies that the part of the code beginning just
+ * after its call is being to be timed, using @p counter. If
+ * @p counter is @c NULL, this function returns immediately.
+ *
+ * This function adds the clock associated to @p counter in a list. If
+ * the memory needed by that clock can not be allocated, the function
+ * returns and the error is set to #EINA_ERROR_OUT_OF_MEMORY.
+ *
+ * To stop the timing, eina_counter_stop() must be called with the
+ * same counter.
+ */
+EAPI void
+eina_counter_start(Eina_Counter *counter)
+{
+   Eina_Clock *clk;
+   Eina_Nano_Time tp;
+
+   EINA_SAFETY_ON_NULL_RETURN(counter);
+   if (_eina_counter_time_get(&tp) != 0)
+      return;
+
+        eina_error_set(0);
+   clk = calloc(1, sizeof (Eina_Clock));
+   if (!clk)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return;
+     }
+
+   counter->clocks = eina_inlist_prepend(counter->clocks, EINA_INLIST_GET(clk));
+
+   clk->valid = EINA_FALSE;
+   clk->start = tp;
+}
+
+/**
+ * @brief Stop the time count.
+ *
+ * @param counter The counter.
+ * @param specimen The number of the test.
+ *
+ * This function stop the timing that has been started with
+ * eina_counter_start(). @p counter must be the same than the one used
+ * with eina_counter_start(). @p specimen is the number of the
+ * test. If @p counter or its associated clock are  @c NULL, or if the
+ * time can't be retrieved the function exits.
+ */
+EAPI void
+eina_counter_stop(Eina_Counter *counter, int specimen)
+{
+   Eina_Clock *clk;
+   Eina_Nano_Time tp;
+
+   EINA_SAFETY_ON_NULL_RETURN(counter);
+   if (_eina_counter_time_get(&tp) != 0)
+      return;
+
+   clk = (Eina_Clock *)counter->clocks;
+
+   if (!clk || clk->valid == EINA_TRUE)
+      return;
+
+   clk->end = tp;
+   clk->specimen = specimen;
+   clk->valid = EINA_TRUE;
+}
+
+/**
+ * @brief Dump the result of all clocks of a counter to a stream.
+ *
+ * @return A string with a summary of the test.
+ * @param counter The counter.
+ *
+ * This function returns an malloc'd string containing the dump of
+ * all the valid clocks of @p counter.
+ * If @p counter @c NULL, the functions exits
+ * immediately. Otherwise, the output is formattted like that:
+ *
+ * @verbatim
+ * \# specimen    experiment time    starting time    ending time
+ * 1              208                120000           120208
+ * @endverbatim
+ *
+ * The unit of time is the nanosecond.
+ */
+EAPI char *
+eina_counter_dump(Eina_Counter *counter)
+{
+   Eina_Clock *clk;
+   char *result = NULL;
+   int position = 0;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(counter, NULL);
+
+   result = _eina_counter_asiprintf(
+         result,
+         &position,
+         "# specimen\texperiment time\tstarting time\tending time\n");
+   if (!result)
+      return NULL;
+
+   EINA_INLIST_REVERSE_FOREACH(counter->clocks, clk)
+   {
+      long int start;
+      long int end;
+      long int diff;
+
+      if (clk->valid == EINA_FALSE)
+         continue;
+
+#ifndef _WIN32
+      start = clk->start.tv_sec * 1000000000 + clk->start.tv_nsec;
+      end = clk->end.tv_sec * 1000000000 + clk->end.tv_nsec;
+      diff =
+         (clk->end.tv_sec -
+          clk->start.tv_sec) * 1000000000 + clk->end.tv_nsec -
+         clk->start.tv_nsec;
+#else
+      start =
+         (long int)(((long long int)clk->start.QuadPart *
+                     1000000000ll) /
+                    (long long int)_eina_counter_frequency.QuadPart);
+      end =
+         (long int)(((long long int)clk->end.QuadPart *
+                     1000000000LL) /
+                    (long long int)_eina_counter_frequency.QuadPart);
+      diff =
+         (long int)(((long long int)(clk->end.QuadPart -
+                                     clk->start.QuadPart) *
+                     1000000000LL) /
+                    (long long int)_eina_counter_frequency.QuadPart);
+#endif /* _WIN2 */
+
+      result = _eina_counter_asiprintf(result, &position,
+                                       "%i\t%li\t%li\t%li\n",
+                                       clk->specimen,
+                                       diff,
+                                       start,
+                                       end);
+   }
+
+   return result;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_cpu.c b/tests/suite/ecore/src/lib/eina_cpu.c
new file mode 100644 (file)
index 0000000..1ee411f
--- /dev/null
@@ -0,0 +1,208 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef EFL_HAVE_THREADS
+# ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include <windows.h>
+# elif defined (__SUNPRO_C) || defined(__GNU__)
+#  include <unistd.h>
+# elif defined (__FreeBSD__) || defined (__OpenBSD__) || \
+   defined (__NetBSD__) || defined (__DragonFly__) || defined (__MacOSX__) || \
+   (defined (__MACH__) && defined (__APPLE__))
+#  include <unistd.h>
+#  include <sys/param.h>
+#  include <sys/sysctl.h>
+# elif defined (__linux__) || defined(__GLIBC__)
+#  define _GNU_SOURCE
+#  include <sched.h>
+# endif
+# ifdef EFL_HAVE_POSIX_THREADS
+#  include <pthread.h>
+# endif
+
+# define TH_MAX 8
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "eina_cpu.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/* FIXME this ifdefs should be replaced */
+#if defined(__i386__) || defined(__x86_64__)
+/* We save ebx and restore it to be PIC compatible */
+static inline void _x86_cpuid(int op, int *a, int *b, int *c, int *d)
+{
+   asm volatile (
+#if defined(__x86_64__)
+      "pushq %%rbx      \n\t" /* save %ebx */
+#else
+      "pushl %%ebx      \n\t" /* save %ebx */
+#endif
+      "cpuid            \n\t"
+      "movl %%ebx, %1   \n\t" /* save what cpuid just put in %ebx */
+#if defined(__x86_64__)
+      "popq %%rbx       \n\t" /* restore the old %ebx */
+#else
+      "popl %%ebx       \n\t" /* restore the old %ebx */
+#endif
+      : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d)
+      : "a" (op)
+      : "cc");
+}
+
+static
+void _x86_simd(Eina_Cpu_Features *features)
+{
+   int a, b, c, d;
+
+   _x86_cpuid(1, &a, &b, &c, &d);
+   /*
+    * edx
+    * 18 = PN (Processor Number)
+    * 19 = CLFlush (Cache Line Flush)
+    * 23 = MMX
+    * 25 = SSE
+    * 26 = SSE2
+    * 28 = HTT (Hyper Threading)
+    * ecx
+    * 0 = SSE3
+    */
+   if ((d >> 23) & 1)
+      *features |= EINA_CPU_MMX;
+
+   if ((d >> 25) & 1)
+      *features |= EINA_CPU_SSE;
+
+   if ((d >> 26) & 1)
+      *features |= EINA_CPU_SSE2;
+
+   if (c & 1)
+      *features |= EINA_CPU_SSE3;
+}
+#endif
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/* FIXME the features checks should be called when this function is called?
+ * or make it static by doing eina_cpu_init() and return a local var
+ */
+/**
+ *
+ * @return
+ */
+EAPI Eina_Cpu_Features eina_cpu_features_get(void)
+{
+   Eina_Cpu_Features ecf = 0;
+#if defined(__i386__) || defined(__x86_64__)
+   _x86_simd(&ecf);
+#endif
+   return ecf;
+}
+
+EAPI int eina_cpu_count(void)
+{
+#ifdef EFL_HAVE_THREADS
+
+# if   defined (_WIN32)
+   SYSTEM_INFO sysinfo;
+
+   GetSystemInfo(&sysinfo);
+   return sysinfo.dwNumberOfProcessors;
+
+# elif defined (__SUNPRO_C) || defined(__GNU__)
+   /*
+    * _SC_NPROCESSORS_ONLN: number of processors that are online, that
+                            is available when sysconf is called. The number
+                            of cpu can change by admins.
+    * _SC_NPROCESSORS_CONF: maximum number of processors that are available
+                            to the current OS instance. That number can be
+                            change after a reboot.
+    * _SC_NPROCESSORS_MAX : maximum number of processors that are on the
+                            motherboard.
+    */
+   return sysconf(_SC_NPROCESSORS_ONLN);
+
+# elif defined (__FreeBSD__) || defined (__OpenBSD__) || \
+   defined (__NetBSD__) || defined (__DragonFly__) || defined (__MacOSX__) || \
+   (defined (__MACH__) && defined (__APPLE__))
+
+   int mib[4];
+   int cpus;
+   size_t len = sizeof(cpus);
+
+   mib[0] = CTL_HW;
+#ifdef HW_AVAILCPU
+   mib[1] = HW_AVAILCPU;
+#else
+   mib[1] = HW_NCPU;
+#endif
+   sysctl(mib, 2, &cpus, &len, NULL, 0);
+   if (cpus < 1)
+      cpus = 1;
+
+   return cpus;
+
+# elif defined (__linux__) || defined(__GLIBC__)
+   cpu_set_t cpu;
+   int i;
+   static int cpus = 0;
+
+   if (cpus != 0)
+      return cpus;
+
+   CPU_ZERO(&cpu);
+   if (sched_getaffinity(0, sizeof(cpu), &cpu) != 0)
+     {
+        fprintf(stderr, "[Eina] could not get cpu affinity: %s\n",
+                strerror(errno));
+        return 1;
+     }
+
+   for (i = 0; i < TH_MAX; i++)
+     {
+        if (CPU_ISSET(i, &cpu))
+           cpus = i + 1;
+        else
+           break;
+     }
+   return cpus;
+
+# else
+#  error "eina_cpu_count() error: Platform not supported"
+# endif
+#else
+   return 1;
+#endif
+}
diff --git a/tests/suite/ecore/src/lib/eina_error.c b/tests/suite/ecore/src/lib/eina_error.c
new file mode 100644 (file)
index 0000000..f273ca5
--- /dev/null
@@ -0,0 +1,463 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * @page tutorial_error_page Error Tutorial
+ *
+ * @section tutorial_error_introduction Introduction
+ *
+ * The Eina error module provides a way to manage errors in a simple
+ * but powerful way in libraries and modules. It is also used in Eina
+ * itself. Similar to libC's @c errno and strerror() facilities, this
+ * is extensible and recommended for other libraries and applications.
+ *
+ * @section tutorial_error_registering_msg Registering messages
+ *
+ * The error module can provide a system that mimic the errno system
+ * of the C standard library. It consists in 2 parts:
+ *
+ * @li a way of registering new messages with
+ * eina_error_msg_register() and eina_error_msg_get(),
+ * @li a way of setting / getting last error message with
+ * eina_error_set() / eina_error_get().
+ *
+ * So one has to fisrt register all the error messages that a program
+ * or a lib should manage. Then, when an error can occur, use
+ * eina_error_set(), and when errors are managed, use
+ * eina_error_get(). If eina_error_set() is used to set an error, do
+ * not forget to call before eina_error_set0), to remove previous set
+ * errors.
+ *
+ * Here is an example of use:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <eina_error.h>
+ *
+ * Eina_Error MY_ERROR_NEGATIVE;
+ * Eina_Error MY_ERROR_NULL;
+ *
+ * voi *data_new()
+ * {
+ *    eina_error_set(0);
+ *
+ *    eina_error_set(MY_ERROR_NULL);
+ *    return NULL;
+ * }
+ *
+ * int test(int n)
+ * {
+ *    eina_error_set(0);
+ *
+ *    if (n < 0)
+ *    {
+ *       eina_error_set(MY_ERROR_NEGATIVE);
+ *       return 0;
+ *    }
+ *
+ *    return 1;
+ * }
+ *
+ * int main(void)
+ * {
+ *    void *data;
+ *
+ *    if (!eina_init())
+ *    {
+ *       printf ("Error during the initialization of eina_error module\n");
+ *       return EXIT_FAILURE;
+ *    }
+ *
+ *    MY_ERROR_NEGATIVE = eina_error_msg_register("Negative number");
+ *    MY_ERROR_NULL = eina_error_msg_register("NULL pointer");
+
+ *    data = data_new();
+ *    if (!data)
+ *    {
+ *       Eina_Error err;
+ *
+ *       err = eina_error_get();
+ *       if (err)
+ *          printf("Error during memory allocation: %s\n",
+ *                 eina_error_msg_get(err));
+ *    }
+ *
+ *    if (!test(0))
+ *    {
+ *       Eina_Error err;
+ *
+ *       err = eina_error_get();
+ *       if (err)
+ *          printf("Error during test function: %s\n",
+ *                 eina_error_msg_get(err));
+ *    }
+ *
+ *    if (!test(-1))
+ *    {
+ *       Eina_Error err;
+ *
+ *       err = eina_error_get();
+ *       if (err)
+ *          printf("Error during test function: %s\n",
+ *                 eina_error_msg_get(err));
+ *    }
+ *
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * Of course, instead of printf(), eina_log_print() can be used to
+ * have beautiful error messages.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_error.h"
+
+/* TODO
+ * + add a wrapper for assert?
+ * + add common error numbers, messages
+ * + add a calltrace of errors, not only store the last error but a list of them
+ * and also store the function that set it
+ */
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+typedef struct _Eina_Error_Message Eina_Error_Message;
+struct _Eina_Error_Message
+{
+   Eina_Bool string_allocated;
+   const char *string;
+};
+
+static Eina_Error_Message *_eina_errors = NULL;
+static size_t _eina_errors_count = 0;
+static size_t _eina_errors_allocated = 0;
+static Eina_Error _eina_last_error;
+
+static Eina_Error_Message *
+_eina_error_msg_alloc(void)
+{
+   size_t idx;
+
+   if (_eina_errors_count == _eina_errors_allocated)
+     {
+        void *tmp;
+        size_t size;
+
+        if (EINA_UNLIKELY(_eina_errors_allocated == 0))
+           size = 24;
+        else
+           size = _eina_errors_allocated + 8;
+
+        tmp = realloc(_eina_errors, sizeof(Eina_Error_Message) * size);
+        if (!tmp)
+           return NULL;
+
+        _eina_errors = tmp;
+        _eina_errors_allocated = size;
+     }
+
+   idx = _eina_errors_count;
+   _eina_errors_count++;
+   return _eina_errors + idx;
+}
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+EAPI Eina_Error EINA_ERROR_OUT_OF_MEMORY = 0;
+
+static const char EINA_ERROR_OUT_OF_MEMORY_STR[] = "Out of memory";
+
+/**
+ * @endcond
+ */
+
+/**
+ * @internal
+ * @brief Initialize the error module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the error module of Eina. It is called by
+ * eina_init().
+ *
+ * This function registers the error #EINA_ERROR_OUT_OF_MEMORY.
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_error_init(void)
+{
+   /* TODO register the eina's basic errors */
+   EINA_ERROR_OUT_OF_MEMORY = eina_error_msg_static_register(
+         EINA_ERROR_OUT_OF_MEMORY_STR);
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the error module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the error module set up by
+ * eina_error_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_error_shutdown(void)
+{
+   Eina_Error_Message *eem, *eem_end;
+
+   eem = _eina_errors;
+   eem_end = eem + _eina_errors_count;
+
+   for (; eem < eem_end; eem++)
+      if (eem->string_allocated)
+         free((char *)eem->string);
+
+         free(_eina_errors);
+   _eina_errors = NULL;
+   _eina_errors_count = 0;
+   _eina_errors_allocated = 0;
+
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Error_Group Error
+ *
+ * @brief These functions provide error management for projects.
+ *
+ * To use the error system Eina must be initialized with eina_init()
+ * and later shut down with eina_shutdown(). Error codes are
+ * registered with eina_error_msg_register() and converted from
+ * identifier to original message string with eina_error_msg_get().
+ *
+ * Logging functions are not in eina_error anymore, see
+ * eina_log_print() instead.
+ *
+ * @{
+ */
+
+/**
+ * @brief Register a new error type.
+ *
+ * @param msg The description of the error. It will be duplicated using
+ *        strdup().
+ * @return The unique number identifier for this error.
+ *
+ * This function stores in a list the error message described by
+ * @p msg. The returned value is a unique identifier greater or equal
+ * than 1. The description can be retrieve later by passing to
+ * eina_error_msg_get() the returned value.
+ *
+ * @see eina_error_msg_static_register()
+ */
+EAPI Eina_Error
+eina_error_msg_register(const char *msg)
+{
+   Eina_Error_Message *eem;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0);
+
+   eem = _eina_error_msg_alloc();
+   if (!eem)
+      return 0;
+
+   eem->string_allocated = EINA_TRUE;
+   eem->string = strdup(msg);
+   if (!eem->string)
+     {
+        _eina_errors_count--;
+        return 0;
+     }
+
+   return _eina_errors_count; /* identifier = index + 1 (== _count). */
+}
+
+/**
+ * @brief Register a new error type, statically allocated message.
+ *
+ * @param msg The description of the error. This string will not be
+ *        duplicated and thus the given pointer should live during
+ *        usage of eina_error.
+ * @return The unique number identifier for this error.
+ *
+ * This function stores in a list the error message described by
+ * @p msg. The returned value is a unique identifier greater or equal
+ * than 1. The description can be retrieve later by passing to
+ * eina_error_msg_get() the returned value.
+ *
+ * @see eina_error_msg_register()
+ */
+EAPI Eina_Error
+eina_error_msg_static_register(const char *msg)
+{
+   Eina_Error_Message *eem;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(msg, 0);
+
+   eem = _eina_error_msg_alloc();
+   if (!eem)
+      return 0;
+
+   eem->string_allocated = EINA_FALSE;
+   eem->string = msg;
+   return _eina_errors_count; /* identifier = index + 1 (== _count). */
+}
+
+/**
+ * @brief Change the message of an already registered message
+ *
+ * @param error The Eina_Error to change the message of
+ * @param msg The description of the error. This string will be
+ * duplicated only if the error was registered with @ref eina_error_msg_register
+ * otherwise it must remain intact for the duration
+ * @return EINA_TRUE if successful, EINA_FALSE on error
+ *
+ * This function modifies the message associated with @p error and changes
+ * it to @p msg.  If the error was previously registered by @ref eina_error_msg_static_register
+ * then the string will not be duplicated, otherwise the previous message
+ * will be freed and @p msg copied.
+ *
+ * @see eina_error_msg_register()
+ */
+EAPI Eina_Bool
+eina_error_msg_modify(Eina_Error error, const char *msg)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(msg, EINA_FALSE);
+   if (error < 1)
+      return EINA_FALSE;
+
+   if ((size_t)error > _eina_errors_count)
+      return EINA_FALSE;
+
+   if (_eina_errors[error - 1].string_allocated)
+     {
+        const char *tmp;
+
+        if (!(tmp = strdup(msg)))
+           return EINA_FALSE;
+
+        free((void *)_eina_errors[error - 1].string);
+        _eina_errors[error - 1].string = tmp;
+        return EINA_TRUE;
+     }
+
+   _eina_errors[error - 1].string = msg;
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Return the description of the given an error number.
+ *
+ * @param error The error number.
+ * @return The description of the error.
+ *
+ * This function returns the description of an error that has been
+ * registered with eina_error_msg_register(). If an incorrect error is
+ * given, then @c NULL is returned.
+ */
+EAPI const char *
+eina_error_msg_get(Eina_Error error)
+{
+   if (error < 1)
+      return NULL;
+
+   if ((size_t)error > _eina_errors_count)
+      return NULL;
+
+   return _eina_errors[error - 1].string;
+}
+
+/**
+ * @brief Return the last set error.
+ *
+ * @return The last error.
+ *
+ * This function returns the last error set by eina_error_set(). The
+ * description of the message is returned by eina_error_msg_get().
+ */
+EAPI Eina_Error
+eina_error_get(void)
+{
+   return _eina_last_error;
+}
+
+/**
+ * @brief Set the last error.
+ *
+ * @param err The error identifier.
+ *
+ * This function sets the last error identifier. The last error can be
+ * retrieved with eina_error_get().
+ */
+EAPI void
+eina_error_set(Eina_Error err)
+{
+   _eina_last_error = err;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_file.c b/tests/suite/ecore/src/lib/eina_file.c
new file mode 100644 (file)
index 0000000..a5c9572
--- /dev/null
@@ -0,0 +1,544 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef _WIN32
+# define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <string.h>
+#include <dirent.h>
+
+#ifndef _WIN32
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+#else
+# include <Evil.h>
+#endif /* _WIN2 */
+
+#ifndef _WIN32
+# define PATH_DELIM '/'
+#else
+# define PATH_DELIM '\\'
+# define NAME_MAX MAX_PATH
+#endif
+
+#ifdef __sun
+# ifndef NAME_MAX
+#  define NAME_MAX 255
+# endif
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_file.h"
+#include "eina_stringshare.h"
+
+typedef struct _Eina_File_Iterator Eina_File_Iterator;
+struct _Eina_File_Iterator
+{
+   Eina_Iterator iterator;
+
+   DIR *dirp;
+   int length;
+
+   char dir[1];
+};
+
+static Eina_Bool
+_eina_file_ls_iterator_next(Eina_File_Iterator *it, void **data)
+{
+   struct dirent *dp;
+   char *name;
+   size_t length;
+
+   do
+     {
+        dp = readdir(it->dirp);
+        if (!dp)
+           return EINA_FALSE;
+     }
+   while ((dp->d_name[0] == '.') &&
+          ((dp->d_name[1] == '\0') ||
+           ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
+
+   length = strlen(dp->d_name);
+   name = alloca(length + 2 + it->length);
+
+   memcpy(name,                  it->dir,    it->length);
+   memcpy(name + it->length,     "/",        1);
+   memcpy(name + it->length + 1, dp->d_name, length + 1);
+
+   *data = (char *)eina_stringshare_add(name);
+   return EINA_TRUE;
+}
+
+static char *
+_eina_file_ls_iterator_container(Eina_File_Iterator *it)
+{
+   return it->dir;
+}
+
+static void
+_eina_file_ls_iterator_free(Eina_File_Iterator *it)
+{
+   closedir(it->dirp);
+
+   EINA_MAGIC_SET(&it->iterator, 0);
+   free(it);
+}
+
+typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
+struct _Eina_File_Direct_Iterator
+{
+   Eina_Iterator iterator;
+
+   DIR *dirp;
+   int length;
+
+   Eina_File_Direct_Info info;
+
+   char dir[1];
+};
+
+static Eina_Bool
+_eina_file_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
+{
+   struct dirent *dp;
+   size_t length;
+
+   do
+     {
+        dp = readdir(it->dirp);
+        if (!dp)
+           return EINA_FALSE;
+
+        length = strlen(dp->d_name);
+        if (it->info.name_start + length + 1 >= PATH_MAX)
+           continue;
+     }
+   while ((dp->d_name[0] == '.') &&
+          ((dp->d_name[1] == '\0') ||
+           ((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))));
+
+   memcpy(it->info.path + it->info.name_start, dp->d_name, length);
+   it->info.name_length = length;
+   it->info.path_length = it->info.name_start + length;
+   it->info.path[it->info.path_length] = '\0';
+   it->info.dirent = dp;
+
+   *data = &it->info;
+   return EINA_TRUE;
+}
+
+static char *
+_eina_file_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
+{
+   return it->dir;
+}
+
+static void
+_eina_file_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
+{
+   closedir(it->dirp);
+
+   EINA_MAGIC_SET(&it->iterator, 0);
+   free(it);
+}
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_File_Group File
+ *
+ * @brief Functions to traverse directories and split paths.
+ *
+ * @li eina_file_dir_list() list the content of a directory,
+ * recusrsively or not, and can call a callback function for eachfound
+ * file.
+ * @li eina_file_split() split a path into all the subdirectories that
+ * compose it, according to the separator of the file system.
+ *
+ * @{
+ */
+
+/**
+ * @brief List all files on the directory calling the function for every file found.
+ *
+ * @param dir The directory name.
+ * @param recursive Iterate recursively in the directory.
+ * @param cb The callback to be called.
+ * @param data The data to pass to the callback.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function lists all the files in @p dir. To list also all the
+ * sub directoris recursively, @p recursive must be set to #EINA_TRUE,
+ * otherwise it must be set to #EINA_FALSE. For each found file, @p cb
+ * is called and @p data is passed to it.
+ *
+ * If @p cb or @p dir are @c NULL, or if @p dir is a string of size 0,
+ * or if @p dir can not be opened, this function returns #EINA_FALSE
+ * immediately. otherwise, it returns #EINA_TRUE.
+ */
+EAPI Eina_Bool
+eina_file_dir_list(const char *dir,
+                   Eina_Bool recursive,
+                   Eina_File_Dir_List_Cb cb,
+                   void *data)
+{
+#ifndef _WIN32
+   struct dirent *de;
+   DIR *d;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
+
+   d = opendir(dir);
+   if (!d)
+      return EINA_FALSE;
+
+   while ((de = readdir(d)))
+     {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+           continue;
+
+        cb(de->d_name, dir, data);
+        /* d_type is only available on linux and bsd (_BSD_SOURCE) */
+
+        if (recursive == EINA_TRUE)
+          {
+             char *path;
+
+             path = alloca(strlen(dir) + strlen(de->d_name) + 2);
+             strcpy(path, dir);
+             strcat(path, "/");
+             strcat(path, de->d_name);
+#ifndef sun
+             if (de->d_type == DT_UNKNOWN)
+               {
+#endif
+             struct stat st;
+
+             if (stat(path, &st))
+                continue;
+
+             if (!S_ISDIR(st.st_mode))
+                continue;
+
+#ifndef sun
+          }
+        else if (de->d_type != DT_DIR)
+           continue;
+
+#endif
+
+             eina_file_dir_list(path, recursive, cb, data);
+          }
+     }
+
+   closedir(d);
+#else
+   WIN32_FIND_DATA file;
+   HANDLE hSearch;
+   char *new_dir;
+   TCHAR *tdir;
+   size_t length_dir;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
+
+   length_dir = strlen(dir);
+   new_dir = (char *)alloca(length_dir + 5);
+   if (!new_dir)
+      return EINA_FALSE;
+
+   memcpy(new_dir,              dir,    length_dir);
+   memcpy(new_dir + length_dir, "/*.*", 5);
+
+#ifdef UNICODE
+   tdir = evil_char_to_wchar(new_dir);
+#else
+   tdir = new_dir;
+#endif /* ! UNICODE */
+   hSearch = FindFirstFile(tdir, &file);
+#ifdef UNICODE
+   free(tdir);
+#endif /* UNICODE */
+
+   if (hSearch == INVALID_HANDLE_VALUE)
+      return EINA_FALSE;
+
+   do
+     {
+        char *filename;
+
+#ifdef UNICODE
+        filename = evil_wchar_to_char(file.cFileName);
+#else
+        filename = file.cFileName;
+#endif /* ! UNICODE */
+        if (!strcmp(filename, ".") || !strcmp(filename, ".."))
+           continue;
+
+        cb(filename, dir, data);
+
+        if (recursive == EINA_TRUE)
+          {
+             char *path;
+
+             path = alloca(strlen(dir) + strlen(filename) + 2);
+             strcpy(path, dir);
+             strcat(path, "/");
+             strcat(path, filename);
+
+             if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                continue;
+
+             eina_file_dir_list(path, recursive, cb, data);
+          }
+
+#ifdef UNICODE
+        free(filename);
+#endif /* UNICODE */
+
+     } while (FindNextFile(hSearch, &file));
+   FindClose(hSearch);
+#endif /* _WIN32 */
+
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Split a path according to the delimiter of the filesystem.
+ *
+ * @param path The path to split.
+ * @return An array of the parts of the path to split.
+ *
+ * This function splits @p path according to the delimiter of the used
+ * filesystem. If  @p path is @c NULL or if the array can not be
+ * created, @c NULL is returned, otherwise, an array with the
+ * different parts of @p path is returned.
+ */
+EAPI Eina_Array *
+eina_file_split(char *path)
+{
+   Eina_Array *ea;
+   char *current;
+   size_t length;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
+
+   ea = eina_array_new(16);
+
+   if (!ea)
+      return NULL;
+
+   for (current = strchr(path, PATH_DELIM);
+        current;
+        path = current + 1, current = strchr(path, PATH_DELIM))
+     {
+        length = current - path;
+
+        if (length <= 0)
+           continue;
+
+        eina_array_push(ea, path);
+        *current = '\0';
+     }
+
+   if (*path != '\0')
+        eina_array_push(ea, path);
+
+   return ea;
+}
+
+/**
+ * Get an iterator to list the content of a directory.
+ *
+ * Iterators are cheap to be created and allow interruption at any
+ * iteration. At each iteration, only the next directory entry is read
+ * from the filesystem with readdir().
+ *
+ * The iterator will handle the user a stringshared value with the
+ * full path. One must call eina_stringshare_del() on it after usage
+ * to not leak!
+ *
+ * The eina_file_direct_ls() function will provide a possibly faster
+ * alternative if you need to filter the results somehow, like
+ * checking extension.
+ *
+ * The iterator will walk over '.' and '..' without returning them.
+ *
+ * @param  dir The name of the directory to list
+ * @return Return an Eina_Iterator that will walk over the files and
+ *         directory in the pointed directory. On failure it will
+ *         return NULL. The iterator emits stringshared value with the
+ *         full path and must be freed with eina_stringshare_del().
+ *
+ * @see eina_file_direct_ls()
+ */
+EAPI Eina_Iterator *
+eina_file_ls(const char *dir)
+{
+   Eina_File_Iterator *it;
+   size_t length;
+
+   if (!dir)
+      return NULL;
+
+   length = strlen(dir);
+   if (length < 1)
+      return NULL;
+
+   it = calloc(1, sizeof (Eina_File_Iterator) + length);
+   if (!it)
+      return NULL;
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->dirp = opendir(dir);
+   if (!it->dirp)
+     {
+        free(it);
+        return NULL;
+     }
+
+   memcpy(it->dir, dir, length + 1);
+   if (dir[length - 1] != '/')
+      it->length = length;
+   else
+      it->length = length - 1;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_ls_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_file_ls_iterator_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_ls_iterator_free);
+
+   return &it->iterator;
+}
+
+/**
+ * Get an iterator to list the content of a directory, with direct information.
+ *
+ * Iterators are cheap to be created and allow interruption at any
+ * iteration. At each iteration, only the next directory entry is read
+ * from the filesystem with readdir().
+ *
+ * The iterator returns the direct pointer to couple of useful information in
+ * #Eina_File_Direct_Info and that pointer should not be modified anyhow!
+ *
+ * The iterator will walk over '.' and '..' without returning them.
+ *
+ * @param  dir The name of the directory to list
+
+ * @return Return an Eina_Iterator that will walk over the files and
+ *         directory in the pointed directory. On failure it will
+ *         return NULL. The iterator emits #Eina_File_Direct_Info
+ *         pointers that could be used but not modified. The lifetime
+ *         of the returned pointer is until the next iteration and
+ *         while the iterator is live, deleting the iterator
+ *         invalidates the pointer.
+ *
+ * @see eina_file_ls()
+ */
+EAPI Eina_Iterator *
+eina_file_direct_ls(const char *dir)
+{
+   Eina_File_Direct_Iterator *it;
+   size_t length;
+
+   if (!dir)
+      return NULL;
+
+   length = strlen(dir);
+   if (length < 1)
+      return NULL;
+
+   if (length + NAME_MAX + 2 >= PATH_MAX)
+      return NULL;
+
+   it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
+   if (!it)
+      return NULL;
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->dirp = opendir(dir);
+   if (!it->dirp)
+     {
+        free(it);
+        return NULL;
+     }
+
+   memcpy(it->dir,       dir, length + 1);
+   it->length = length;
+
+   memcpy(it->info.path, dir, length);
+   if (dir[length - 1] == '/')
+      it->info.name_start = length;
+   else
+     {
+        it->info.path[length] = '/';
+        it->info.name_start = length + 1;
+     }
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_direct_ls_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_file_direct_ls_iterator_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_direct_ls_iterator_free);
+
+   return &it->iterator;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_fp.c b/tests/suite/ecore/src/lib/eina_fp.c
new file mode 100644 (file)
index 0000000..73a2df0
--- /dev/null
@@ -0,0 +1,532 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+
+#include "eina_types.h"
+#include "eina_fp.h"
+
+#define MAX_PREC 1025
+static const Eina_F32p32 eina_trigo[MAX_PREC] =
+{
+   0x0000000100000000, 0x00000000ffffec43, 0x00000000ffffb10b,
+   0x00000000ffff4e5a, 0x00000000fffec42e, 0x00000000fffe1287,
+   0x00000000fffd3967, 0x00000000fffc38cd, 0x00000000fffb10b9,
+   0x00000000fff9c12c,
+   0x00000000fff84a25, 0x00000000fff6aba5, 0x00000000fff4e5ac,
+   0x00000000fff2f83b, 0x00000000fff0e351, 0x00000000ffeea6ef,
+   0x00000000ffec4316, 0x00000000ffe9b7c5, 0x00000000ffe704fe,
+   0x00000000ffe42ac0,
+   0x00000000ffe1290b, 0x00000000ffddffe2, 0x00000000ffdaaf43,
+   0x00000000ffd7372f, 0x00000000ffd397a8, 0x00000000ffcfd0ad,
+   0x00000000ffcbe23f, 0x00000000ffc7cc5f, 0x00000000ffc38f0d,
+   0x00000000ffbf2a4b,
+   0x00000000ffba9e17, 0x00000000ffb5ea75, 0x00000000ffb10f63,
+   0x00000000ffac0ce3, 0x00000000ffa6e2f6, 0x00000000ffa1919c,
+   0x00000000ff9c18d6, 0x00000000ff9678a6, 0x00000000ff90b10b,
+   0x00000000ff8ac208,
+   0x00000000ff84ab9c, 0x00000000ff7e6dc8, 0x00000000ff78088f,
+   0x00000000ff717bf0, 0x00000000ff6ac7ec, 0x00000000ff63ec85,
+   0x00000000ff5ce9bc, 0x00000000ff55bf92, 0x00000000ff4e6e08,
+   0x00000000ff46f51f,
+   0x00000000ff3f54d8, 0x00000000ff378d34, 0x00000000ff2f9e35,
+   0x00000000ff2787dc, 0x00000000ff1f4a2a, 0x00000000ff16e520,
+   0x00000000ff0e58c0, 0x00000000ff05a50a, 0x00000000fefcca01,
+   0x00000000fef3c7a6,
+   0x00000000feea9df9, 0x00000000fee14cfe, 0x00000000fed7d4b3,
+   0x00000000fece351d, 0x00000000fec46e3b, 0x00000000feba800f,
+   0x00000000feb06a9c, 0x00000000fea62de1, 0x00000000fe9bc9e2,
+   0x00000000fe913e9f,
+   0x00000000fe868c1b, 0x00000000fe7bb256, 0x00000000fe70b153,
+   0x00000000fe658913, 0x00000000fe5a3998, 0x00000000fe4ec2e4,
+   0x00000000fe4324f9, 0x00000000fe375fd7, 0x00000000fe2b7382,
+   0x00000000fe1f5ffa,
+   0x00000000fe132543, 0x00000000fe06c35d, 0x00000000fdfa3a4b,
+   0x00000000fded8a0e, 0x00000000fde0b2a8, 0x00000000fdd3b41c,
+   0x00000000fdc68e6c, 0x00000000fdb94199, 0x00000000fdabcda5,
+   0x00000000fd9e3294,
+   0x00000000fd907065, 0x00000000fd82871d, 0x00000000fd7476bd,
+   0x00000000fd663f46, 0x00000000fd57e0bd, 0x00000000fd495b21,
+   0x00000000fd3aae77, 0x00000000fd2bdabf, 0x00000000fd1cdffd,
+   0x00000000fd0dbe32,
+   0x00000000fcfe7562, 0x00000000fcef058e, 0x00000000fcdf6eb8,
+   0x00000000fccfb0e4, 0x00000000fcbfcc13, 0x00000000fcafc048,
+   0x00000000fc9f8d86, 0x00000000fc8f33ce, 0x00000000fc7eb325,
+   0x00000000fc6e0b8b,
+   0x00000000fc5d3d03, 0x00000000fc4c4791, 0x00000000fc3b2b37,
+   0x00000000fc29e7f7, 0x00000000fc187dd5, 0x00000000fc06ecd2,
+   0x00000000fbf534f2, 0x00000000fbe35637, 0x00000000fbd150a3,
+   0x00000000fbbf243b,
+   0x00000000fbacd100, 0x00000000fb9a56f6, 0x00000000fb87b61f,
+   0x00000000fb74ee7e, 0x00000000fb620016, 0x00000000fb4eeaea,
+   0x00000000fb3baefd, 0x00000000fb284c52, 0x00000000fb14c2eb,
+   0x00000000fb0112cd,
+   0x00000000faed3bf9, 0x00000000fad93e73, 0x00000000fac51a3f,
+   0x00000000fab0cf5e, 0x00000000fa9c5dd5, 0x00000000fa87c5a6,
+   0x00000000fa7306d5, 0x00000000fa5e2164, 0x00000000fa491558,
+   0x00000000fa33e2b3,
+   0x00000000fa1e8978, 0x00000000fa0909ab, 0x00000000f9f36350,
+   0x00000000f9dd9668, 0x00000000f9c7a2f9, 0x00000000f9b18905,
+   0x00000000f99b488f, 0x00000000f984e19c, 0x00000000f96e542e,
+   0x00000000f957a049,
+   0x00000000f940c5f1, 0x00000000f929c528, 0x00000000f9129df3,
+   0x00000000f8fb5056, 0x00000000f8e3dc53, 0x00000000f8cc41ee,
+   0x00000000f8b4812b, 0x00000000f89c9a0e, 0x00000000f8848c9b,
+   0x00000000f86c58d4,
+   0x00000000f853febe, 0x00000000f83b7e5d, 0x00000000f822d7b4,
+   0x00000000f80a0ac7, 0x00000000f7f1179a, 0x00000000f7d7fe31,
+   0x00000000f7bebe90, 0x00000000f7a558ba, 0x00000000f78bccb3,
+   0x00000000f7721a80,
+   0x00000000f7584225, 0x00000000f73e43a5, 0x00000000f7241f04,
+   0x00000000f709d446, 0x00000000f6ef6370, 0x00000000f6d4cc85,
+   0x00000000f6ba0f8a, 0x00000000f69f2c83, 0x00000000f6842374,
+   0x00000000f668f461,
+   0x00000000f64d9f4e, 0x00000000f632243f, 0x00000000f616833a,
+   0x00000000f5fabc41, 0x00000000f5decf59, 0x00000000f5c2bc87,
+   0x00000000f5a683cf, 0x00000000f58a2535, 0x00000000f56da0be,
+   0x00000000f550f66e,
+   0x00000000f5342649, 0x00000000f5173054, 0x00000000f4fa1494,
+   0x00000000f4dcd30c, 0x00000000f4bf6bc2, 0x00000000f4a1deb9,
+   0x00000000f4842bf7, 0x00000000f4665380, 0x00000000f4485559,
+   0x00000000f42a3186,
+   0x00000000f40be80c, 0x00000000f3ed78ef, 0x00000000f3cee434,
+   0x00000000f3b029e1, 0x00000000f39149f9, 0x00000000f3724482,
+   0x00000000f3531980, 0x00000000f333c8f8, 0x00000000f31452ef,
+   0x00000000f2f4b76a,
+   0x00000000f2d4f66d, 0x00000000f2b50ffe, 0x00000000f2950421,
+   0x00000000f274d2dc, 0x00000000f2547c33, 0x00000000f234002b,
+   0x00000000f2135eca, 0x00000000f1f29814, 0x00000000f1d1ac0e,
+   0x00000000f1b09abe,
+   0x00000000f18f6429, 0x00000000f16e0853, 0x00000000f14c8742,
+   0x00000000f12ae0fb, 0x00000000f1091583, 0x00000000f0e724e0,
+   0x00000000f0c50f17, 0x00000000f0a2d42c, 0x00000000f0807426,
+   0x00000000f05def09,
+   0x00000000f03b44db, 0x00000000f01875a1, 0x00000000eff58161,
+   0x00000000efd2681f, 0x00000000efaf29e2, 0x00000000ef8bc6af,
+   0x00000000ef683e8b, 0x00000000ef44917b, 0x00000000ef20bf86,
+   0x00000000eefcc8b1,
+   0x00000000eed8ad01, 0x00000000eeb46c7b, 0x00000000ee900727,
+   0x00000000ee6b7d08, 0x00000000ee46ce25, 0x00000000ee21fa83,
+   0x00000000edfd0228, 0x00000000edd7e51a, 0x00000000edb2a35f,
+   0x00000000ed8d3cfc,
+   0x00000000ed67b1f6, 0x00000000ed420255, 0x00000000ed1c2e1d,
+   0x00000000ecf63554, 0x00000000ecd01801, 0x00000000eca9d628,
+   0x00000000ec836fd1, 0x00000000ec5ce501, 0x00000000ec3635bd,
+   0x00000000ec0f620d,
+   0x00000000ebe869f5, 0x00000000ebc14d7d, 0x00000000eb9a0ca9,
+   0x00000000eb72a780, 0x00000000eb4b1e08, 0x00000000eb237047,
+   0x00000000eafb9e43, 0x00000000ead3a803, 0x00000000eaab8d8d,
+   0x00000000ea834ee6,
+   0x00000000ea5aec15, 0x00000000ea326520, 0x00000000ea09ba0d,
+   0x00000000e9e0eae4, 0x00000000e9b7f7a9, 0x00000000e98ee063,
+   0x00000000e965a51a, 0x00000000e93c45d2, 0x00000000e912c292,
+   0x00000000e8e91b61,
+   0x00000000e8bf5046, 0x00000000e8956146, 0x00000000e86b4e68,
+   0x00000000e84117b3, 0x00000000e816bd2d, 0x00000000e7ec3edc,
+   0x00000000e7c19cc8, 0x00000000e796d6f6, 0x00000000e76bed6e,
+   0x00000000e740e036,
+   0x00000000e715af54, 0x00000000e6ea5ad0, 0x00000000e6bee2af,
+   0x00000000e69346f9, 0x00000000e66787b5, 0x00000000e63ba4e9,
+   0x00000000e60f9e9b, 0x00000000e5e374d4, 0x00000000e5b72798,
+   0x00000000e58ab6f1,
+   0x00000000e55e22e3, 0x00000000e5316b76, 0x00000000e50490b1,
+   0x00000000e4d7929c, 0x00000000e4aa713c, 0x00000000e47d2c98,
+   0x00000000e44fc4b9, 0x00000000e42239a4, 0x00000000e3f48b61,
+   0x00000000e3c6b9f7,
+   0x00000000e398c56c, 0x00000000e36aadc9, 0x00000000e33c7314,
+   0x00000000e30e1554, 0x00000000e2df9490, 0x00000000e2b0f0d0,
+   0x00000000e2822a1a, 0x00000000e2534077, 0x00000000e22433ec,
+   0x00000000e1f50482,
+   0x00000000e1c5b240, 0x00000000e1963d2d, 0x00000000e166a550,
+   0x00000000e136eab0, 0x00000000e1070d56, 0x00000000e0d70d48,
+   0x00000000e0a6ea8e, 0x00000000e076a52f, 0x00000000e0463d33,
+   0x00000000e015b2a1,
+   0x00000000dfe50580, 0x00000000dfb435d9, 0x00000000df8343b2,
+   0x00000000df522f13, 0x00000000df20f804, 0x00000000deef9e8d,
+   0x00000000debe22b5, 0x00000000de8c8483, 0x00000000de5ac3ff,
+   0x00000000de28e131,
+   0x00000000ddf6dc21, 0x00000000ddc4b4d6, 0x00000000dd926b59,
+   0x00000000dd5fffb0, 0x00000000dd2d71e3, 0x00000000dcfac1fb,
+   0x00000000dcc7f000, 0x00000000dc94fbf8, 0x00000000dc61e5ec,
+   0x00000000dc2eade4,
+   0x00000000dbfb53e8, 0x00000000dbc7d7ff, 0x00000000db943a31,
+   0x00000000db607a88, 0x00000000db2c9909, 0x00000000daf895bf,
+   0x00000000dac470af, 0x00000000da9029e3, 0x00000000da5bc163,
+   0x00000000da273737,
+   0x00000000d9f28b66, 0x00000000d9bdbdf9, 0x00000000d988cef8,
+   0x00000000d953be6b, 0x00000000d91e8c5b, 0x00000000d8e938d0,
+   0x00000000d8b3c3d1, 0x00000000d87e2d67, 0x00000000d848759b,
+   0x00000000d8129c74,
+   0x00000000d7dca1fb, 0x00000000d7a68638, 0x00000000d7704934,
+   0x00000000d739eaf7, 0x00000000d7036b89, 0x00000000d6cccaf3,
+   0x00000000d696093d, 0x00000000d65f266f, 0x00000000d6282293,
+   0x00000000d5f0fdb0,
+   0x00000000d5b9b7d0, 0x00000000d58250fa, 0x00000000d54ac937,
+   0x00000000d513208f, 0x00000000d4db570c, 0x00000000d4a36cb6,
+   0x00000000d46b6195, 0x00000000d43335b3, 0x00000000d3fae917,
+   0x00000000d3c27bcb,
+   0x00000000d389edd7, 0x00000000d3513f43, 0x00000000d318701a,
+   0x00000000d2df8063, 0x00000000d2a67027, 0x00000000d26d3f6f,
+   0x00000000d233ee43, 0x00000000d1fa7cae, 0x00000000d1c0eab7,
+   0x00000000d1873867,
+   0x00000000d14d65c8, 0x00000000d11372e1, 0x00000000d0d95fbd,
+   0x00000000d09f2c64, 0x00000000d064d8df, 0x00000000d02a6537,
+   0x00000000cfefd176, 0x00000000cfb51da3, 0x00000000cf7a49c8,
+   0x00000000cf3f55ef,
+   0x00000000cf044220, 0x00000000cec90e64, 0x00000000ce8dbac5,
+   0x00000000ce52474c, 0x00000000ce16b401, 0x00000000cddb00ef,
+   0x00000000cd9f2e1e, 0x00000000cd633b97, 0x00000000cd272964,
+   0x00000000cceaf78e,
+   0x00000000ccaea61e, 0x00000000cc72351e, 0x00000000cc35a497,
+   0x00000000cbf8f492, 0x00000000cbbc2519, 0x00000000cb7f3634,
+   0x00000000cb4227ee, 0x00000000cb04fa50, 0x00000000cac7ad63,
+   0x00000000ca8a4131,
+   0x00000000ca4cb5c3, 0x00000000ca0f0b22, 0x00000000c9d14159,
+   0x00000000c9935870, 0x00000000c9555072, 0x00000000c9172967,
+   0x00000000c8d8e35a, 0x00000000c89a7e53, 0x00000000c85bfa5e,
+   0x00000000c81d5782,
+   0x00000000c7de95cb, 0x00000000c79fb541, 0x00000000c760b5ee,
+   0x00000000c72197dc, 0x00000000c6e25b15, 0x00000000c6a2ffa3,
+   0x00000000c663858f, 0x00000000c623ece2, 0x00000000c5e435a8,
+   0x00000000c5a45fe9,
+   0x00000000c5646bb0, 0x00000000c5245906, 0x00000000c4e427f6,
+   0x00000000c4a3d888, 0x00000000c4636ac8, 0x00000000c422debf,
+   0x00000000c3e23476, 0x00000000c3a16bf9, 0x00000000c3608550,
+   0x00000000c31f8087,
+   0x00000000c2de5da6, 0x00000000c29d1cb8, 0x00000000c25bbdc8,
+   0x00000000c21a40de, 0x00000000c1d8a606, 0x00000000c196ed49,
+   0x00000000c15516b2, 0x00000000c113224a, 0x00000000c0d1101d,
+   0x00000000c08ee033,
+   0x00000000c04c9297, 0x00000000c00a2754, 0x00000000bfc79e73,
+   0x00000000bf84f800, 0x00000000bf423404, 0x00000000beff5289,
+   0x00000000bebc539a, 0x00000000be793741, 0x00000000be35fd89,
+   0x00000000bdf2a67b,
+   0x00000000bdaf3223, 0x00000000bd6ba08b, 0x00000000bd27f1bc,
+   0x00000000bce425c2, 0x00000000bca03ca7, 0x00000000bc5c3676,
+   0x00000000bc181338, 0x00000000bbd3d2f9, 0x00000000bb8f75c3,
+   0x00000000bb4afba1,
+   0x00000000bb06649c, 0x00000000bac1b0c0, 0x00000000ba7ce018,
+   0x00000000ba37f2ad, 0x00000000b9f2e88b, 0x00000000b9adc1bc,
+   0x00000000b9687e4a, 0x00000000b9231e41, 0x00000000b8dda1ac,
+   0x00000000b8980894,
+   0x00000000b8525305, 0x00000000b80c8109, 0x00000000b7c692ac,
+   0x00000000b78087f7, 0x00000000b73a60f6, 0x00000000b6f41db4,
+   0x00000000b6adbe3a, 0x00000000b6674296, 0x00000000b620aad0,
+   0x00000000b5d9f6f4,
+   0x00000000b593270e, 0x00000000b54c3b27, 0x00000000b505334a,
+   0x00000000b4be0f84, 0x00000000b476cfde, 0x00000000b42f7464,
+   0x00000000b3e7fd20, 0x00000000b3a06a1e, 0x00000000b358bb69,
+   0x00000000b310f10c,
+   0x00000000b2c90b11, 0x00000000b2810985, 0x00000000b238ec71,
+   0x00000000b1f0b3e2, 0x00000000b1a85fe2, 0x00000000b15ff07c,
+   0x00000000b11765bc, 0x00000000b0cebfad, 0x00000000b085fe5a,
+   0x00000000b03d21ce,
+   0x00000000aff42a15, 0x00000000afab1739, 0x00000000af61e946,
+   0x00000000af18a048, 0x00000000aecf3c49, 0x00000000ae85bd55,
+   0x00000000ae3c2377, 0x00000000adf26ebb, 0x00000000ada89f2c,
+   0x00000000ad5eb4d5,
+   0x00000000ad14afc2, 0x00000000acca8ffd, 0x00000000ac805594,
+   0x00000000ac360090, 0x00000000abeb90fe, 0x00000000aba106e9,
+   0x00000000ab56625d, 0x00000000ab0ba364, 0x00000000aac0ca0b,
+   0x00000000aa75d65d,
+   0x00000000aa2ac865, 0x00000000a9dfa030, 0x00000000a9945dc9,
+   0x00000000a949013a, 0x00000000a8fd8a91, 0x00000000a8b1f9d8,
+   0x00000000a8664f1c, 0x00000000a81a8a68, 0x00000000a7ceabc7,
+   0x00000000a782b345,
+   0x00000000a736a0ef, 0x00000000a6ea74cf, 0x00000000a69e2ef2,
+   0x00000000a651cf63, 0x00000000a605562f, 0x00000000a5b8c360,
+   0x00000000a56c1702, 0x00000000a51f5123, 0x00000000a4d271cc,
+   0x00000000a485790b,
+   0x00000000a43866eb, 0x00000000a3eb3b77, 0x00000000a39df6bd,
+   0x00000000a35098c7, 0x00000000a30321a2, 0x00000000a2b5915a,
+   0x00000000a267e7fa, 0x00000000a21a258e, 0x00000000a1cc4a24,
+   0x00000000a17e55c5,
+   0x00000000a1304880, 0x00000000a0e2225f, 0x00000000a093e36f,
+   0x00000000a0458bbb, 0x000000009ff71b50, 0x000000009fa8923a,
+   0x000000009f59f086, 0x000000009f0b363e, 0x000000009ebc6370,
+   0x000000009e6d7827,
+   0x000000009e1e746f, 0x000000009dcf5856, 0x000000009d8023e6,
+   0x000000009d30d72d, 0x000000009ce17236, 0x000000009c91f50e,
+   0x000000009c425fc1, 0x000000009bf2b25b, 0x000000009ba2ece8,
+   0x000000009b530f76,
+   0x000000009b031a0f, 0x000000009ab30cc1, 0x000000009a62e797,
+   0x000000009a12aa9f, 0x0000000099c255e5, 0x000000009971e974,
+   0x000000009921655a, 0x0000000098d0c9a2, 0x0000000098801659,
+   0x00000000982f4b8d,
+   0x0000000097de6948, 0x00000000978d6f97, 0x00000000973c5e88,
+   0x0000000096eb3626, 0x000000009699f67f, 0x0000000096489f9e,
+   0x0000000095f73190, 0x0000000095a5ac61, 0x000000009554101f,
+   0x0000000095025cd6,
+   0x0000000094b09292, 0x00000000945eb161, 0x00000000940cb94e,
+   0x0000000093baaa66, 0x00000000936884b6, 0x000000009316484b,
+   0x0000000092c3f531, 0x0000000092718b75, 0x00000000921f0b24,
+   0x0000000091cc744b,
+   0x000000009179c6f5, 0x0000000091270331, 0x0000000090d4290a,
+   0x000000009081388e, 0x00000000902e31c8, 0x000000008fdb14c7,
+   0x000000008f87e197, 0x000000008f349845, 0x000000008ee138dd,
+   0x000000008e8dc36c,
+   0x000000008e3a3800, 0x000000008de696a5, 0x000000008d92df68,
+   0x000000008d3f1256, 0x000000008ceb2f7c, 0x000000008c9736e7,
+   0x000000008c4328a3, 0x000000008bef04bf, 0x000000008b9acb46,
+   0x000000008b467c45,
+   0x000000008af217cb, 0x000000008a9d9de3, 0x000000008a490e9b,
+   0x0000000089f469ff, 0x00000000899fb01e, 0x00000000894ae103,
+   0x0000000088f5fcbc, 0x0000000088a10357, 0x00000000884bf4df,
+   0x0000000087f6d163,
+   0x0000000087a198f0, 0x00000000874c4b92, 0x0000000086f6e956,
+   0x0000000086a1724b, 0x00000000864be67c, 0x0000000085f645f8,
+   0x0000000085a090cc, 0x00000000854ac704, 0x0000000084f4e8ad,
+   0x00000000849ef5d7,
+   0x000000008448ee8c, 0x0000000083f2d2db, 0x00000000839ca2d1,
+   0x0000000083465e7c, 0x0000000082f005e8, 0x0000000082999922,
+   0x0000000082431839, 0x0000000081ec833a, 0x000000008195da31,
+   0x00000000813f1d2d,
+   0x0000000080e84c3a, 0x0000000080916766, 0x00000000803a6ebf,
+   0x000000007fe36251, 0x000000007f8c422b, 0x000000007f350e59,
+   0x000000007eddc6ea, 0x000000007e866bea, 0x000000007e2efd67,
+   0x000000007dd77b6f,
+   0x000000007d7fe60f, 0x000000007d283d54, 0x000000007cd0814c,
+   0x000000007c78b205, 0x000000007c20cf8c, 0x000000007bc8d9ef,
+   0x000000007b70d13b, 0x000000007b18b57e, 0x000000007ac086c5,
+   0x000000007a68451f,
+   0x000000007a0ff098, 0x0000000079b7893e, 0x00000000795f0f1f,
+   0x0000000079068248, 0x0000000078ade2c8, 0x00000000785530ab,
+   0x0000000077fc6c01, 0x0000000077a394d5, 0x00000000774aab36,
+   0x0000000076f1af32,
+   0x000000007698a0d6, 0x00000000763f8030, 0x0000000075e64d4e,
+   0x00000000758d083e, 0x000000007533b10d, 0x0000000074da47c9,
+   0x000000007480cc80, 0x0000000074273f3f, 0x0000000073cda016,
+   0x000000007373ef10,
+   0x00000000731a2c3d, 0x0000000072c057aa, 0x0000000072667164,
+   0x00000000720c797a, 0x0000000071b26ffa, 0x00000000715854f2,
+   0x0000000070fe286e, 0x0000000070a3ea7e, 0x0000000070499b30,
+   0x000000006fef3a90,
+   0x000000006f94c8ae, 0x000000006f3a4596, 0x000000006edfb157,
+   0x000000006e850c00, 0x000000006e2a559d, 0x000000006dcf8e3d,
+   0x000000006d74b5ee, 0x000000006d19ccbe, 0x000000006cbed2bb,
+   0x000000006c63c7f3,
+   0x000000006c08ac74, 0x000000006bad804c, 0x000000006b524389,
+   0x000000006af6f639, 0x000000006a9b986b, 0x000000006a402a2c,
+   0x0000000069e4ab8a, 0x0000000069891c94, 0x00000000692d7d57,
+   0x0000000068d1cde3,
+   0x0000000068760e44, 0x00000000681a3e89, 0x0000000067be5ec1,
+   0x0000000067626ef9, 0x0000000067066f40, 0x0000000066aa5fa3,
+   0x00000000664e4032, 0x0000000065f210f9, 0x000000006595d209,
+   0x000000006539836d,
+   0x0000000064dd2536, 0x000000006480b770, 0x0000000064243a2b,
+   0x0000000063c7ad75, 0x00000000636b115c, 0x00000000630e65ed,
+   0x0000000062b1ab39, 0x000000006254e14c, 0x0000000061f80835,
+   0x00000000619b2002,
+   0x00000000613e28c2, 0x0000000060e12283, 0x0000000060840d54,
+   0x000000006026e943, 0x000000005fc9b65d, 0x000000005f6c74b2,
+   0x000000005f0f2450, 0x000000005eb1c545, 0x000000005e5457a0,
+   0x000000005df6db6f,
+   0x000000005d9950c0, 0x000000005d3bb7a3, 0x000000005cde1024,
+   0x000000005c805a54, 0x000000005c22963f, 0x000000005bc4c3f6,
+   0x000000005b66e385, 0x000000005b08f4fd, 0x000000005aaaf86a,
+   0x000000005a4ceddc,
+   0x0000000059eed561, 0x000000005990af08, 0x0000000059327adf,
+   0x0000000058d438f4, 0x000000005875e957, 0x0000000058178c16,
+   0x0000000057b9213f, 0x00000000575aa8e0, 0x0000000056fc230a,
+   0x00000000569d8fc9,
+   0x00000000563eef2d, 0x0000000055e04144, 0x000000005581861d,
+   0x000000005522bdc6, 0x0000000054c3e84e, 0x00000000546505c4,
+   0x0000000054061636, 0x0000000053a719b3, 0x000000005348104a,
+   0x0000000052e8fa09,
+   0x000000005289d6ff, 0x00000000522aa73a, 0x0000000051cb6aca,
+   0x00000000516c21bc, 0x00000000510ccc20, 0x0000000050ad6a05,
+   0x00000000504dfb78, 0x000000004fee808a, 0x000000004f8ef947,
+   0x000000004f2f65c0,
+   0x000000004ecfc603, 0x000000004e701a1f, 0x000000004e106222,
+   0x000000004db09e1b, 0x000000004d50ce19, 0x000000004cf0f22b,
+   0x000000004c910a5f, 0x000000004c3116c5, 0x000000004bd1176b,
+   0x000000004b710c5f,
+   0x000000004b10f5b2, 0x000000004ab0d371, 0x000000004a50a5ab,
+   0x0000000049f06c70, 0x00000000499027cd, 0x00000000492fd7d3,
+   0x0000000048cf7c8f, 0x00000000486f1611, 0x00000000480ea467,
+   0x0000000047ae27a1,
+   0x00000000474d9fcd, 0x0000000046ed0cfa, 0x00000000468c6f37,
+   0x00000000462bc693, 0x0000000045cb131c, 0x00000000456a54e3,
+   0x0000000045098bf5, 0x0000000044a8b861, 0x000000004447da37,
+   0x0000000043e6f186,
+   0x000000004385fe5c, 0x00000000432500c8, 0x0000000042c3f8d9,
+   0x000000004262e69f, 0x000000004201ca28, 0x0000000041a0a383,
+   0x00000000413f72bf, 0x0000000040de37eb, 0x00000000407cf317,
+   0x00000000401ba450,
+   0x000000003fba4ba7, 0x000000003f58e92a, 0x000000003ef77ce8,
+   0x000000003e9606f1, 0x000000003e348752, 0x000000003dd2fe1c,
+   0x000000003d716b5e, 0x000000003d0fcf25, 0x000000003cae2982,
+   0x000000003c4c7a83,
+   0x000000003beac238, 0x000000003b8900b0, 0x000000003b2735f9,
+   0x000000003ac56223, 0x000000003a63853d, 0x000000003a019f56,
+   0x00000000399fb07d, 0x00000000393db8c1, 0x0000000038dbb831,
+   0x000000003879aedd,
+   0x0000000038179cd3, 0x0000000037b58222, 0x0000000037535edb,
+   0x0000000036f1330b, 0x00000000368efec2, 0x00000000362cc20f,
+   0x0000000035ca7d02, 0x0000000035682fa9, 0x000000003505da14,
+   0x0000000034a37c51,
+   0x0000000034411671, 0x0000000033dea881, 0x00000000337c3292,
+   0x000000003319b4b3, 0x0000000032b72ef2, 0x000000003254a15e,
+   0x0000000031f20c08, 0x00000000318f6efe, 0x00000000312cca50,
+   0x0000000030ca1e0c,
+   0x0000000030676a43, 0x000000003004af02, 0x000000002fa1ec5a,
+   0x000000002f3f2259, 0x000000002edc510f, 0x000000002e79788b,
+   0x000000002e1698dc, 0x000000002db3b212, 0x000000002d50c43c,
+   0x000000002cedcf68,
+   0x000000002c8ad3a7, 0x000000002c27d108, 0x000000002bc4c799,
+   0x000000002b61b76b, 0x000000002afea08c, 0x000000002a9b830b,
+   0x000000002a385ef9, 0x0000000029d53464, 0x000000002972035b,
+   0x00000000290ecbee,
+   0x0000000028ab8e2c, 0x0000000028484a25, 0x0000000027e4ffe7,
+   0x000000002781af83, 0x00000000271e5906, 0x0000000026bafc82,
+   0x0000000026579a04, 0x0000000025f4319d, 0x000000002590c35c,
+   0x00000000252d4f4f,
+   0x0000000024c9d587, 0x0000000024665613, 0x000000002402d101,
+   0x00000000239f4662, 0x00000000233bb644, 0x0000000022d820b8,
+   0x00000000227485cc, 0x000000002210e590, 0x0000000021ad4013,
+   0x0000000021499565,
+   0x0000000020e5e594, 0x00000000208230b1, 0x00000000201e76ca,
+   0x000000001fbab7ef, 0x000000001f56f430, 0x000000001ef32b9b,
+   0x000000001e8f5e41, 0x000000001e2b8c30, 0x000000001dc7b578,
+   0x000000001d63da29,
+   0x000000001cfffa51, 0x000000001c9c1600, 0x000000001c382d46,
+   0x000000001bd44032, 0x000000001b704ed3, 0x000000001b0c5939,
+   0x000000001aa85f74, 0x000000001a446191, 0x0000000019e05fa2,
+   0x00000000197c59b5,
+   0x0000000019184fdb, 0x0000000018b44221, 0x0000000018503098,
+   0x0000000017ec1b50, 0x0000000017880257, 0x000000001723e5bd,
+   0x0000000016bfc591, 0x00000000165ba1e4, 0x0000000015f77ac3,
+   0x0000000015935040,
+   0x00000000152f2269, 0x0000000014caf14d, 0x000000001466bcfd,
+   0x0000000014028587, 0x00000000139e4afb, 0x00000000133a0d69,
+   0x0000000012d5cce0, 0x000000001271896f, 0x00000000120d4326,
+   0x0000000011a8fa15,
+   0x000000001144ae4a, 0x0000000010e05fd6, 0x00000000107c0ec7,
+   0x000000001017bb2d, 0x000000000fb36519, 0x000000000f4f0c98,
+   0x000000000eeab1bb, 0x000000000e865491, 0x000000000e21f52a,
+   0x000000000dbd9395,
+   0x000000000d592fe1, 0x000000000cf4ca1f, 0x000000000c90625c,
+   0x000000000c2bf8aa, 0x000000000bc78d18, 0x000000000b631fb4,
+   0x000000000afeb08f, 0x000000000a9a3fb8, 0x000000000a35cd3e,
+   0x0000000009d15931,
+   0x00000000096ce3a1, 0x0000000009086c9c, 0x0000000008a3f433,
+   0x00000000083f7a75, 0x0000000007daff71, 0x0000000007768337,
+   0x00000000071205d6, 0x0000000006ad875f, 0x00000000064907df,
+   0x0000000005e48768,
+   0x0000000005800608, 0x00000000051b83cf, 0x0000000004b700cc,
+   0x0000000004527d0f, 0x0000000003edf8a7, 0x00000000038973a4,
+   0x000000000324ee16, 0x0000000002c0680b, 0x00000000025be194,
+   0x0000000001f75ac0,
+   0x000000000192d39e, 0x00000000012e4c3e, 0x0000000000c9c4af,
+   0x0000000000653d02, 0x0000000000000000
+};
+
+EAPI Eina_F32p32
+eina_f32p32_cos(Eina_F32p32 a)
+{
+   Eina_F32p32 F32P32_2PI;
+   Eina_F32p32 F32P32_PI2;
+   Eina_F32p32 F32P32_3PI2;
+   Eina_F32p32 remainder_2PI;
+   Eina_F32p32 remainder_PI;
+   Eina_F32p32 interpol;
+   Eina_F32p32 result;
+   int idx;
+   int index2;
+
+   F32P32_2PI = EINA_F32P32_PI << 1;
+   F32P32_PI2 = EINA_F32P32_PI >> 1;
+   F32P32_3PI2 = EINA_F32P32_PI + F32P32_PI2;
+
+   /* Take advantage of cosinus symetrie. */
+   a = eina_fp32p32_llabs(a);
+
+   /* Find table entry in 0 to PI / 2 */
+   remainder_PI = a - (a / EINA_F32P32_PI) * EINA_F32P32_PI;
+
+   /* Find which case from 0 to 2 * PI */
+   remainder_2PI = a - (a / F32P32_2PI) * F32P32_2PI;
+
+   interpol = eina_f32p32_div(eina_f32p32_scale(remainder_PI, MAX_PREC * 2),
+                              EINA_F32P32_PI);
+   idx = eina_f32p32_int_to(interpol);
+   if (idx >= MAX_PREC)
+      idx = 2 * MAX_PREC - (idx - 1);
+
+   index2 = idx + 1;
+   if (index2 == MAX_PREC)
+      index2 = idx - 1;
+
+   result = eina_f32p32_add(eina_trigo[idx],
+                            eina_f32p32_mul(eina_f32p32_sub(eina_trigo[idx],
+                                                            eina_trigo[index2]),
+                                            (Eina_F32p32)eina_f32p32_fracc_get(
+                                               interpol)));
+
+   if (0 <= remainder_2PI && remainder_2PI < F32P32_PI2)
+      return result;
+   else if (F32P32_PI2 <= remainder_2PI && remainder_2PI < EINA_F32P32_PI)
+      return -result;
+   else if (EINA_F32P32_PI <= remainder_2PI && remainder_2PI < F32P32_3PI2)
+      return -result;
+   else /*  if (F32P32_3PI2 <= remainder_2PI) */
+      return result;
+}
+
+EAPI Eina_F32p32
+eina_f32p32_sin(Eina_F32p32 a)
+{
+   Eina_F32p32 F32P32_2PI;
+   Eina_F32p32 F32P32_PI2;
+   Eina_F32p32 F32P32_3PI2;
+   Eina_F32p32 remainder_2PI;
+   Eina_F32p32 remainder_PI;
+   Eina_F32p32 interpol;
+   Eina_F32p32 result;
+   int idx;
+   int index2;
+
+   F32P32_2PI = EINA_F32P32_PI << 1;
+   F32P32_PI2 = EINA_F32P32_PI >> 1;
+   F32P32_3PI2 = EINA_F32P32_PI + F32P32_PI2;
+
+   /* We only have a table for cosinus, but sin(a) = cos(pi / 2 - a) */
+   a = eina_f32p32_sub(F32P32_PI2, a);
+
+   /* Take advantage of cosinus symetrie. */
+   a = eina_fp32p32_llabs(a);
+
+   /* Find table entry in 0 to PI / 2 */
+   remainder_PI = a - (a / EINA_F32P32_PI) * EINA_F32P32_PI;
+
+   /* Find which case from 0 to 2 * PI */
+   remainder_2PI = a - (a / F32P32_2PI) * F32P32_2PI;
+
+   interpol = eina_f32p32_div(eina_f32p32_scale(remainder_PI, MAX_PREC * 2),
+                              EINA_F32P32_PI);
+   idx = eina_f32p32_int_to(interpol);
+   if (idx >= MAX_PREC)
+      idx = 2 * MAX_PREC - (idx + 1);
+
+   index2 = idx + 1;
+   if (index2 == MAX_PREC)
+      index2 = idx - 1;
+
+   result = eina_f32p32_add(eina_trigo[idx],
+                            eina_f32p32_mul(eina_f32p32_sub(eina_trigo[idx],
+                                                            eina_trigo[index2]),
+                                            (Eina_F32p32)eina_f32p32_fracc_get(
+                                               interpol)));
+
+   if (0 <= remainder_2PI && remainder_2PI < F32P32_PI2)
+      return result;
+   else if (F32P32_PI2 <= remainder_2PI && remainder_2PI < EINA_F32P32_PI)
+      return -result;
+   else if (EINA_F32P32_PI <= remainder_2PI && remainder_2PI < F32P32_3PI2)
+      return -result;
+   else /* if (F32P32_3PI2 <= remainder_2PI) */
+      return result;
+}
+
diff --git a/tests/suite/ecore/src/lib/eina_hamster.c b/tests/suite/ecore/src/lib/eina_hamster.c
new file mode 100644 (file)
index 0000000..2f68777
--- /dev/null
@@ -0,0 +1,128 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "eina_config.h"
+#include "eina_types.h"
+#include "eina_hamster.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+const char *_eina_hamster_time = __TIME__;
+const char *_eina_hamster_date = __DATE__;
+static int _eina_hamsters = -1;
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Hamster_Group Hamster
+ *
+ * @brief These functions provide hamster calls.
+ *
+ * @{
+ */
+
+/**
+ * @brief Get the hamster count.
+ *
+ * @return The number of available hamsters.
+ *
+ * This function returns how many hamsters you have.
+ */
+EAPI int
+eina_hamster_count(void)
+{
+   if (_eina_hamsters < 0)
+     {
+        int hrs = 0, min = 0, sec = 0;
+        char mon[8] = "";
+        int monnum = 0, day = 0, year = 0;
+        int fields;
+
+        fields = sscanf(_eina_hamster_time, "%02i:%02i:%02i", &hrs, &min, &sec);
+        if (fields == 3)
+          {
+             _eina_hamsters = (hrs * 60) + min;
+             fields = sscanf(_eina_hamster_date, "%s %i %i", mon, &day, &year);
+             if (fields == 3)
+               {
+                  int i;
+                  const char *mons[] =
+                  {
+                     "Jan",
+                     "Feb",
+                     "Mar",
+                     "Apr",
+                     "May",
+                     "Jun",
+                     "Jul",
+                     "Aug",
+                     "Sep",
+                     "Oct",
+                     "Nov",
+                     "Dec"
+                  };
+
+                  for (i = 0; i < 12; i++)
+                    {
+                       if (!strcmp(mon, mons[i]))
+                         {
+                            monnum = i + 1;
+                            break;
+                         }
+                    }
+                  // alloc 60 for mins, 24 for hrs
+                  // alloc 1-31 (32) for days, 1-12 (13) for months
+                  // use year as-is, for 31 bits (signed) this gives us up to
+                  // 3584 years, which is good enough imho. - 1500 years from
+                  // now or so. :)
+                  _eina_hamsters +=
+                     (day + (monnum * 32) + (13 * 32 * year)) * (24 * 60);
+               }
+          }
+     }
+
+   // format: [rest - year][0-12 - month][0-31 - day][0-23 - hrs][0-59 - sec]
+   return _eina_hamsters;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_hash.c b/tests/suite/ecore/src/lib/eina_hash.c
new file mode 100644 (file)
index 0000000..4c5f533
--- /dev/null
@@ -0,0 +1,1933 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri,
+ *                         Vincent Torri, Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+# include <Evil.h>
+#else
+# include <stdint.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_rbtree.h"
+#include "eina_error.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_hash.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_MAGIC_CHECK_HASH(d)                                        \
+   do {                                                                  \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_HASH)) {                         \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_HASH); }                             \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_HASH_ITERATOR(d, ...)                          \
+   do {                                                                  \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_HASH_ITERATOR))                \
+          {                                                                  \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_HASH_ITERATOR);                 \
+             return __VA_ARGS__;                                                   \
+          }                                                                  \
+     } while(0)
+
+#define EINA_HASH_BUCKET_SIZE 8
+#define EINA_HASH_SMALL_BUCKET_SIZE 5
+
+#define EINA_HASH_RBTREE_MASK 0xFFF
+
+typedef struct _Eina_Hash_Head Eina_Hash_Head;
+typedef struct _Eina_Hash_Element Eina_Hash_Element;
+typedef struct _Eina_Hash_Foreach_Data Eina_Hash_Foreach_Data;
+typedef struct _Eina_Iterator_Hash Eina_Iterator_Hash;
+typedef struct _Eina_Hash_Each Eina_Hash_Each;
+
+struct _Eina_Hash
+{
+   Eina_Key_Length key_length_cb;
+   Eina_Key_Cmp key_cmp_cb;
+   Eina_Key_Hash key_hash_cb;
+   Eina_Free_Cb data_free_cb;
+
+   Eina_Rbtree **buckets;
+   int size;
+   int mask;
+
+   int population;
+
+   EINA_MAGIC
+};
+
+struct _Eina_Hash_Head
+{
+   EINA_RBTREE;
+   int hash;
+
+   Eina_Rbtree *head;
+};
+
+struct _Eina_Hash_Element
+{
+   EINA_RBTREE;
+   Eina_Hash_Tuple tuple;
+   Eina_Bool begin : 1;
+};
+
+struct _Eina_Hash_Foreach_Data
+{
+   Eina_Hash_Foreach cb;
+   const void *fdata;
+};
+
+typedef void *(*Eina_Iterator_Get_Content_Callback)(Eina_Iterator_Hash *it);
+#define FUNC_ITERATOR_GET_CONTENT(Function) ((Eina_Iterator_Get_Content_Callback)Function)
+
+struct _Eina_Iterator_Hash
+{
+   Eina_Iterator iterator;
+
+   Eina_Iterator_Get_Content_Callback get_content;
+   const Eina_Hash *hash;
+
+   Eina_Iterator *current;
+   Eina_Iterator *list;
+   Eina_Hash_Head *hash_head;
+   Eina_Hash_Element *hash_element;
+   int bucket;
+
+   int index;
+
+   EINA_MAGIC
+};
+
+struct _Eina_Hash_Each
+{
+   Eina_Hash_Head *hash_head;
+   const Eina_Hash_Element *hash_element;
+   const void *data;
+};
+
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+   || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+# define get16bits(d) (*((const uint16_t *)(d)))
+#endif
+
+#if !defined (get16bits)
+# define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+                       + (uint32_t)(((const uint8_t *)(d))[0]))
+#endif
+
+static inline int
+_eina_hash_hash_rbtree_cmp_hash(const Eina_Hash_Head *hash_head,
+                                const int *hash,
+                                __UNUSED__ int key_length,
+                                __UNUSED__ void *data)
+{
+   return hash_head->hash - *hash;
+}
+
+static Eina_Rbtree_Direction
+_eina_hash_hash_rbtree_cmp_node(const Eina_Hash_Head *left,
+                                const Eina_Hash_Head *right,
+                                __UNUSED__ void *data)
+{
+   if (left->hash - right->hash < 0)
+      return EINA_RBTREE_LEFT;
+
+   return EINA_RBTREE_RIGHT;
+}
+
+static inline int
+_eina_hash_key_rbtree_cmp_key_data(const Eina_Hash_Element *hash_element,
+                                   const Eina_Hash_Tuple *tuple,
+                                   __UNUSED__ unsigned int key_length,
+                                   Eina_Key_Cmp cmp)
+{
+   int result;
+
+   result = cmp(hash_element->tuple.key,
+                hash_element->tuple.key_length,
+                tuple->key,
+                tuple->key_length);
+
+   if (result == 0 && tuple->data && tuple->data != hash_element->tuple.data)
+      return 1;
+
+   return result;
+}
+
+static Eina_Rbtree_Direction
+_eina_hash_key_rbtree_cmp_node(const Eina_Hash_Element *left,
+                               const Eina_Hash_Element *right,
+                               Eina_Key_Cmp cmp)
+{
+   int result;
+
+   result = cmp(left->tuple.key, left->tuple.key_length,
+                right->tuple.key, right->tuple.key_length);
+
+   if (result < 0)
+      return EINA_RBTREE_LEFT;
+
+   return EINA_RBTREE_RIGHT;
+}
+
+static inline Eina_Bool
+eina_hash_add_alloc_by_hash(Eina_Hash *hash,
+                            const void *key, int key_length, int alloc_length,
+                            int key_hash,
+                            const void *data)
+{
+   Eina_Hash_Element *new_hash_element = NULL;
+   Eina_Hash_Head *hash_head;
+   Eina_Error error = 0;
+   int hash_num;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   error = EINA_ERROR_OUT_OF_MEMORY;
+
+   /* Apply eina mask to hash. */
+   hash_num = key_hash & hash->mask;
+   key_hash &= EINA_HASH_RBTREE_MASK;
+
+   if (!hash->buckets)
+     {
+        hash->buckets = calloc(sizeof (Eina_Rbtree *), hash->size);
+        if (!hash->buckets) goto on_error;
+
+        hash_head = NULL;
+     }
+   else
+      /* Look up for head node. */
+      hash_head = (Eina_Hash_Head *)eina_rbtree_inline_lookup(hash->buckets[hash_num],
+                                                              &key_hash, 0,
+                                                              EINA_RBTREE_CMP_KEY_CB(_eina_hash_hash_rbtree_cmp_hash),
+                                                              NULL);
+
+   if (!hash_head)
+     {
+        /* If not found allocate it and an element. */
+        hash_head = malloc(sizeof(Eina_Hash_Head) + sizeof(Eina_Hash_Element) + alloc_length);
+        if (!hash_head)
+           goto on_error;
+
+        hash_head->hash = key_hash;
+        hash_head->head = NULL;
+
+        hash->buckets[hash_num] =
+           eina_rbtree_inline_insert(hash->buckets[hash_num], EINA_RBTREE_GET(hash_head),
+                                     EINA_RBTREE_CMP_NODE_CB(
+                                        _eina_hash_hash_rbtree_cmp_node), NULL);
+
+        new_hash_element = (Eina_Hash_Element *)(hash_head + 1);
+        new_hash_element->begin = EINA_TRUE;
+     }
+
+   if (!new_hash_element)
+     {
+        /*
+           Alloc a new element
+           (No more lookup as we expect to support more than one item for one key).
+         */
+        new_hash_element = malloc(sizeof (Eina_Hash_Element) + alloc_length);
+        if (!new_hash_element)
+           goto on_error;
+
+        new_hash_element->begin = EINA_FALSE;
+     }
+
+   /* Setup the element */
+   new_hash_element->tuple.key_length = key_length;
+   new_hash_element->tuple.data = (void *)data;
+   if (alloc_length > 0)
+     {
+        new_hash_element->tuple.key = (char *)(new_hash_element + 1);
+        memcpy((char *)new_hash_element->tuple.key, key, alloc_length);
+     }
+   else
+      new_hash_element->tuple.key = key;
+
+   /* add the new element to the hash. */
+   hash_head->head = eina_rbtree_inline_insert(hash_head->head, EINA_RBTREE_GET(new_hash_element),
+                                        EINA_RBTREE_CMP_NODE_CB(
+                                           _eina_hash_key_rbtree_cmp_node),
+                                        (const void *)hash->key_cmp_cb);
+   hash->population++;
+   return EINA_TRUE;
+
+on_error:
+   eina_error_set(error);
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_eina_hash_rbtree_each(__UNUSED__ const Eina_Rbtree *container,
+                       const Eina_Hash_Head *hash_head,
+                       Eina_Hash_Each *data)
+{
+   Eina_Iterator *it;
+   Eina_Hash_Element *hash_element;
+   Eina_Bool found = EINA_TRUE;
+
+   it = eina_rbtree_iterator_prefix(hash_head->head);
+   EINA_ITERATOR_FOREACH(it, hash_element)
+   {
+      if (hash_element->tuple.data == data->data)
+        {
+           data->hash_element = hash_element;
+           data->hash_head = (Eina_Hash_Head *)hash_head;
+           found = EINA_FALSE;
+           break;
+        }
+   }
+
+   eina_iterator_free(it);
+   return found;
+}
+
+static inline Eina_Hash_Element *
+_eina_hash_find_by_hash(const Eina_Hash *hash,
+                        Eina_Hash_Tuple *tuple,
+                        int key_hash,
+                        Eina_Hash_Head **hash_head)
+{
+   Eina_Hash_Element *hash_element;
+   int rb_hash = key_hash & EINA_HASH_RBTREE_MASK;
+
+   key_hash &= hash->mask;
+
+   if (!hash->buckets)
+      return NULL;
+
+   *hash_head = (Eina_Hash_Head *)eina_rbtree_inline_lookup(hash->buckets[key_hash],
+                                                     &rb_hash, 0,
+                                                     EINA_RBTREE_CMP_KEY_CB(
+                                                        _eina_hash_hash_rbtree_cmp_hash),
+                                                     NULL);
+   if (!*hash_head)
+      return NULL;
+
+   hash_element = (Eina_Hash_Element *)eina_rbtree_inline_lookup((*hash_head)->head,
+                                                  tuple, 0,
+                                                     EINA_RBTREE_CMP_KEY_CB(
+                                                     _eina_hash_key_rbtree_cmp_key_data),
+                                                  (const void *)hash->
+                                                  key_cmp_cb);
+
+   return hash_element;
+}
+
+static inline Eina_Hash_Element *
+_eina_hash_find_by_data(const Eina_Hash *hash,
+                        const void *data,
+                        int *key_hash,
+                        Eina_Hash_Head **hash_head)
+{
+   Eina_Hash_Each each;
+   Eina_Iterator *it;
+   int hash_num;
+
+   if (!hash->buckets)
+      return NULL;
+
+   each.hash_element = NULL;
+   each.data = data;
+
+   for (hash_num = 0; hash_num < hash->size; hash_num++)
+     {
+        if (!hash->buckets[hash_num])
+           continue;
+
+        it = eina_rbtree_iterator_prefix(hash->buckets[hash_num]);
+        eina_iterator_foreach(it, EINA_EACH_CB(_eina_hash_rbtree_each), &each);
+        eina_iterator_free(it);
+
+        if (each.hash_element)
+          {
+             *key_hash = hash_num;
+             *hash_head = each.hash_head;
+             return (Eina_Hash_Element *)each.hash_element;
+          }
+     }
+
+   return NULL;
+}
+
+static void
+_eina_hash_el_free(Eina_Hash_Element *hash_element, Eina_Hash *hash)
+{
+   if (hash->data_free_cb)
+      hash->data_free_cb(hash_element->tuple.data);
+
+   if (hash_element->begin == EINA_FALSE)
+      free(hash_element);
+}
+
+static void
+_eina_hash_head_free(Eina_Hash_Head *hash_head, Eina_Hash *hash)
+{
+   eina_rbtree_delete(hash_head->head, EINA_RBTREE_FREE_CB(_eina_hash_el_free), hash);
+   free(hash_head);
+}
+
+static Eina_Bool
+_eina_hash_del_by_hash_el(Eina_Hash *hash,
+                          Eina_Hash_Element *hash_element,
+                          Eina_Hash_Head *hash_head,
+                          int key_hash)
+{
+   hash_head->head = eina_rbtree_inline_remove(hash_head->head, EINA_RBTREE_GET(
+                                           hash_element), EINA_RBTREE_CMP_NODE_CB(
+                                           _eina_hash_key_rbtree_cmp_node),
+                                        (const void *)hash->key_cmp_cb);
+   _eina_hash_el_free(hash_element, hash);
+
+   if (!hash_head->head)
+     {
+        key_hash &= hash->mask;
+
+        hash->buckets[key_hash] =
+           eina_rbtree_inline_remove(hash->buckets[key_hash], EINA_RBTREE_GET(
+                                        hash_head),
+                                     EINA_RBTREE_CMP_NODE_CB(
+                                        _eina_hash_hash_rbtree_cmp_node), NULL);
+        free(hash_head);
+     }
+
+   hash->population--;
+   if (hash->population == 0)
+     {
+        free(hash->buckets);
+        hash->buckets = NULL;
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_eina_hash_del_by_key_hash(Eina_Hash *hash,
+                           const void *key,
+                           int key_length,
+                           int key_hash,
+                           const void *data)
+{
+   Eina_Hash_Element *hash_element;
+   Eina_Hash_Head *hash_head;
+   Eina_Hash_Tuple tuple;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!hash->buckets)
+      return EINA_FALSE;
+
+   tuple.key = (void *)key;
+   tuple.key_length = key_length;
+   tuple.data = (void *)data;
+
+   hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
+   if (!hash_element)
+      return EINA_FALSE;
+
+   return _eina_hash_del_by_hash_el(hash, hash_element, hash_head, key_hash);
+}
+
+static Eina_Bool
+_eina_hash_del_by_key(Eina_Hash *hash, const void *key, const void *data)
+{
+   int key_length, key_hash;
+
+   EINA_MAGIC_CHECK_HASH(hash);
+   if (!hash)
+      return EINA_FALSE;
+
+   if (!key)
+      return EINA_FALSE;
+
+   if (!hash->buckets)
+      return EINA_FALSE;
+
+   key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
+   key_hash = hash->key_hash_cb(key, key_length);
+   return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, data);
+}
+
+static unsigned int
+_eina_string_key_length(const char *key)
+{
+   if (!key)
+      return 0;
+
+   return (int)strlen(key) + 1;
+}
+
+static int
+_eina_string_key_cmp(const char *key1, __UNUSED__ int key1_length,
+                     const char *key2, __UNUSED__ int key2_length)
+{
+   return strcmp(key1, key2);
+}
+
+static int
+_eina_stringshared_key_cmp(const char *key1, __UNUSED__ int key1_length,
+                           const char *key2, __UNUSED__ int key2_length)
+{
+   return key1 - key2;
+}
+
+static unsigned int
+_eina_int32_key_length(__UNUSED__ const uint32_t *key)
+{
+   return 4;
+}
+
+static int
+_eina_int32_key_cmp(const uint32_t *key1, __UNUSED__ int key1_length,
+                    const uint32_t *key2, __UNUSED__ int key2_length)
+{
+   return *key1 - *key2;
+}
+
+static unsigned int
+_eina_int64_key_length(__UNUSED__ const uint32_t *key)
+{
+   return 8;
+}
+
+static int
+_eina_int64_key_cmp(const uint64_t *key1, __UNUSED__ int key1_length,
+                    const uint64_t *key2, __UNUSED__ int key2_length)
+{
+   return *key1 - *key2;
+}
+
+static Eina_Bool
+_eina_foreach_cb(const Eina_Hash *hash,
+                 Eina_Hash_Tuple *data,
+                 Eina_Hash_Foreach_Data *fdata)
+{
+   return fdata->cb((Eina_Hash *)hash,
+                    data->key,
+                    data->data,
+                    (void *)fdata->fdata);
+}
+
+static void *
+_eina_hash_iterator_data_get_content(Eina_Iterator_Hash *it)
+{
+   Eina_Hash_Element *stuff;
+
+   EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL);
+
+   stuff = it->hash_element;
+
+   if (!stuff)
+      return NULL;
+
+   return stuff->tuple.data;
+}
+
+static void *
+_eina_hash_iterator_key_get_content(Eina_Iterator_Hash *it)
+{
+   Eina_Hash_Element *stuff;
+
+   EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL);
+
+   stuff = it->hash_element;
+
+   if (!stuff)
+      return NULL;
+
+   return (void *)stuff->tuple.key;
+}
+
+static Eina_Hash_Tuple *
+_eina_hash_iterator_tuple_get_content(Eina_Iterator_Hash *it)
+{
+   Eina_Hash_Element *stuff;
+
+   EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL);
+
+   stuff = it->hash_element;
+
+   if (!stuff)
+      return NULL;
+
+   return &stuff->tuple;
+}
+
+static Eina_Bool
+_eina_hash_iterator_next(Eina_Iterator_Hash *it, void **data)
+{
+   Eina_Bool ok;
+   int bucket;
+
+   if (!(it->index < it->hash->population))
+      return EINA_FALSE;
+
+   if (!it->current)
+     {
+        ok = EINA_FALSE;
+        bucket = 0;
+        it->index = -1;
+     }
+   else
+     {
+        ok = eina_iterator_next(it->list, (void **)&it->hash_element);
+        if (!ok)
+          {
+                  eina_iterator_free(it->list);
+             it->list = NULL;
+
+             ok = eina_iterator_next(it->current, (void **)&it->hash_head);
+             if (!ok)
+               {
+                  eina_iterator_free(it->current);
+                  it->current = NULL;
+                  it->bucket++;
+               }
+             else
+               {
+                  it->list = eina_rbtree_iterator_prefix(it->hash_head->head);
+                  ok = eina_iterator_next(it->list, (void **)&it->hash_element);
+               }
+          }
+
+        bucket = it->bucket;
+     }
+
+   if (ok == EINA_FALSE)
+     {
+        while (bucket < it->hash->size)
+          {
+             if (it->hash->buckets[bucket])
+               {
+                  it->current =
+                     eina_rbtree_iterator_prefix(it->hash->buckets[bucket]);
+                  ok = eina_iterator_next(it->current, (void **)&it->hash_head);
+                  if (ok)
+                     break;
+
+                  eina_iterator_free(it->current);
+                  it->current = NULL;
+               }
+
+             ++bucket;
+          }
+        if (it->list)
+                  eina_iterator_free(it->list);
+
+        it->list = eina_rbtree_iterator_prefix(it->hash_head->head);
+        ok = eina_iterator_next(it->list, (void **)&it->hash_element);
+        if (bucket == it->hash->size)
+           ok = EINA_FALSE;
+     }
+
+   it->index++;
+   it->bucket = bucket;
+
+   if (ok)
+      *data = it->get_content(it);
+
+   return ok;
+}
+
+static void *
+_eina_hash_iterator_get_container(Eina_Iterator_Hash *it)
+{
+      EINA_MAGIC_CHECK_HASH_ITERATOR(it, NULL);
+   return (void *)it->hash;
+}
+
+static void
+_eina_hash_iterator_free(Eina_Iterator_Hash *it)
+{
+      EINA_MAGIC_CHECK_HASH_ITERATOR(it);
+   if (it->current)
+      eina_iterator_free(it->current);
+
+   if (it->list)
+      eina_iterator_free(it->list);
+
+      free(it);
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Hash_Group Hash Table
+ *
+ * @brief give a small description here : what it is for, what it does
+ * , etc...
+ *
+ * Hash API. Give some hints about the use (functions that must be
+ * used like init / shutdown), general use, etc... Give also a link to
+ * tutorial below.
+ *
+ * @section hashtable_algo Algorithm
+ *
+ * Give here the algorithm used in the implementation
+ *
+ * @section hashtable_perf Performance
+ *
+ * Give some hints about performance if it is possible, and an image !
+ *
+ * @section hashtable_tutorial Tutorial
+ *
+ * Here is a fantastic tutorial about our hash table
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new hash table.
+ *
+ * @param key_length_cb The function called when getting the size of the key.
+ * @param key_cmp_cb The function called when comparing the keys.
+ * @param key_hash_cb The function called when getting the values.
+ * @param data_free_cb The function called when the hash table is freed.
+ * @param buckets_power_size The size of the buckets.
+ * @return The new hash table.
+ *
+ * This function create a new hash table using user-defined callbacks
+ * to manage the hash table. On failure, @c NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. If @p key_cmp_cb or @pkey_hash_cb
+ * are @c NULL, @c NULL is returned. If @p buckets_power_size is
+ * smaller or equal than 2, or if it is greater or equal than 17,
+ * @c NULL is returned.
+ *
+ * Pre-defined functions are available to create a hash table. See
+ * eina_hash_string_djb2_new(), eina_hash_string_superfast_new(),
+ * eina_hash_string_small_new(), eina_hash_int32_new(),
+ * eina_hash_int64_new(), eina_hash_pointer_new() and
+ * eina_hash_stringshared_new().
+ */
+EAPI Eina_Hash *
+eina_hash_new(Eina_Key_Length key_length_cb,
+              Eina_Key_Cmp key_cmp_cb,
+              Eina_Key_Hash key_hash_cb,
+              Eina_Free_Cb data_free_cb,
+              int buckets_power_size)
+{
+   /* FIXME: Use mempool. */
+   Eina_Hash *new;
+
+   eina_error_set(0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key_cmp_cb,  NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key_hash_cb, NULL);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(buckets_power_size < 3,  NULL);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(buckets_power_size > 16, NULL);
+
+   new = malloc(sizeof (Eina_Hash));
+   if (!new)
+      goto on_error;
+
+   EINA_MAGIC_SET(new, EINA_MAGIC_HASH);
+   
+   new->key_length_cb = key_length_cb;
+   new->key_cmp_cb = key_cmp_cb;
+   new->key_hash_cb = key_hash_cb;
+   new->data_free_cb = data_free_cb;
+   new->buckets = NULL;
+   new->population = 0;
+
+   new->size = 1 << buckets_power_size;
+   new->mask = new->size - 1;
+
+   return new;
+
+on_error:
+   eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+   return NULL;
+}
+
+/**
+ * @brief Create a new hash table using the djb2 algorithm.
+ *
+ * @param data_free_cb The function called when the hash table is freed.
+ * @return The new hash table.
+ *
+ * This function create a new hash table using the djb2 algorithm for
+ * table management and strcmp() to compare the keys. Values can then
+ * be looked up with pointers other than the original key pointer that
+ * was used to add values. On failure, this function returns @c NULL.
+ * @p data_free_cb is a callback called when the hash table is
+ * freed. @c NULL can be passed as callback.
+ */
+EAPI Eina_Hash *
+eina_hash_string_djb2_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length),
+                        EINA_KEY_CMP(_eina_string_key_cmp),
+                        EINA_KEY_HASH(eina_hash_djb2),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @brief Create a new hash table for use with strings.
+ *
+ * @param data_free_cb The function called when the hash table is freed.
+ * @return The new hash table.
+ *
+ * This function create a new hash table using the superfast algorithm
+ * for table management and strcmp() to compare the keys. Values can
+ * then be looked up with pointers other than the original key pointer
+ * that was used to add values. On failure, this function returns
+ * @c NULL. @p data_free_cb is a callback called when the hash table is
+ * freed. @c NULL can be passed as callback. 
+ */
+EAPI Eina_Hash *
+eina_hash_string_superfast_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length),
+                        EINA_KEY_CMP(_eina_string_key_cmp),
+                        EINA_KEY_HASH(eina_hash_superfast),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @brief Create a new hash table for use with strings with small bucket size.
+ *
+ * @param data_free_cb  The function called when the hash table is freed.
+ * @return  The new hash table.
+ *
+ * This function create a new hash table using the superfast algorithm
+ * for table management and strcmp() to compare the keys, but with a
+ * smaller bucket size (compared to eina_hash_string_superfast_new())
+ * which will minimize the memory used by the returned hash
+ * table. Values can then be looked up with pointers other than the
+ * original key pointer that was used to add values. On failure, this
+ * function returns @c NULL. @p data_free_cb is a callback called when
+ * the hash table is freed. @c NULL can be passed as callback.
+ */
+EAPI Eina_Hash *
+eina_hash_string_small_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length),
+                        EINA_KEY_CMP(_eina_string_key_cmp),
+                        EINA_KEY_HASH(eina_hash_superfast),
+                        data_free_cb,
+                        EINA_HASH_SMALL_BUCKET_SIZE);
+}
+
+/**
+ * @brief Create a new hash table for use with 32bit integers.
+ *
+ * @param data_free_cb  The function called when the hash table is freed.
+ * @return  The new hash table.
+ *
+ * This function create a new hash table using the int32 algorithm for
+ * table management and dereferenced pointers to compare the
+ * keys. Values can then be looked up with pointers other than the
+ * original key pointer that was used to add values. This method may
+ * appear to be able to match string keys, actually it only matches
+ * the first character. On failure, this function returns @c NULL.
+ * @p data_free_cb is a callback called when the hash table is freed.
+ * @c NULL can be passed as callback.
+ */
+EAPI Eina_Hash *
+eina_hash_int32_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int32_key_length),
+                        EINA_KEY_CMP(_eina_int32_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int32),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @brief Create a new hash table for use with 64bit integers.
+ *
+ * @param data_free_cb  The function called when the hash table is freed.
+ * @return  The new hash table.
+ *
+ * This function create a new hash table using the int64 algorithm for
+ * table management and dereferenced pointers to compare the
+ * keys. Values can then be looked up with pointers other than the
+ * original key pointer that was used to add values. This method may
+ * appear to be able to match string keys, actually it only matches
+ * the first character. On failure, this function returns @c NULL.
+ * @p data_free_cb is a callback called when the hash table is freed.
+ * @c NULL can be passed as callback.
+ */
+EAPI Eina_Hash *
+eina_hash_int64_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int64_key_length),
+                        EINA_KEY_CMP(_eina_int64_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int64),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @brief Create a new hash table for use with pointers.
+ *
+ * @param data_free_cb  The function called when the hash table is freed.
+ * @return  The new hash table.
+ *
+ * This function create a new hash table using the int64 algorithm for
+ * table management and dereferenced pointers to compare the
+ * keys. Values can then be looked up with pointers other than the
+ * original key pointer that was used to add values. This method may
+ * appear to be able to match string keys, actually it only matches
+ * the first character. On failure, this function returns @c NULL.
+ * @p data_free_cb is a callback called when the hash table is freed.
+ * @c NULL can be passed as callback.
+ */
+EAPI Eina_Hash *
+eina_hash_pointer_new(Eina_Free_Cb data_free_cb)
+{
+#ifdef __LP64__
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int64_key_length),
+                        EINA_KEY_CMP(_eina_int64_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int64),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+#else
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int32_key_length),
+                        EINA_KEY_CMP(_eina_int32_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int32),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+#endif
+}
+
+/**
+ * @brief Create a new hash table optimized for stringshared values.
+ *
+ * @param data_free_cb  The function called when the hash table is freed.
+ * @return  The new hash table.
+ *
+ * This function create a new hash table optimized for stringshared
+ * values. Values CAN NOT be looked up with pointers not
+ * equal to the original key pointer that was used to add a value. On failure, this function returns @c NULL.
+ * @p data_free_cb is a callback called when the hash table is freed.
+ * @c NULL can be passed as callback.
+ *
+ * Excerpt of code that will NOT work with this type of hash:
+ *
+ * @code
+ * extern Eina_Hash *hash;
+ * extern const char *value;
+ * const char *a = eina_stringshare_add("key");
+ *
+ * eina_hash_add(hash, a, value);
+ * eina_hash_find(hash, "key")
+ * @endcode
+ */
+EAPI Eina_Hash *
+eina_hash_stringshared_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(NULL,
+                        EINA_KEY_CMP(_eina_stringshared_key_cmp),
+                        EINA_KEY_HASH(eina_hash_superfast),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @brief Returns the number of entries in the given hash table.
+ *
+ * @param hash The given hash table.
+ * @return The number of entries in the hash table.
+ *
+ * This function returns the number of entries in @p hash, or 0 on
+ * error. If @p hash is @c NULL, 0 is returned.
+ */
+EAPI int
+eina_hash_population(const Eina_Hash *hash)
+{
+   if (!hash)
+      return 0;
+
+   EINA_MAGIC_CHECK_HASH(hash);
+   return hash->population;
+}
+
+/**
+ * Free the given hash table resources.
+ *
+ * @param hash The hash table to be freed.
+ *
+ * This function frees up all the memory allocated to storing @p hash,
+ * and call the free callback if it has been passed to the hash table
+ * at creation time. If no free callback has been passed, any entries
+ * in the table that the program has no more pointers for elsewhere
+ * may now be lost, so this should only be called if the program has
+ * already freed any allocated data in the hash table or has the
+ * pointers for data in the table stored elsewhere as well. If @p hash
+ * is @c NULL, the function returns immediately.
+ *
+ * Example:
+ * @code
+ * extern Eina_Hash *hash;
+ *
+ * eina_hash_free(hash);
+ * hash = NULL;
+ * @endcode
+ */
+EAPI void
+eina_hash_free(Eina_Hash *hash)
+{
+   int i;
+
+   EINA_MAGIC_CHECK_HASH(hash);
+   EINA_SAFETY_ON_NULL_RETURN(hash);
+
+   if (hash->buckets)
+     {
+        for (i = 0; i < hash->size; i++)
+           eina_rbtree_delete(hash->buckets[i], EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash);
+        free(hash->buckets);
+     }
+   free(hash);
+}
+
+/**
+ * Free the given hash table buckets resources.
+ *
+ * @param hash The hash table whose buckets have to be freed.
+ *
+ * This function frees up all the memory allocated to storing the
+ * buckets of @p hash, and call the free callback on all hash table
+ * buckets if it has been passed to the hash table at creation time,
+ * then frees the buckets. If no free callback has been passed, no
+ * buckets value will be freed. If @p hash is @c NULL, the function
+ * returns immediately.
+ */
+EAPI void
+eina_hash_free_buckets(Eina_Hash *hash)
+{
+   int i;
+
+   EINA_MAGIC_CHECK_HASH(hash);
+   EINA_SAFETY_ON_NULL_RETURN(hash);
+
+   if (hash->buckets)
+     {
+        for (i = 0; i < hash->size; i++)
+           eina_rbtree_delete(hash->buckets[i],
+                              EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash);
+        free(hash->buckets);
+        hash->buckets = NULL;
+        hash->population = 0;
+     }
+}
+
+/**
+ * @brief Add an entry to the given hash table.
+ *
+ * @param hash The given hash table.
+ * @param key A unique key.
+ * @param key_length The length of the key.
+ * @param key_hash The hash that will always match key.
+ * @param data The data to associate with the string given by the key.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function adds @p key to @p hash. @p hash, @p key and @p data
+ * can be @c NULL, in that case #EINA_FALSE is returned. @p key is
+ * expected to be a unique string within the hash table. Otherwise,
+ * one cannot be sure which inserted data pointer will be accessed
+ * with @ref eina_hash_find, and removed with @ref eina_hash_del. Do
+ * not forget to count '\\0' for string when setting the value of
+ * @p key_length. @p key_hash is expected to always match
+ * @p key. Otherwise, one cannot be sure to find it again with @ref
+ * eina_hash_find_by_hash. Key strings are case sensitive. If an error
+ * occurs, eina_error_get() should be used to determine if an
+ * allocation error occurred during this function. This function
+ * returns #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ */
+EAPI Eina_Bool
+eina_hash_add_by_hash(Eina_Hash  *hash,
+                      const void *key,
+                      int         key_length,
+                      int         key_hash,
+                      const void *data)
+{
+   return eina_hash_add_alloc_by_hash(hash,
+                                     key,
+                                      key_length,
+                                      key_length,
+                                      key_hash,
+                                      data);
+}
+
+/**
+ * @brief Add an entry to the given hash table and do not duplicate the string key.
+ *
+ * @param hash The given hash table.  Can be @c NULL.
+ * @param key A unique key.  Can be @c NULL.
+ * @param key_length Should be the length of @p key (don't forget to count '\\0' for string).
+ * @param key_hash The hash that will always match key.
+ * @param data Data to associate with the string given by @p key.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function adds @p key to @p hash. @p hash, @p key and @p data
+ * can be @c NULL, in that case #EINA_FALSE is returned. @p key is
+ * expected to be a unique string within the hash table. Otherwise,
+ * one cannot be sure which inserted data pointer will be accessed
+ * with @ref eina_hash_find, and removed with @ref eina_hash_del. This
+ * function does not make a copy of @p key so it must be a string
+ * constant or stored elsewhere (in the object being added). Do
+ * not forget to count '\\0' for string when setting the value of
+ * @p key_length. @p key_hash is expected to always match
+ * @p key. Otherwise, one cannot be sure to find it again with @ref
+ * eina_hash_find_by_hash. Key strings are case sensitive. If an error
+ * occurs, eina_error_get() should be used to determine if an
+ * allocation error occurred during this function. This function
+ * returns #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ */
+EAPI Eina_Bool
+eina_hash_direct_add_by_hash(Eina_Hash  *hash,
+                             const void *key,
+                             int         key_length,
+                             int         key_hash,
+                             const void *data)
+{
+   return eina_hash_add_alloc_by_hash(hash, key, key_length, 0, key_hash, data);
+}
+
+/**
+ * @brief Add an entry to the given hash table.
+ *
+ * @param hash The given hash table.
+ * @param key A unique key.
+ * @param data Data to associate with the string given by @p key.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function adds @p key to @p hash. @p hash, @p key and @p data
+ * can be @c NULL, in that case #EINA_FALSE is returned. @p key is
+ * expected to be unique within the hash table. Key uniqueness varies
+ * depending on the type of @p hash: a stringshared @ref Eina_Hash
+ * need only have unique pointers for keys, but the strings in the
+ * pointers may be identical. All other hash types require the strings
+ * themselves to be unique. Failure to use sufficient uniqueness will
+ * result in unexpected results when inserting data pointers accessed
+ * with eina_hash_find(), and removed with eina_hash_del(). Key
+ * strings are case sensitive. If an error occurs, eina_error_get()
+ * should be used to determine if an allocation error occurred during
+ * this function. This function returns #EINA_FALSE if an error
+ * occurred, #EINA_TRUE otherwise.
+ */
+EAPI Eina_Bool
+eina_hash_add(Eina_Hash *hash, const void *key, const void *data)
+{
+   unsigned int key_length;
+   int key_hash;
+
+   EINA_MAGIC_CHECK_HASH(hash);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,               EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data,              EINA_FALSE);
+
+   key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
+   key_hash = hash->key_hash_cb(key, key_length);
+
+   return eina_hash_add_alloc_by_hash(hash, key, key_length, key_length, key_hash, data);
+}
+
+/**
+ * @brief Add an entry to the given hash table without duplicating the string key.
+ *
+ * @param hash The given hash table.  Can be @c NULL.
+ * @param key A unique key.  Can be @c NULL.
+ * @param data Data to associate with the string given by @p key.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function adds @p key to @p hash. @p hash, @p key and @p data
+ * can be @c NULL, in that case #EINA_FALSE is returned. @p key is
+ * expected to be unique within the hash table. Key uniqueness varies
+ * depending on the type of @p hash: a stringshared @ref Eina_Hash
+ * need only have unique pointers for keys, but the strings in the
+ * pointers may be identical. All other hash types require the strings
+ * themselves to be unique. Failure to use sufficient uniqueness will
+ * result in unexpected results when inserting data pointers accessed
+ * with eina_hash_find(), and removed with eina_hash_del(). This
+ * function does not make a copy of @p key, so it must be a string
+ * constant or stored elsewhere ( in the object being added). Key
+ * strings are case sensitive. If an error occurs, eina_error_get()
+ * should be used to determine if an allocation error occurred during
+ * this function. This function returns #EINA_FALSE if an error
+ * occurred, #EINA_TRUE otherwise.
+ */
+EAPI Eina_Bool
+eina_hash_direct_add(Eina_Hash *hash, const void *key, const void *data)
+{
+   int key_length;
+   int key_hash;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,               EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data,              EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
+   key_hash = hash->key_hash_cb(key, key_length);
+
+   return eina_hash_add_alloc_by_hash(hash, key, key_length, 0, key_hash, data);
+}
+
+/**
+ * @brief Remove the entry identified by a key and a key hash from the given hash table.
+ *
+ * @param hash The given hash table.
+ * @param key The key.
+ * @param key_length The length of the key.
+ * @param key_hash The hash that always match the key.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function removes the entry identified by @p key and
+ * @p key_hash from @p hash. If a free function was given to the
+ * callback on creation, it will be called for the data being
+ * deleted. Do not forget to count '\\0' for string when setting the
+ * value of @p key_length. If @p hash or @p key are @c NULL, the
+ * functions returns immediately #EINA_FALSE. This function returns
+ * #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * @note if you don't have the key_hash, use eina_hash_del_by_key() instead.
+ * @note if you don't have the key, use eina_hash_del_by_data() instead.
+ */
+EAPI Eina_Bool
+eina_hash_del_by_key_hash(Eina_Hash *hash,
+                          const void *key,
+                          int key_length,
+                          int key_hash)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
+
+   return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, NULL);
+}
+
+/**
+ * @brief Remove the entry identified by a key from the given hash table.
+ *
+ * This version will calculate key length and hash by using functions
+ * provided to hash creation function.
+ *
+ * @param hash The given hash table.
+ * @param key  The key.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function removes the entry identified by @p key from @p
+ * hash. The key length and hash will be calculated automatically by
+ * using functiond provided to has creation function. If a free
+ * function was given to the callback on creation, it will be called
+ * for the data being deleted. If @p hash or @p key are @c NULL, the
+ * functions returns immediately #EINA_FALSE. This function returns
+ * #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * @note if you already have the key_hash, use eina_hash_del_by_key_hash() instead.
+ * @note if you don't have the key, use eina_hash_del_by_data() instead.
+ */
+EAPI Eina_Bool
+eina_hash_del_by_key(Eina_Hash *hash, const void *key)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
+
+   return _eina_hash_del_by_key(hash, key, NULL);
+}
+
+/**
+ * @brief Remove the entry identified by a data from the given hash table.
+ *
+ * This version is slow since there is no quick access to nodes based on data.
+ *
+ * @param hash The given hash table.
+ * @param data The data value to search and remove.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *          thing goes fine.
+ *
+ * This function removes the entry identified by @p data from @p
+ * hash. If a free function was given to the callback on creation, it
+ * will be called for the data being deleted. If @p hash or @p data
+ * are @c NULL, the functions returns immediately #EINA_FALSE. This
+ * function returns #EINA_FALSE if an error occurred, #EINA_TRUE
+ * otherwise.
+ *
+ * @note if you already have the key, use eina_hash_del_by_key() or eina_hash_del_by_key_hash() instead.
+ */
+EAPI Eina_Bool
+eina_hash_del_by_data(Eina_Hash *hash, const void *data)
+{
+   Eina_Hash_Element *hash_element;
+   Eina_Hash_Head *hash_head;
+   int key_hash;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   hash_element = _eina_hash_find_by_data(hash, data, &key_hash, &hash_head);
+   if (!hash_element)
+      goto error;
+
+   if (hash_element->tuple.data != data)
+      goto error;
+
+   return _eina_hash_del_by_hash_el(hash, hash_element, hash_head, key_hash);
+
+error:
+   return EINA_FALSE;
+}
+
+/**
+ * @brief Remove the entry identified by a key and a key hash or a
+ * data from the given hash table.
+ *
+ * If @p key is @c NULL, then @p data is used to find a match to
+ * remove.
+ *
+ * @param hash The given hash table.
+ * @param key The key.
+ * @param key_length The length of the key.
+ * @param key_hash The hash that always match the key.
+ * @param data The data pointer to remove if the key is @c NULL.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function removes the entry identified by @p key and
+ * @p key_hash, or @p data, from @p hash. If a free function was given to
+ * the  callback on creation, it will be called for the data being
+ * deleted. If @p hash is @c NULL, the functions returns immediately
+ * #EINA_FALSE. If @p key is @c NULL, then @p key_hash and @p key_hash
+ * are ignored and @p data is used to find a match to remove,
+ * otherwise @p key and @p key_hash are used and @p data is not
+ * required and can be @c NULL. Do not forget to count '\\0' for
+ * string when setting the value of @p key_length. This function
+ * returns #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * @note if you know you already have the key, use eina_hash_del_by_key_hash(),
+ *       if you know you don't have the key, use eina_hash_del_by_data()
+ *       directly.
+ */
+EAPI Eina_Bool
+eina_hash_del_by_hash(Eina_Hash *hash,
+                      const void *key,
+                      int key_length,
+                      int key_hash,
+                      const void *data)
+{
+   Eina_Bool ret;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (key)
+      ret = _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, data);
+   else
+      ret = eina_hash_del_by_data(hash, data);
+
+   return ret;
+}
+
+/**
+ * @brief Remove the entry identified by a key or a data from the given
+ * hash table.
+ *
+ * @param hash The given hash table.
+ * @param key  The key.
+ * @param data The data pointer to remove if the key is @c NULL.
+ * @return #EINA_FALSE if an error occurred, #EINA_TRUE otherwise.
+ *
+ * This function removes the entry identified by @p key or @p data
+ * from @p hash. If a free function was given to the
+ * callback on creation, it will be called for the data being
+ * deleted. If @p hash is @c NULL, the functions returns immediately
+ * #EINA_FALSE. If @p key is @c NULL, then @p data is used to find the a
+ * match to remove, otherwise @p key is used and @p data is not
+ * required and can be @c NULL. This function returns #EINA_FALSE if
+ * an error occurred, #EINA_TRUE otherwise.
+ *
+ * @note if you know you already have the key, use
+ *       eina_hash_del_by_key() or eina_hash_del_by_key_hash(). If you
+ *       know you don't have the key, use eina_hash_del_by_data()
+ *       directly.
+ */
+EAPI Eina_Bool
+eina_hash_del(Eina_Hash *hash, const void *key, const void *data)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!key)
+     return eina_hash_del_by_data(hash, data);
+
+   return _eina_hash_del_by_key(hash, key, data);
+}
+
+/**
+ * @brief Retrieve a specific entry in the given hash table.
+ *
+ * @param hash The given hash table.
+ * @param key The key of the entry to find.
+ * @param key_length The length of the key.
+ * @param key_hash The hash that always match the key
+ * @return The data pointer for the stored entry on success, @c NULL
+ * otherwise.
+ *
+ * This function retrieves the entry associated to @p key of length
+ * @p key_length in @p hash. @p key_hash is the hash that always match
+ * @p key. It is ignored if @p key is @c NULL. Do not forget to count
+ * '\\0' for string when setting the value of @p key_length. If
+ * @p hash is @c NULL, this function returns immediately @c NULL. This
+ * function returns the data pointer on success, @c NULL otherwise.
+ */
+EAPI void *
+eina_hash_find_by_hash(const Eina_Hash *hash,
+                       const void *key,
+                       int key_length,
+                       int key_hash)
+{
+   Eina_Hash_Head *hash_head;
+   Eina_Hash_Element *hash_element;
+   Eina_Hash_Tuple tuple;
+
+   if (!hash)
+      return NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   tuple.key = key;
+   tuple.key_length = key_length;
+   tuple.data = NULL;
+
+   hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
+   if (hash_element)
+      return hash_element->tuple.data;
+
+   return NULL;
+}
+
+/**
+ * @brief Retrieve a specific entry in the given hash table.
+ *
+ * @param hash The given hash table.
+ * @param key The key of the entry to find.
+ * @return The data pointer for the stored entry on success, @c NULL
+ * otherwise.
+ *
+ * This function retrieves the entry associated to @p key in
+ * @p hash. If @p hash is @c NULL, this function returns immediately
+ * @c NULL. This function returns the data pointer on success, @c NULL
+ * otherwise.
+ */
+EAPI void *
+eina_hash_find(const Eina_Hash *hash, const void *key)
+{
+   int key_length;
+   int hash_num;
+
+   if (!hash)
+      return NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,               NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
+   hash_num = hash->key_hash_cb(key, key_length);
+
+   return eina_hash_find_by_hash(hash, key, key_length, hash_num);
+}
+
+/**
+ * @brief Modify the entry pointer at the specified key and returns
+ * the old entry.
+ *
+ * @param hash The given hash table.
+ * @param key The key of the entry to modify.
+ * @param key_length Should be the length of @p key (don't forget to count '\\0' for string).
+ * @param key_hash The hash that always match the key. Ignored if @p key is @c NULL.
+ * @param data The data to replace the old entry, if it exists.
+ * @return The data pointer for the old stored entry, or @c NULL if not
+ *          found. If an existing entry is not found, nothing is added to the
+ *          hash.
+ */
+EAPI void *
+eina_hash_modify_by_hash(Eina_Hash *hash,
+                         const void *key,
+                         int key_length,
+                         int key_hash,
+                         const void *data)
+{
+   Eina_Hash_Head *hash_head;
+   Eina_Hash_Element *hash_element;
+   void *old_data = NULL;
+   Eina_Hash_Tuple tuple;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,  NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   tuple.key = key;
+   tuple.key_length = key_length;
+   tuple.data = NULL;
+
+   hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
+   if (hash_element)
+     {
+        old_data = hash_element->tuple.data;
+        hash_element->tuple.data = (void *)data;
+     }
+
+   return old_data;
+}
+
+/**
+ * @brief Modify the entry pointer at the specified key and return the
+ * old entry or add the entry if not found.
+ *
+ * @param hash The given hash table.
+ * @param key The key of the entry to modify.
+ * @param data The data to replace the old entry
+ * @return The data pointer for the old stored entry, or @c NULL
+ * otherwise.
+ *
+ * This function modifies the data of @p key with @p data in @p
+ * hash. If no entry is found, @p data is added to @p hash with the
+ * key @p key. On success this function returns the old entry,
+ * otherwise it returns @c NULL. To check for errors, use
+ * eina_error_get().
+ */
+EAPI void *
+eina_hash_set(Eina_Hash *hash, const void *key, const void *data)
+{
+   Eina_Hash_Tuple tuple;
+   Eina_Hash_Head *hash_head;
+   Eina_Hash_Element *hash_element;
+   int key_length;
+   int key_hash;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,               NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data,              NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
+   key_hash = hash->key_hash_cb(key, key_length);
+
+   tuple.key = key;
+   tuple.key_length = key_length;
+   tuple.data = NULL;
+
+   hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
+   if (hash_element)
+     {
+        void *old_data = NULL;
+
+        old_data = hash_element->tuple.data;
+        hash_element->tuple.data = (void *)data;
+        return old_data;
+     }
+
+   eina_hash_add_alloc_by_hash(hash,
+                               key,
+                               key_length,
+                               key_length,
+                               key_hash,
+                               data);
+   return NULL;
+}
+/**
+ * @brief Modify the entry pointer at the specified key and return the old entry.
+ * @param hash The given hash table.
+ * @param key The key of the entry to modify.
+ * @param data The data to replace the old entry.
+ * @return The data pointer for the old stored entry on success, or
+ * @c NULL otherwise.
+ *
+ * This function modifies the data of @p key with @p data in @p
+ * hash. If no entry is found, nothing is added to @p hash. On success
+ * this function returns the old entry, otherwise it returns @c NULL.
+ */
+EAPI void *
+eina_hash_modify(Eina_Hash *hash, const void *key, const void *data)
+{
+   int key_length;
+   int hash_num;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(key,               NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data,              NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
+   hash_num = hash->key_hash_cb(key, key_length);
+
+   return eina_hash_modify_by_hash(hash, key, key_length, hash_num, data);
+}
+
+/**
+ * @brief Change the key associated with a data without triggering the
+ * free callback.
+ *
+ * @param hash    The given hash table.
+ * @param old_key The current key associated with the data
+ * @param new_key The new key to associate data with
+ * @return EINA_FALSE in any case but success, EINA_TRUE on success.
+ *
+ * This function allows for the move of data from one key to another,
+ * but does not call the Eina_Free_Cb associated with the hash table
+ * when destroying the old key.
+ */
+EAPI Eina_Bool
+eina_hash_move(Eina_Hash *hash, const void *old_key, const void *new_key)
+{
+   Eina_Free_Cb hash_free_cb;
+   const void *data;
+   Eina_Bool result = EINA_FALSE;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(old_key,           EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(new_key,           EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   data = eina_hash_find(hash, old_key);
+   if (!data) goto error;
+
+   hash_free_cb = hash->data_free_cb;
+   hash->data_free_cb = NULL;
+
+   eina_hash_del(hash, old_key, data);
+   result = eina_hash_add(hash, new_key, data);
+
+   hash->data_free_cb = hash_free_cb;
+
+error:
+   return result;
+}
+
+/*============================================================================*
+*                                Iterator                                    *
+*============================================================================*/
+
+/**
+ * @brief Call a function on every member stored in the hash table
+ *
+ * @param hash The hash table whose members will be walked
+ * @param func The function to call on each parameter
+ * @param fdata The data pointer to pass to the function being called
+ *
+ * This function goes through every entry in the hash table @p hash and calls
+ * the function @p func on each member. The function should @b not modify the
+ * hash table contents if it returns 1. @b If the hash table contents are
+ * modified by this function or the function wishes to stop processing it must
+ * return 0, otherwise return 1 to keep processing.
+ *
+ * Example:
+ * @code
+ * extern Eina_Hash *hash;
+ *
+ * Eina_Bool hash_fn(const Eina_Hash *hash, const void *key, void *data, void *fdata)
+ * {
+ *   printf("Func data: %s, Hash entry: %s / %p\n", fdata, (const char *)key, data);
+ *   return 1;
+ * }
+ *
+ * int main(int argc, char **argv)
+ * {
+ *   char *hash_fn_data;
+ *
+ *   hash_fn_data = strdup("Hello World");
+ *   eina_hash_foreach(hash, hash_fn, hash_fn_data);
+ *   free(hash_fn_data);
+ * }
+ * @endcode
+ */
+EAPI void
+eina_hash_foreach(const Eina_Hash *hash,
+                  Eina_Hash_Foreach func,
+                  const void *fdata)
+{
+   Eina_Iterator *it;
+   Eina_Hash_Foreach_Data foreach;
+
+   EINA_MAGIC_CHECK_HASH(hash);
+   EINA_SAFETY_ON_NULL_RETURN(hash);
+   EINA_SAFETY_ON_NULL_RETURN(func);
+
+   foreach.cb = func;
+   foreach.fdata = fdata;
+
+   it = eina_hash_iterator_tuple_new(hash);
+   if (!it)
+      return;
+   eina_iterator_foreach(it, EINA_EACH_CB(_eina_foreach_cb), &foreach);
+
+   eina_iterator_free(it);
+}
+
+/**
+ * @brief Returned a new iterator associated to hash data.
+ *
+ * @param hash The hash.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to
+ * @p hash. If @p hash is not populated, this function still returns a
+ * valid iterator that will always return false on
+ * eina_iterator_next(), thus keeping API sane.
+ *
+ * If the memory can not be allocated, @c NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the hash structure changes then the iterator becomes
+ * invalid. That is, if you add or remove items this iterator behavior
+ * is undefined and your program may crash.
+ */
+EAPI Eina_Iterator *
+eina_hash_iterator_data_new(const Eina_Hash *hash)
+{
+   Eina_Iterator_Hash *it;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_Hash));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   it->hash = hash;
+   it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_data_get_content);
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_hash_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free);
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+   EINA_MAGIC_SET(it,            EINA_MAGIC_HASH_ITERATOR);
+
+   return &it->iterator;
+}
+
+/**
+ * @brief Returned a new iterator associated to hash keys.
+ *
+ * @param hash The hash.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * hash. If @p hash is not populated, this function still returns a
+ * valid iterator that will always return false on
+ * eina_iterator_next(), thus keeping API sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the hash structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove items this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_hash_iterator_key_new(const Eina_Hash *hash)
+{
+   Eina_Iterator_Hash *it;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_Hash));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   it->hash = hash;
+   it->get_content = FUNC_ITERATOR_GET_CONTENT(
+         _eina_hash_iterator_key_get_content);
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_hash_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free);
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+   EINA_MAGIC_SET(it,            EINA_MAGIC_HASH_ITERATOR);
+
+   return &it->iterator;
+}
+
+/**
+ * @brief Returned a new iterator associated to hash keys and data.
+ *
+ * @param hash The hash.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * hash. If @p hash is not populated, this function still returns a
+ * valid iterator that will always return false on
+ * eina_iterator_next(), thus keeping API sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @note iterator data will provide values as Eina_Hash_Tuple that should not
+ *   be modified!
+ *
+ * @warning if the hash structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove items this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_hash_iterator_tuple_new(const Eina_Hash *hash)
+{
+   Eina_Iterator_Hash *it;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_Hash));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   it->hash = hash;
+   it->get_content = FUNC_ITERATOR_GET_CONTENT(
+         _eina_hash_iterator_tuple_get_content);
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_hash_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free);
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+   EINA_MAGIC_SET(it,            EINA_MAGIC_HASH_ITERATOR);
+
+   return &it->iterator;
+}
+
+/* Common hash functions */
+
+/* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html)
+   used by WebCore (http://webkit.org/blog/8/hashtables-part-2/) */
+EAPI int
+eina_hash_superfast(const char *key, int len)
+{
+   int hash = len, tmp;
+   int rem;
+
+   rem = len & 3;
+   len >>= 2;
+
+   /* Main loop */
+   for (; len > 0; len--)
+     {
+        hash += get16bits(key);
+        tmp = (get16bits(key + 2) << 11) ^ hash;
+        hash = (hash << 16) ^ tmp;
+        key += 2 * sizeof (uint16_t);
+        hash += hash >> 11;
+     }
+
+   /* Handle end cases */
+   switch (rem)
+     {
+      case 3:
+         hash += get16bits(key);
+         hash ^= hash << 16;
+         hash ^= key[sizeof (uint16_t)] << 18;
+         hash += hash >> 11;
+         break;
+
+      case 2:
+         hash += get16bits(key);
+         hash ^= hash << 11;
+         hash += hash >> 17;
+         break;
+
+      case 1:
+         hash += *key;
+         hash ^= hash << 10;
+         hash += hash >> 1;
+     }
+
+   /* Force "avalanching" of final 127 bits */
+   hash ^= hash << 3;
+   hash += hash >> 5;
+   hash ^= hash << 4;
+   hash += hash >> 17;
+   hash ^= hash << 25;
+   hash += hash >> 6;
+
+   return hash;
+}
+
+/**
+ * @}
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_inlist.c b/tests/suite/ecore/src/lib/eina_inlist.c
new file mode 100644 (file)
index 0000000..9ebc623
--- /dev/null
@@ -0,0 +1,693 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Vincent Torri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_error.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_inlist.h"
+
+/* FIXME: TODO please, refactor this :) */
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+typedef struct _Eina_Iterator_Inlist Eina_Iterator_Inlist;
+typedef struct _Eina_Accessor_Inlist Eina_Accessor_Inlist;
+
+struct _Eina_Iterator_Inlist
+{
+   Eina_Iterator iterator;
+   const Eina_Inlist *head;
+   const Eina_Inlist *current;
+};
+
+struct _Eina_Accessor_Inlist
+{
+   Eina_Accessor accessor;
+
+   const Eina_Inlist *head;
+   const Eina_Inlist *current;
+
+   unsigned int index;
+};
+
+static Eina_Bool
+eina_inlist_iterator_next(Eina_Iterator_Inlist *it, void **data) {
+   if (!it->current)
+      return EINA_FALSE;
+
+   if (data)
+      *data = (void *)it->current;
+
+   it->current = it->current->next;
+
+   return EINA_TRUE;
+}
+
+static Eina_Inlist *
+eina_inlist_iterator_get_container(Eina_Iterator_Inlist *it) {
+   return (Eina_Inlist *)it->head;
+}
+
+static void
+eina_inlist_iterator_free(Eina_Iterator_Inlist *it) {
+   free(it);
+}
+
+static Eina_Bool
+eina_inlist_accessor_get_at(Eina_Accessor_Inlist *it,
+                            unsigned int idx,
+                            void **data) {
+   const Eina_Inlist *over;
+   unsigned int middle;
+   unsigned int i;
+
+   if (it->index == idx)
+      over = it->current;
+   else if (idx > it->index)
+      /* Looking after current. */
+      for (i = it->index, over = it->current;
+           i < idx && over;
+           ++i, over = over->next)
+         ;
+   else
+     {
+        middle = it->index >> 1;
+
+        if (idx > middle)
+           /* Looking backward from current. */
+           for (i = it->index, over = it->current;
+                i > idx && over;
+                --i, over = over->prev)
+              ;
+        else
+           /* Looking from the start. */
+           for (i = 0, over = it->head;
+                i < idx && over;
+                ++i, over = over->next)
+              ;
+     }
+
+   if (!over)
+      return EINA_FALSE;
+
+   it->current = over;
+   it->index = idx;
+
+   if (data)
+      *data = (void *)over;
+
+   return EINA_TRUE;
+}
+
+static Eina_Inlist *
+eina_inlist_accessor_get_container(Eina_Accessor_Inlist *it) {
+   return (Eina_Inlist *)it->head;
+}
+
+static void
+eina_inlist_accessor_free(Eina_Accessor_Inlist *it) {
+   free(it);
+}
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Inline_List_Group Inline List
+ *
+ * @brief These functions provide inline list management.
+ *
+ * Inline lists mean its nodes pointers are part of same memory as
+ * data. This has the benefit of framenting memory less and avoiding
+ * @c node->data indirection, but has the drawback of elements only
+ * being able to be part of one single inlist at same time. But it is
+ * possible to have inlist nodes to be part of regular lists created
+ * with eina_list_append() or eina_list_prepend().
+ *
+ * Inline lists have its purposes, but if you don't know them go with
+ * regular lists instead.
+ *
+ * @code
+ * #include <Eina.h>
+ * #include <stdio.h>
+ *
+ * int
+ * main(void)
+ * {
+ *    struct my_struct {
+ *       EINA_INLIST;
+ *       int a, b;
+ *    } *d, *cur;
+ *    Eina_Inlist *list, *itr;
+ *
+ *    eina_init();
+ *
+ *    d = malloc(sizeof(*d));
+ *    d->a = 1;
+ *    d->b = 10;
+ *    list = eina_inlist_append(NULL, EINA_INLIST_GET(d));
+ *
+ *    d = malloc(sizeof(*d));
+ *    d->a = 2;
+ *    d->b = 20;
+ *    list = eina_inlist_append(list, EINA_INLIST_GET(d));
+ *
+ *    d = malloc(sizeof(*d));
+ *    d->a = 3;
+ *    d->b = 30;
+ *    list = eina_inlist_prepend(list, EINA_INLIST_GET(d));
+ *
+ *    printf("list=%p\n", list);
+ *    EINA_INLIST_FOREACH(list, cur)
+ *      printf("\ta=%d, b=%d\n", cur->a, cur->b);
+ *
+ *    list = eina_inlist_remove(list, EINA_INLIST_GET(d));
+ *    free(d);
+ *    printf("list=%p\n", list);
+ *    for (itr = list; itr != NULL; itr = itr->next)
+ *      {
+ *  cur = EINA_INLIST_CONTAINER_GET(itr, struct my_struct);
+ *  printf("\ta=%d, b=%d\n", cur->a, cur->b);
+ *      }
+ *
+ *    while (list)
+ *      {
+ *  Eina_Inlist *aux = list;
+ *  list = eina_inlist_remove(list, list);
+ *  free(aux);
+ *      }
+ *
+ *    eina_shutdown();
+ *
+ *    return 0;
+ * }
+ * @endcode
+ *
+ * @{
+ */
+
+/**
+ * Add a new node to end of list.
+ *
+ * @note this code is meant to be fast, appends are O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a new_l is considered to be in no list. If it was in another
+ *       list before, please eina_inlist_remove() it before adding. No
+ *       check of @a new_l prev and next pointers is done, so it' safe
+ *       to have them uninitialized.
+ *
+ * @param list existing list head or NULL to create a new list.
+ * @param new_l new list node, must not be NULL.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_append(Eina_Inlist *list, Eina_Inlist *new_l)
+{
+   Eina_Inlist *l;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list);
+
+   new_l->next = NULL;
+   if (!list)
+     {
+        new_l->prev = NULL;
+        new_l->last = new_l;
+        return new_l;
+     }
+
+   if (list->last)
+      l = list->last;
+   else
+      for (l = list; (l) && (l->next); l = l->next)
+         ;
+
+   l->next = new_l;
+   new_l->prev = l;
+   list->last = new_l;
+   return list;
+}
+
+/**
+ * Add a new node to beginning of list.
+ *
+ * @note this code is meant to be fast, prepends are O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a new_l is considered to be in no list. If it was in another
+ *       list before, please eina_inlist_remove() it before adding. No
+ *       check of @a new_l prev and next pointers is done, so it' safe
+ *       to have them uninitialized.
+ *
+ * @param list existing list head or NULL to create a new list.
+ * @param new_l new list node, must not be NULL.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_prepend(Eina_Inlist *list, Eina_Inlist *new_l)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list);
+
+   new_l->prev = NULL;
+   if (!list)
+     {
+        new_l->next = NULL;
+        new_l->last = new_l;
+        return new_l;
+     }
+
+   new_l->next = list;
+   list->prev = new_l;
+   new_l->last = list->last;
+   list->last = NULL;
+   return new_l;
+}
+
+/**
+ * Add a new node after the given relative item in list.
+ *
+ * @note this code is meant to be fast, appends are O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a new_l is considered to be in no list. If it was in another
+ *       list before, please eina_inlist_remove() it before adding. No
+ *       check of @a new_l prev and next pointers is done, so it' safe
+ *       to have them uninitialized.
+ *
+ * @note @a relative is considered to be inside @a list, no checks are
+ *       done to confirm that and giving nodes from different lists
+ *       will lead to problems. Giving NULL @a relative is the same as
+ *       eina_list_append().
+ *
+ * @param list existing list head or NULL to create a new list.
+ * @param new_l new list node, must not be NULL.
+ * @param relative reference node, @a new_l will be added after it.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_append_relative(Eina_Inlist *list,
+                            Eina_Inlist *new_l,
+                            Eina_Inlist *relative)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list);
+
+   if (relative)
+     {
+        if (relative->next)
+          {
+             new_l->next = relative->next;
+             relative->next->prev = new_l;
+          }
+        else
+           new_l->next = NULL;
+
+        relative->next = new_l;
+        new_l->prev = relative;
+        if (!new_l->next)
+           list->last = new_l;
+
+        return list;
+     }
+
+   return eina_inlist_append(list, new_l);
+}
+
+/**
+ * Add a new node before the given relative item in list.
+ *
+ * @note this code is meant to be fast, prepends are O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a new_l is considered to be in no list. If it was in another
+ *       list before, please eina_inlist_remove() it before adding. No
+ *       check of @a new_l prev and next pointers is done, so it' safe
+ *       to have them uninitialized.
+ *
+ * @note @a relative is considered to be inside @a list, no checks are
+ *       done to confirm that and giving nodes from different lists
+ *       will lead to problems. Giving NULL @a relative is the same as
+ *       eina_list_prepend().
+ *
+ * @param list existing list head or NULL to create a new list.
+ * @param new_l new list node, must not be NULL.
+ * @param relative reference node, @a new_l will be added before it.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_prepend_relative(Eina_Inlist *list,
+                             Eina_Inlist *new_l,
+                             Eina_Inlist *relative)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(new_l, list);
+
+   if (relative)
+     {
+        new_l->prev = relative->prev;
+        new_l->next = relative;
+        relative->prev = new_l;
+        if (new_l->prev)
+          {
+             new_l->prev->next = new_l;
+             /* new_l->next could not be NULL, as it was set to 'relative' */
+             assert(new_l->next);
+             return list;
+          }
+        else
+          {
+             /* new_l->next could not be NULL, as it was set to 'relative' */
+             assert(new_l->next);
+
+             new_l->last = list->last;
+             list->last = NULL;
+             return new_l;
+          }
+     }
+
+   return eina_inlist_prepend(list, new_l);
+}
+
+/**
+ * Remove node from list.
+ *
+ * @note this code is meant to be fast, removals are O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a item is considered to be inside @a list, no checks are
+ *       done to confirm that and giving nodes from different lists
+ *       will lead to problems, specially if @a item is the head since
+ *       it will be different from @a list and the wrong new head will
+ *       be returned.
+ *
+ * @param list existing list head, must not be NULL.
+ * @param item existing list node, must not be NULL.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_remove(Eina_Inlist *list, Eina_Inlist *item)
+{
+   Eina_Inlist *return_l;
+
+   /* checkme */
+   EINA_SAFETY_ON_NULL_RETURN_VAL(list, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(item, list);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL
+      ((item != list) && (!item->prev) && (!item->next), list);
+
+   if (item->next)
+      item->next->prev = item->prev;
+
+   if (item->prev)
+     {
+        item->prev->next = item->next;
+        return_l = list;
+     }
+   else
+     {
+        return_l = item->next;
+        if (return_l)
+           return_l->last = list->last;
+     }
+
+   if (item == list->last)
+      list->last = item->prev;
+
+   item->next = NULL;
+   item->prev = NULL;
+   return return_l;
+}
+
+/**
+ * Move existing node to beginning of list.
+ *
+ * @note this code is meant to be fast, promotion is O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a item is considered to be inside @a list, no checks are
+ *       done to confirm that and giving nodes from different lists
+ *       will lead to problems.
+ *
+ * @param list existing list head or NULL to create a new list.
+ * @param item list node to move to beginning (head), must not be NULL.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_promote(Eina_Inlist *list, Eina_Inlist *item)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(list, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(item, list);
+
+   if (item == list)
+      return list;
+
+   if (item->next)
+      item->next->prev = item->prev;
+
+   item->prev->next = item->next;
+
+   if (list->last == item)
+      list->last = item->prev;
+
+   item->next = list;
+   item->prev = NULL;
+   item->last = list->last;
+
+   list->prev = item;
+   list->last = NULL;
+
+   return item;
+}
+
+/**
+ * Move existing node to end of list.
+ *
+ * @note this code is meant to be fast, demoting is O(1) and do not
+ *       walk @a list anyhow.
+ *
+ * @note @a item is considered to be inside @a list, no checks are
+ *       done to confirm that and giving nodes from different lists
+ *       will lead to problems.
+ *
+ * @param list existing list head or NULL to create a new list.
+ * @param item list node to move to end (tail), must not be NULL.
+ *
+ * @return the new list head. Use it and not given @a list anymore.
+ */
+EAPI Eina_Inlist *
+eina_inlist_demote(Eina_Inlist *list, Eina_Inlist *item)
+{
+   Eina_Inlist *l;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(list, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(item, list);
+
+   if (list->last == item)
+      return list;
+
+   if (!list->last)
+     {
+        for (l = list; l->next; l = l->next)
+           ;
+        list->last = l;
+     }
+
+   l = list;
+   if (item->prev)
+      item->prev->next = item->next;
+   else
+      l = item->next;
+
+   item->next->prev = item->prev;
+
+   list->last->next = item;
+   item->prev = list->last;
+   item->next = NULL;
+
+   l->last = item;
+   return l;
+}
+
+/**
+ * Find given node in list, returns itself if found, NULL if not.
+ *
+ * @warning this is an expensive call and have O(n) cost, possibly
+ *    walking the whole list.
+ *
+ * @param list existing list to search @a item in, must not be NULL.
+ * @param item what to search for, must not be NULL.
+ *
+ * @return @a item if found, NULL if not.
+ */
+EAPI Eina_Inlist *
+eina_inlist_find(Eina_Inlist *list, Eina_Inlist *item)
+{
+   Eina_Inlist *l;
+
+   for (l = list; l; l = l->next) {
+        if (l == item)
+           return item;
+     }
+   return NULL;
+}
+
+/**
+ * @brief Get the count of the number of items in a list.
+ *
+ * @param list The list whose count to return.
+ * @return The number of members in the list.
+ *
+ * This function returns how many members @p list contains. If the
+ * list is @c NULL, 0 is returned.
+ *
+ * @warning This is an order-N operation and so the time will depend
+ *    on the number of elements on the list, that is, it might become
+ *    slow for big lists!
+ */
+EAPI unsigned int
+eina_inlist_count(const Eina_Inlist *list)
+{
+   const Eina_Inlist *l;
+   unsigned int i = 0;
+
+   for (l = list; l; l = l->next)
+      i++;
+
+   return i;
+}
+
+/**
+ * @brief Returned a new iterator associated to a list.
+ *
+ * @param list The list.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * list. If @p list is @c NULL or the count member of @p list is less
+ * or equal than 0, this function still returns a valid iterator that
+ * will always return false on eina_iterator_next(), thus keeping API
+ * sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the list structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove nodes this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_inlist_iterator_new(const Eina_Inlist *list)
+{
+   Eina_Iterator_Inlist *it;
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_Inlist));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   it->head = list;
+   it->current = list;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(eina_inlist_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         eina_inlist_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(eina_inlist_iterator_free);
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   return &it->iterator;
+}
+
+/**
+ * @brief Returned a new accessor associated to a list.
+ *
+ * @param list The list.
+ * @return A new accessor.
+ *
+ * This function returns a newly allocated accessor associated to
+ * @p list. If @p list is @c NULL or the count member of @p list is
+ * less or equal than 0, this function returns NULL. If the memory can
+ * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is
+ * set. Otherwise, a valid accessor is returned.
+ */
+EAPI Eina_Accessor *
+eina_inlist_accessor_new(const Eina_Inlist *list)
+{
+   Eina_Accessor_Inlist *ac;
+
+        eina_error_set(0);
+   ac = calloc(1, sizeof (Eina_Accessor_Inlist));
+   if (!ac)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   ac->head = list;
+   ac->current = list;
+   ac->index = 0;
+
+   ac->accessor.version = EINA_ACCESSOR_VERSION;
+   ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_inlist_accessor_get_at);
+   ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(
+         eina_inlist_accessor_get_container);
+   ac->accessor.free = FUNC_ACCESSOR_FREE(eina_inlist_accessor_free);
+
+   EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR);
+
+   return &ac->accessor;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_iterator.c b/tests/suite/ecore/src/lib/eina_iterator.c
new file mode 100644 (file)
index 0000000..66dbbf4
--- /dev/null
@@ -0,0 +1,260 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_iterator.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char EINA_MAGIC_ITERATOR_STR[] = "Eina Iterator";
+
+#define EINA_MAGIC_CHECK_ITERATOR(d)                            \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_ITERATOR)) {              \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_ITERATOR); }                  \
+     } while(0)
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the iterator module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the iterator module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_iterator_init(void)
+{
+   return eina_magic_string_set(EINA_MAGIC_ITERATOR, EINA_MAGIC_ITERATOR_STR);
+}
+
+/**
+ * @internal
+ * @brief Shut down the iterator module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the iterator module set up by
+ * eina_iterator_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_iterator_shutdown(void)
+{
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Iterator_Group Iterator Functions
+ *
+ * @brief These functions manage iterators on containers.
+ *
+ * These functions allow to access elements of a container in a
+ * generic way, without knowing which container is used (a bit like
+ * iterators in the C++ STL). Iterators only allows sequential access
+ * (that is, from an element to the next one). For random access, see
+ * @ref Eina_Accessor_Group.
+ *
+ * An iterator is created from container data types, so no creation
+ * function is available here. An iterator is deleted with
+ * eina_iterator_free(). To get the data and iterate, use
+ * eina_iterator_next(). To call a function on all the elements of a
+ * container, use eina_iterator_foreach().
+ *
+ * @{
+ */
+
+/**
+ * @brief Free an iterator.
+ *
+ * @param iterator The iterator to free.
+ *
+ * This function frees @p iterator if it is not @c NULL;
+ */
+EAPI void
+eina_iterator_free(Eina_Iterator *iterator)
+{
+   EINA_MAGIC_CHECK_ITERATOR(iterator);
+   EINA_SAFETY_ON_NULL_RETURN(iterator);
+   EINA_SAFETY_ON_NULL_RETURN(iterator->free);
+   iterator->free(iterator);
+}
+
+/**
+ * @brief Return the container of an iterator.
+ *
+ * @param iterator The iterator.
+ * @return The container which created the iterator.
+ *
+ * This function returns the container which created @p iterator. If
+ * @p iterator is @c NULL, this function returns @c NULL.
+ */
+EAPI void *
+eina_iterator_container_get(Eina_Iterator *iterator)
+{
+   EINA_MAGIC_CHECK_ITERATOR(iterator);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(iterator,                NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(iterator->get_container, NULL);
+   return iterator->get_container(iterator);
+}
+
+/**
+ * @brief Return the value of the current element and go to the next one.
+ *
+ * @param iterator The iterator.
+ * @param data The data of the element.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function returns the value of the current element pointed by
+ * @p iterator in @p data, then goes to the next element. If @p
+ * iterator is @c NULL or if a problem occurred, #EINA_FALSE is
+ * returned, otherwise #EINA_TRUE is returned.
+ */
+EAPI Eina_Bool
+eina_iterator_next(Eina_Iterator *iterator, void **data)
+{
+   if (!iterator)
+      return EINA_FALSE;
+
+   EINA_MAGIC_CHECK_ITERATOR(iterator);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(iterator,       EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(iterator->next, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(data,           EINA_FALSE);
+   return iterator->next(iterator, data);
+}
+
+/**
+ * @brief Iterate over the container and execute a callback on each element.
+ *
+ * @param iterator The iterator.
+ * @param cb The callback called on each iteration.
+ * @param fdata The data passed to the callback.
+ *
+ * This function iterates over the elements pointed by @p iterator,
+ * beginning from the current element. For Each element, the callback
+ * @p cb is called with the data @p fdata. If @p iterator is @c NULL,
+ * the function returns immediately. Also, if @p cb returns @c
+ * EINA_FALSE, the iteration stops at that point.
+ */
+EAPI void
+eina_iterator_foreach(Eina_Iterator *iterator,
+                      Eina_Each_Cb cb,
+                      const void *fdata)
+{
+   const void *container;
+   void *data;
+
+   EINA_MAGIC_CHECK_ITERATOR(iterator);
+   EINA_SAFETY_ON_NULL_RETURN(iterator);
+   EINA_SAFETY_ON_NULL_RETURN(iterator->get_container);
+   EINA_SAFETY_ON_NULL_RETURN(iterator->next);
+   EINA_SAFETY_ON_NULL_RETURN(cb);
+
+   if (!eina_iterator_lock(iterator)) return ;
+
+   container = iterator->get_container(iterator);
+   while (iterator->next(iterator, &data) == EINA_TRUE) {
+        if (cb(container, data, (void *)fdata) != EINA_TRUE)
+          goto on_exit;
+     }
+
+ on_exit:
+   (void) eina_iterator_unlock(iterator);
+}
+
+/**
+ * @brief Lock the container of the iterator.
+ *
+ * @param iterator The iterator.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * If the container of the @p iterator permit it, it will be locked.
+ * If @p iterator is @c NULL or if a problem occurred, #EINA_FALSE is
+ * returned, otherwise #EINA_TRUE is returned. If the container
+ * is not lockable, it will return EINA_TRUE.
+ */
+EAPI Eina_Bool
+eina_iterator_lock(Eina_Iterator *iterator)
+{
+   EINA_MAGIC_CHECK_ITERATOR(iterator);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, EINA_FALSE);
+
+   if (iterator->lock)
+      return iterator->lock(iterator);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Unlock the container of the iterator.
+ *
+ * @param iterator The iterator.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * If the container of the @p iterator permit it and was previously
+ * locked, it will be unlocked. If @p iterator is @c NULL or if a
+ * problem occurred, #EINA_FALSE is returned, otherwise #EINA_TRUE
+ * is returned. If the container is not lockable, it will return
+ * EINA_TRUE.
+ */
+EAPI Eina_Bool
+eina_iterator_unlock(Eina_Iterator *iterator)
+{
+   EINA_MAGIC_CHECK_ITERATOR(iterator);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(iterator, EINA_FALSE);
+
+   if (iterator->unlock)
+      return iterator->unlock(iterator);
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_lalloc.c b/tests/suite/ecore/src/lib/eina_lalloc.c
new file mode 100644 (file)
index 0000000..b1e62b7
--- /dev/null
@@ -0,0 +1,158 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_lalloc.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+struct _Eina_Lalloc
+{
+   void *data;
+   int num_allocated;
+   int num_elements;
+   int acc;
+   Eina_Lalloc_Alloc alloc_cb;
+   Eina_Lalloc_Free free_cb;
+};
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Lalloc_Group Lazy allocator
+ *
+ * @{
+ */
+
+EAPI Eina_Lalloc *eina_lalloc_new(void *data,
+                                  Eina_Lalloc_Alloc alloc_cb,
+                                  Eina_Lalloc_Free free_cb,
+                                  int num_init)
+{
+   Eina_Lalloc *a;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(alloc_cb, NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(free_cb,  NULL);
+
+   a = calloc(1, sizeof(Eina_Lalloc));
+   a->data = data;
+   a->alloc_cb = alloc_cb;
+   a->free_cb = free_cb;
+   if (num_init > 0)
+     {
+        a->num_allocated = num_init;
+        a->alloc_cb(a->data, a->num_allocated);
+     }
+
+   return a;
+}
+
+EAPI void eina_lalloc_free(Eina_Lalloc *a)
+{
+   EINA_SAFETY_ON_NULL_RETURN(a);
+   EINA_SAFETY_ON_NULL_RETURN(a->free_cb);
+   a->free_cb(a->data);
+   free(a);
+}
+
+EAPI Eina_Bool eina_lalloc_element_add(Eina_Lalloc *a)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(a,           EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(a->alloc_cb, EINA_FALSE);
+
+   if (a->num_elements == a->num_allocated)
+     {
+        if (a->alloc_cb(a->data, (1 << a->acc)) == EINA_TRUE)
+          {
+             a->num_allocated = (1 << a->acc);
+             a->acc++;
+          }
+        else
+           return EINA_FALSE;
+     }
+
+   a->num_elements++;
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool eina_lalloc_elements_add(Eina_Lalloc *a, int num)
+{
+   int tmp;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(a,           EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(a->alloc_cb, EINA_FALSE);
+
+   tmp = a->num_elements + num;
+   if (tmp > a->num_allocated)
+     {
+        int allocated;
+        int acc;
+
+        allocated = a->num_allocated;
+        acc = a->acc;
+
+        while (tmp > allocated)
+          {
+             allocated = (1 << acc);
+             acc++;
+          }
+
+        if (a->alloc_cb(a->data, allocated) == EINA_TRUE)
+          {
+             a->num_allocated = allocated;
+             a->acc = acc;
+          }
+        else
+           return EINA_FALSE;
+     }
+
+   a->num_elements += num;
+
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_list.c b/tests/suite/ecore/src/lib/eina_list.c
new file mode 100644 (file)
index 0000000..e301476
--- /dev/null
@@ -0,0 +1,2184 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Gustavo Sverzut Barbieri, Tilman Sauerbeck,
+ *                         Vincent Torri, Cedric Bail, Jorge Luis Zapata Muga,
+ *                         Corey Donohoe, Arnaud de Turckheim, Alexandre Becoulet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2004 ncn
+ * Copyright (C) 2006 Sebastian Dransfeld
+ * Copyright (C) 2007 Christopher Michael
+ *
+ *  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 and its Copyright notices. In addition publicly
+ *  documented acknowledgment must be given that this software has been used if no
+ *  source code of this software is made available publicly. This includes
+ *  acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+ *  documents or any documentation provided with any product containing this
+ *  software. This License does not apply to any software that links to the
+ *  libraries provided by this software (statically or dynamically), but only to
+ *  the software provided.
+
+ *  Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice
+ *  and it's intent.
+
+ *  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.
+ */
+
+
+/**
+ * @page tutorial_list_page List Tutorial
+ *
+ * to be written...
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_error.h"
+#include "eina_log.h"
+#include "eina_mempool.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_list.h"
+
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char EINA_MAGIC_LIST_STR[] = "Eina List";
+static const char EINA_MAGIC_LIST_ITERATOR_STR[] = "Eina List Iterator";
+static const char EINA_MAGIC_LIST_ACCESSOR_STR[] = "Eina List Accessor";
+static const char EINA_MAGIC_LIST_ACCOUNTING_STR[] = "Eina List Accounting";
+
+
+#define EINA_MAGIC_CHECK_LIST(d, ...)                           \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST))                  \
+          {                                                           \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST);                    \
+             return __VA_ARGS__;                                     \
+          }                                                           \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_LIST_ITERATOR(d, ...)                  \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST_ITERATOR))         \
+          {                                                           \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST_ITERATOR);           \
+             return __VA_ARGS__;                                     \
+          }                                                           \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_LIST_ACCESSOR(d, ...)                  \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST_ACCESSOR))         \
+          {                                                           \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST_ACCESSOR);           \
+             return __VA_ARGS__;                                     \
+          }                                                           \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_LIST_ACCOUNTING(d)                     \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_LIST_ACCOUNTING))       \
+          {                                                           \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_LIST_ACCOUNTING);         \
+             return;                                                 \
+          }                                                           \
+     } while(0)
+
+#define EINA_LIST_SORT_STACK_SIZE 32
+
+typedef struct _Eina_Iterator_List Eina_Iterator_List;
+typedef struct _Eina_Accessor_List Eina_Accessor_List;
+
+struct _Eina_Iterator_List
+{
+   Eina_Iterator iterator;
+
+   const Eina_List *head;
+   const Eina_List *current;
+
+   EINA_MAGIC
+};
+
+struct _Eina_Accessor_List
+{
+   Eina_Accessor accessor;
+
+   const Eina_List *head;
+   const Eina_List *current;
+
+   unsigned int index;
+
+   EINA_MAGIC
+};
+
+static Eina_Mempool *_eina_list_mp = NULL;
+static Eina_Mempool *_eina_list_accounting_mp = NULL;
+static int _eina_list_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_list_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_list_log_dom, __VA_ARGS__)
+
+static inline Eina_List_Accounting *
+_eina_list_mempool_accounting_new(__UNUSED__ Eina_List *list)
+{
+   Eina_List_Accounting *tmp;
+
+   tmp =
+      eina_mempool_malloc(_eina_list_accounting_mp,
+                          sizeof (Eina_List_Accounting));
+   if (!tmp)
+      return NULL;
+
+   EINA_MAGIC_SET(tmp, EINA_MAGIC_LIST_ACCOUNTING);
+
+   return tmp;
+}
+static inline void
+_eina_list_mempool_accounting_free(Eina_List_Accounting *accounting)
+{
+   EINA_MAGIC_CHECK_LIST_ACCOUNTING(accounting);
+
+   EINA_MAGIC_SET(accounting, EINA_MAGIC_NONE);
+   eina_mempool_free(_eina_list_accounting_mp, accounting);
+}
+
+static inline Eina_List *
+_eina_list_mempool_list_new(__UNUSED__ Eina_List *list)
+{
+   Eina_List *tmp;
+
+   tmp = eina_mempool_malloc(_eina_list_mp, sizeof (Eina_List));
+   if (!tmp)
+      return NULL;
+
+   EINA_MAGIC_SET(tmp, EINA_MAGIC_LIST);
+
+   return tmp;
+}
+static inline void
+_eina_list_mempool_list_free(Eina_List *list)
+{
+   EINA_MAGIC_CHECK_LIST(list);
+
+   list->accounting->count--;
+   if (list->accounting->count == 0)
+      _eina_list_mempool_accounting_free(list->accounting);
+
+   EINA_MAGIC_SET(list, EINA_MAGIC_NONE);
+   eina_mempool_free(_eina_list_mp, list);
+}
+
+static Eina_List *
+_eina_list_setup_accounting(Eina_List *list)
+{
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   list->accounting = _eina_list_mempool_accounting_new(list);
+   if (!list->accounting)
+      goto on_error;
+
+   list->accounting->last = list;
+   list->accounting->count = 1;
+
+   return list;
+
+on_error:
+   _eina_list_mempool_list_free(list);
+   return NULL;
+}
+
+static inline void
+_eina_list_update_accounting(Eina_List *list, Eina_List *new_list)
+{
+   EINA_MAGIC_CHECK_LIST(list);
+   EINA_MAGIC_CHECK_LIST(new_list);
+
+   list->accounting->count++;
+   new_list->accounting = list->accounting;
+}
+
+#if 0
+static Eina_Mempool2 _eina_list_mempool =
+{
+   sizeof(Eina_List),
+   320,
+   0, NULL, NULL
+};
+static Eina_Mempool2 _eina_list_accounting_mempool =
+{
+   sizeof(Eina_List_Accounting),
+   80,
+   0, NULL, NULL
+};
+#endif
+
+static Eina_Bool
+eina_list_iterator_next(Eina_Iterator_List *it, void **data)
+{
+   EINA_MAGIC_CHECK_LIST_ITERATOR(it, EINA_FALSE);
+
+   if (!it->current)
+      return EINA_FALSE;
+
+   *data = eina_list_data_get(it->current);
+
+   it->current = eina_list_next(it->current);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+eina_list_iterator_prev(Eina_Iterator_List *it, void **data)
+{
+   EINA_MAGIC_CHECK_LIST_ITERATOR(it, EINA_FALSE);
+
+   if (!it->current)
+      return EINA_FALSE;
+
+   *data = eina_list_data_get(it->current);
+
+   it->current = eina_list_prev(it->current);
+
+   return EINA_TRUE;
+}
+
+static Eina_List *
+eina_list_iterator_get_container(Eina_Iterator_List *it)
+{
+   EINA_MAGIC_CHECK_LIST_ITERATOR(it, NULL);
+
+   return (Eina_List *)it->head;
+}
+
+static void
+eina_list_iterator_free(Eina_Iterator_List *it)
+{
+   EINA_MAGIC_CHECK_LIST_ITERATOR(it);
+
+   MAGIC_FREE(it);
+}
+
+static Eina_Bool
+eina_list_accessor_get_at(Eina_Accessor_List *it, unsigned int idx, void **data)
+{
+   const Eina_List *over;
+   unsigned int middle;
+   unsigned int i;
+
+   EINA_MAGIC_CHECK_LIST_ACCESSOR(it, EINA_FALSE);
+
+   if (idx >= eina_list_count(it->head))
+      return EINA_FALSE;
+
+   if (it->index == idx)
+      over = it->current;
+   else if (idx > it->index)
+     {
+        /* After current position. */
+        middle = ((eina_list_count(it->head) - it->index) >> 1) + it->index;
+
+        if (idx > middle)
+           /* Go backward from the end. */
+           for (i = eina_list_count(it->head) - 1,
+                over = eina_list_last(it->head);
+                i > idx && over;
+                --i, over = eina_list_prev(over))
+              ;
+        else
+           /* Go forward from current. */
+           for (i = it->index, over = it->current;
+                i < idx && over;
+                ++i, over = eina_list_next(over))
+              ;
+     }
+   else
+     {
+        /* Before current position. */
+        middle = it->index >> 1;
+
+        if (idx > middle)
+           /* Go backward from current. */
+           for (i = it->index, over = it->current;
+                i > idx && over;
+                --i, over = eina_list_prev(over))
+              ;
+        else
+           /* Go forward from start. */
+           for (i = 0, over = it->head;
+                i < idx && over;
+                ++i, over = eina_list_next(over))
+              ;
+     }
+
+   if (!over)
+      return EINA_FALSE;
+
+   it->current = over;
+   it->index = idx;
+
+   *data = eina_list_data_get(it->current);
+   return EINA_TRUE;
+}
+
+static Eina_List *
+eina_list_accessor_get_container(Eina_Accessor_List *it)
+{
+   EINA_MAGIC_CHECK_LIST_ACCESSOR(it, NULL);
+
+   return (Eina_List *)it->head;
+}
+
+static void
+eina_list_accessor_free(Eina_Accessor_List *it)
+{
+   EINA_MAGIC_CHECK_LIST_ACCESSOR(it);
+
+   MAGIC_FREE(it);
+}
+
+static Eina_List *
+eina_list_sort_rebuild_prev(Eina_List *list)
+{
+   Eina_List *prev = NULL;
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   for (; list; list = list->next)
+     {
+        list->prev = prev;
+        prev = list;
+     }
+
+   return prev;
+}
+
+static Eina_List *
+eina_list_sort_merge(Eina_List *a, Eina_List *b, Eina_Compare_Cb func)
+{
+   Eina_List *first, *last;
+
+   if (func(a->data, b->data) < 0)
+      a = (last = first = a)->next;
+   else
+      b = (last = first = b)->next;
+
+   while (a && b)
+      if (func(a->data, b->data) < 0)
+         a = (last = last->next = a)->next;
+      else
+         b = (last = last->next = b)->next;
+
+   last->next = a ? a : b;
+
+   return first;
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the list module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the list module of Eina. It is called by
+ * eina_init().
+ *
+ * This function creates mempool to speed up list node and accounting
+ * management, using EINA_MEMPOOL environment variable if it is set to
+ * choose the memory pool type to use.
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_list_init(void)
+{
+   const char *choice, *tmp;
+
+   _eina_list_log_dom = eina_log_domain_register("eina_list",
+                                                 EINA_LOG_COLOR_DEFAULT);
+   if (_eina_list_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_list");
+        return EINA_FALSE;
+     }
+
+#ifdef EINA_DEFAULT_MEMPOOL
+   choice = "pass_through";
+#else
+   choice = "chained_mempool";
+#endif
+   tmp = getenv("EINA_MEMPOOL");
+   if (tmp && tmp[0])
+      choice = tmp;
+
+   _eina_list_mp = eina_mempool_add
+         (choice, "list", NULL, sizeof(Eina_List), 320);
+   if (!_eina_list_mp)
+     {
+        ERR("ERROR: Mempool for list cannot be allocated in list init.");
+        goto on_init_fail;
+     }
+
+   _eina_list_accounting_mp = eina_mempool_add
+         (choice, "list_accounting", NULL, sizeof(Eina_List_Accounting), 80);
+   if (!_eina_list_accounting_mp)
+     {
+        ERR(
+           "ERROR: Mempool for list accounting cannot be allocated in list init.");
+        eina_mempool_del(_eina_list_mp);
+        goto on_init_fail;
+     }
+
+#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
+   EMS(EINA_MAGIC_LIST);
+   EMS(EINA_MAGIC_LIST_ITERATOR);
+   EMS(EINA_MAGIC_LIST_ACCESSOR);
+   EMS(EINA_MAGIC_LIST_ACCOUNTING);
+#undef EMS
+
+   return EINA_TRUE;
+
+on_init_fail:
+   eina_log_domain_unregister(_eina_list_log_dom);
+   _eina_list_log_dom = -1;
+   return EINA_FALSE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the list module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the list module set up by
+ * eina_list_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_list_shutdown(void)
+{
+   eina_mempool_del(_eina_list_accounting_mp);
+   eina_mempool_del(_eina_list_mp);
+
+   eina_log_domain_unregister(_eina_list_log_dom);
+   _eina_list_log_dom = -1;
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_List_Group List
+ *
+ * @brief These functions provide double linked list management.
+ *
+ * For more information, you can look at the @ref tutorial_list_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Append the given data to the given linked list.
+ *
+ * @param list The given list.
+ * @param data The data to append.
+ * @return A list pointer.
+ *
+ * This function appends @p data to @p list. If @p list is @c NULL, a
+ * new list is returned. On success, a new list pointer that should be
+ * used in place of the one given to this function is
+ * returned. Otherwise, the old pointer is returned.
+ *
+ * The following example code demonstrates how to ensure that the
+ * given data has been successfully appended.
+ *
+ * @code
+ * Eina_List *list = NULL;
+ * extern void *my_data;
+ *
+ * list = eina_list_append(list, my_data);
+ * if (eina_error_get())
+ *   {
+ *     fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n");
+ *     exit(-1);
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_append(Eina_List *list, const void *data)
+{
+   Eina_List *l, *new_l;
+
+   eina_error_set(0);
+   new_l = _eina_list_mempool_list_new(list);
+   if (!new_l)
+      return list;
+
+   new_l->next = NULL;
+   new_l->data = (void *)data;
+   if (!list)
+     {
+        new_l->prev = NULL;
+        return _eina_list_setup_accounting(new_l);
+     }
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   l = list->accounting->last;
+   list->accounting->last = new_l;
+
+   l->next = new_l;
+   new_l->prev = l;
+
+   _eina_list_update_accounting(list, new_l);
+   return list;
+}
+
+/**
+ * @brief Prepends the given data to the given linked list.
+ *
+ * @param list The given list.
+ * @param data The data to prepend.
+ * @return A list pointer.
+ *
+ * This function prepends @p data to @p list. If @p list is @c NULL, a
+ * new list is returned. On success, a new list pointer that should be
+ * used in place of the one given to this function is
+ * returned. Otherwise, the old pointer is returned.
+ *
+ * The following example code demonstrates how to ensure that the
+ * given data has been successfully prepended.
+ *
+ * Example:
+ * @code
+ * Eina_List *list = NULL;
+ * extern void *my_data;
+ *
+ * list = eina_list_prepend(list, my_data);
+ * if (eina_error_get())
+ *   {
+ *     fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n");
+ *     exit(-1);
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_prepend(Eina_List *list, const void *data)
+{
+   Eina_List *new_l;
+
+   eina_error_set(0);
+   new_l = _eina_list_mempool_list_new(list);
+   if (!new_l)
+      return list;
+
+   new_l->prev = NULL;
+   new_l->next = list;
+   new_l->data = (void *)data;
+
+   if (!list)
+      return _eina_list_setup_accounting(new_l);
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   list->prev = new_l;
+
+   _eina_list_update_accounting(list, new_l);
+
+   return new_l;
+}
+
+/**
+ * @brief Insert the given data into the given linked list after the specified data.
+ *
+ * @param list The given linked list.
+ * @param data The data to insert.
+ * @param relative The data to insert after.
+ * @return A list pointer.
+ *
+ * This function inserts @p data to @p list after @p relative. If
+ * @p relative is not in the list, @p data is appended to the end of
+ * the list.  If @p list is @c NULL, a  new list is returned. If there
+ * are multiple instances of @p relative in the list, @p data is
+ * inserted after the first instance.On success, a new list pointer
+ * that should be used in place of the one given to this function is
+ * returned. Otherwise, the old pointer is returned.
+ *
+ * The following example code demonstrates how to ensure that the
+ * given data has been successfully inserted.
+ *
+ * @code
+ * Eina_List *list = NULL;
+ * extern void *my_data;
+ * extern void *relative_member;
+ *
+ * list = eina_list_append(list, relative_member);
+ * if (eina_error_get())
+ *   {
+ *     fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n");
+ *     exit(-1);
+ *   }
+ * list = eina_list_append_relative(list, my_data, relative_member);
+ * if (eina_error_get())
+ *   {
+ *     fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n");
+ *     exit(-1);
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_append_relative(Eina_List *list,
+                          const void *data,
+                          const void *relative)
+{
+   Eina_List *l;
+   void *list_data;
+
+   if (list)
+      EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   EINA_LIST_FOREACH(list, l, list_data)
+   {
+      if (list_data == relative)
+         return eina_list_append_relative_list(list, data, l);
+   }
+
+   return eina_list_append(list, data);
+}
+
+/**
+ * @brief Append a list node to a linked list after the specified member
+ *
+ * @param list The given linked list.
+ * @param data The data to insert.
+ * @param relative The list node to insert after.
+ * @return A list pointer.
+ *
+ * This function inserts @p data to @p list after the list node
+ * @p relative. If @p list or @p relative are @c NULL, @p data is just
+ * appended to @p list using eina_list_append(). If @p list is
+ * @c NULL, a  new list is returned. If there are multiple instances
+ * of @p relative in the list, @p data is inserted after the first
+ * instance. On success, a new list pointer that should be used in
+ * place of the one given to this function is returned. Otherwise, the
+ * old pointer is returned.
+ */
+EAPI Eina_List *
+eina_list_append_relative_list(Eina_List *list,
+                               const void *data,
+                               Eina_List *relative)
+{
+   Eina_List *new_l;
+
+   if ((!list) || (!relative))
+      return eina_list_append(list, data);
+
+   eina_error_set(0);
+   new_l = _eina_list_mempool_list_new(list);
+   if (!new_l)
+      return list;
+
+   EINA_MAGIC_CHECK_LIST(relative, NULL);
+   new_l->next = relative->next;
+   new_l->data = (void *)data;
+
+   if (relative->next)
+      relative->next->prev = new_l;
+
+   relative->next = new_l;
+   new_l->prev = relative;
+
+   _eina_list_update_accounting(list, new_l);
+
+   if (!new_l->next)
+      new_l->accounting->last = new_l;
+
+   return list;
+}
+
+/**
+ * @brief Prepend a data pointer to a linked list before the specified member
+ *
+ * @param list The given linked list.
+ * @param data The data to insert.
+ * @param relative The data to insert before.
+ * @return A list pointer.
+ *
+ * This function inserts @p data to @p list before @p relative. If
+ * @p relative is not in the list, @p data is prepended to the list
+ * with eina_list_prepend(). If @p list is @c NULL, a  new list is
+ * returned. If there are multiple instances of @p relative in the
+ * list, @p data is inserted before the first instance. On success, a
+ * new list pointer that should be used in place of the one given to
+ * this function is returned. Otherwise, the old pointer is returned.
+ *
+ * The following code example demonstrates how to ensure that the
+ * given data has been successfully inserted.
+ *
+ * @code
+ * Eina_List *list = NULL;
+ * extern void *my_data;
+ * extern void *relative_member;
+ *
+ * list = eina_list_append(list, relative_member);
+ * if (eina_error_get_error())
+ *   {
+ *     fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n");
+ *     exit(-1);
+ *   }
+ * list = eina_list_prepend_relative(list, my_data, relative_member);
+ * if (eina_error_get())
+ *   {
+ *     fprintf(stderr, "ERROR: Memory is low. List allocation failed.\n");
+ *     exit(-1);
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_prepend_relative(Eina_List *list,
+                           const void *data,
+                           const void *relative)
+{
+   Eina_List *l;
+   void *list_data;
+
+   if (list)
+      EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   EINA_LIST_FOREACH(list, l, list_data)
+   {
+      if (list_data == relative)
+         return eina_list_prepend_relative_list(list, data, l);
+   }
+   return eina_list_prepend(list, data);
+}
+
+/**
+ * @brief Prepend a list node to a linked list before the specified member
+ *
+ * @param list The given linked list.
+ * @param data The data to insert.
+ * @param relative The list node to insert before.
+ * @return A list pointer.
+ *
+ * This function inserts @p data to @p list before the list node
+ * @p relative. If @p list or @p relative are @c NULL, @p data is just
+ * prepended to @p list using eina_list_prepend(). If @p list is
+ * @c NULL, a  new list is returned. If there are multiple instances
+ * of @p relative in the list, @p data is inserted before the first
+ * instance. On success, a new list pointer that should be used in
+ * place of the one given to this function is returned. Otherwise, the
+ * old pointer is returned.
+ */
+EAPI Eina_List *
+eina_list_prepend_relative_list(Eina_List *list,
+                                const void *data,
+                                Eina_List *relative)
+{
+   Eina_List *new_l;
+
+   if ((!list) || (!relative))
+      return eina_list_prepend(list, data);
+
+   eina_error_set(0);
+   new_l = _eina_list_mempool_list_new(list);
+   if (!new_l)
+      return list;
+
+   EINA_MAGIC_CHECK_LIST(relative, NULL);
+
+   new_l->prev = relative->prev;
+   new_l->next = relative;
+   new_l->data = (void *)data;
+
+   if (relative->prev)
+      relative->prev->next = new_l;
+
+   relative->prev = new_l;
+
+   _eina_list_update_accounting(list, new_l);
+
+   if (new_l->prev)
+      return list;
+
+   return new_l;
+}
+
+/**
+ * @brief Insert a new node into a sorted list.
+ *
+ * @param list The given linked list, @b must be sorted.
+ * @param func The function called for the sort.
+ * @param data The data to insert sorted.
+ * @return A list pointer.
+ *
+ * This function inserts values into a linked list assuming it was
+ * sorted and the result will be sorted. If @p list is @c NULLL, a new
+ * list is returned. On success, a new list pointer that should be
+ * used in place of the one given to this function is
+ * returned. Otherwise, the old pointer is returned. See eina_error_get().
+ *
+ * @note O(log2(n)) comparisons (calls to @p func) average/worst case
+ * performance as it uses eina_list_search_sorted_near_list() and thus
+ * is bounded to that. As said in eina_list_search_sorted_near_list(),
+ * lists do not have O(1) access time, so walking to the correct node
+ * can be costly, consider worst case to be almost O(n) pointer
+ * dereference (list walk).
+ */
+EAPI Eina_List *
+eina_list_sorted_insert(Eina_List *list, Eina_Compare_Cb func, const void *data)
+{
+   Eina_List *lnear;
+   int cmp;
+
+   if (!list)
+      return eina_list_append(NULL, data);
+
+   lnear = eina_list_search_sorted_near_list(list, func, data, &cmp);
+   if (cmp < 0)
+      return eina_list_append_relative_list(list, data, lnear);
+   else
+      return eina_list_prepend_relative_list(list, data, lnear);
+}
+
+/**
+ * @brief Remove the first instance of the specified data from the given list.
+ *
+ * @param list The given list.
+ * @param data The specified data.
+ * @return A list pointer.
+ *
+ * This function removes the first instance of @p data from
+ * @p list. If the specified data is not in the given list (tihis
+ * include the case where @p data is @c NULL), nothing is done. If
+ * @p list is @c NULL, @c NULL is returned, otherwise a new list
+ * pointer that should be used in place of the one passed to this
+ * function.
+ */
+EAPI Eina_List *
+eina_list_remove(Eina_List *list, const void *data)
+{
+   Eina_List *l;
+
+   if (list)
+        EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   l = eina_list_data_find_list(list, data);
+   return eina_list_remove_list(list, l);
+}
+
+/**
+ * @brief Remove the specified data.
+ *
+ * @param list The given linked list.
+ * @param remove_list The list node which is to be removed.
+ * @return A list pointer.
+ *
+ * This function removes the list node @p remove_list from @p list and
+ * frees the list node structure @p remove_list. If @p list is
+ * @c NULL, this function returns @c NULL. If @p remove_list is
+ * @c NULL, it returns @p list, otherwise, a new list pointer that
+ * should be used in place of the one passed to this function.
+ *
+ * The following code gives an example (notice we use EINA_LIST_FOREACH
+ * instead of EINA_LIST_FOREACH_SAFE because we stop the loop after
+ * removing the current node).
+ *
+ * @code
+ * extern Eina_List *list;
+ * Eina_List *l;
+ * extern void *my_data;
+ * void *data
+ *
+ * EINA_LIST_FOREACH(list, l, data)
+ *   {
+ *     if (data == my_data)
+ *       {
+ *         list = eina_list_remove_list(list, l);
+ *         break;
+ *       }
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_remove_list(Eina_List *list, Eina_List *remove_list)
+{
+   Eina_List *return_l;
+
+   if (!list)
+      return NULL;
+
+   if (!remove_list)
+      return list;
+
+        EINA_MAGIC_CHECK_LIST(remove_list, NULL);
+
+   if (remove_list->next)
+      remove_list->next->prev = remove_list->prev;
+
+   if (remove_list->prev)
+     {
+        remove_list->prev->next = remove_list->next;
+        return_l = list;
+     }
+   else
+      return_l = remove_list->next;
+
+   if (remove_list == remove_list->accounting->last)
+     {
+        EINA_MAGIC_CHECK_LIST(list, NULL);
+        list->accounting->last = remove_list->prev;
+     }
+
+   _eina_list_mempool_list_free(remove_list);
+   return return_l;
+}
+
+/**
+ * @brief Free an entire list and all the nodes, ignoring the data contained.
+
+ * @param list The list to free
+ * @return A NULL pointer
+ *
+ * This function frees all the nodes of @p list. It does not free the
+ * data of the nodes. To free them, use #EINA_LIST_FREE.
+ */
+EAPI Eina_List *
+eina_list_free(Eina_List *list)
+{
+   Eina_List *l, *free_l;
+
+   if (!list)
+      return NULL;
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   for (l = list; l; )
+     {
+        free_l = l;
+        l = l->next;
+
+        _eina_list_mempool_list_free(free_l);
+     }
+
+   return NULL;
+}
+
+/**
+ * @brief Move the specified data to the head of the list.
+ *
+ * @param list The list handle to move the data.
+ * @param move_list The list node to move.
+ * @return A new list handle to replace the old one
+ *
+ * This function move @p move_list to the front of @p list. If list is
+ * @c NULL, @c NULL is returned. If @p move_list is @c NULL,
+ * @p list is returned. Otherwise, a new list pointer that should be
+ * used in place of the one passed to this function.
+ *
+ * Example:
+ * @code
+ * extern Eina_List *list;
+ * Eina_List *l;
+ * extern void *my_data;
+ * void *data;
+ *
+ * EINA_LIST_FOREACH(list, l, data)
+ *   {
+ *     if (data == my_data)
+ *       {
+ *         list = eina_list_promote_list(list, l);
+ *         break;
+ *       }
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_promote_list(Eina_List *list, Eina_List *move_list)
+{
+   if (!list)
+      return NULL;
+
+   if (!move_list)
+     {
+        return list; /* Promoting head to be head. */
+
+     }
+
+   if (move_list == list)
+      return list;
+
+   if (move_list->next == list)
+      return move_list;
+
+      EINA_MAGIC_CHECK_LIST(list,      NULL);
+      EINA_MAGIC_CHECK_LIST(move_list, NULL);
+
+   /* Remove the promoted item from the list. */
+   if (!move_list->prev)
+      move_list->next->prev = NULL;
+   else
+     {
+        move_list->prev->next = move_list->next;
+        if (move_list == list->accounting->last)
+           list->accounting->last = move_list->prev;
+        else
+           move_list->next->prev = move_list->prev;
+     }
+
+   /* Add the promoted item in the list. */
+   move_list->next = list;
+   move_list->prev = list->prev;
+   list->prev = move_list;
+   if (move_list->prev)
+      move_list->prev->next = move_list;
+
+   return move_list;
+}
+
+/**
+ * @brief Move the specified data to the tail of the list.
+ *
+ * @param list The list handle to move the data.
+ * @param move_list The list node to move.
+ * @return A new list handle to replace the old one
+ *
+ * This function move @p move_list to the back of @p list. If list is
+ * @c NULL, @c NULL is returned. If @p move_list is @c NULL,
+ * @p list is returned. Otherwise, a new list pointer that should be
+ * used in place of the one passed to this function.
+ *
+ * Example:
+ * @code
+ * extern Eina_List *list;
+ * Eina_List *l;
+ * extern void *my_data;
+ * void *data;
+ *
+ * EINA_LIST_FOREACH(list, l, data)
+ *   {
+ *     if (data == my_data)
+ *       {
+ *         list = eina_list_demote_list(list, l);
+ *         break;
+ *       }
+ *   }
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_demote_list(Eina_List *list, Eina_List *move_list)
+{
+   if (!list)
+      return NULL;
+
+   if (!move_list)
+     {
+        return list; /* Demoting tail to be tail. */
+
+     }
+
+   if (move_list == list->accounting->last)
+      return list;
+
+      EINA_MAGIC_CHECK_LIST(list,      NULL);
+      EINA_MAGIC_CHECK_LIST(move_list, NULL);
+
+   /* Update pointer list if necessary. */
+   if (list == move_list)
+     {
+        list = move_list->next; /* Remove the demoted item from the list. */
+
+     }
+
+   if (move_list->prev)
+      move_list->prev->next = move_list->next;
+
+   move_list->next->prev = move_list->prev;
+   /* Add the demoted item in the list. */
+   move_list->prev = list->accounting->last;
+   move_list->prev->next = move_list;
+   move_list->next = NULL;
+   list->accounting->last = move_list;
+
+   return list;
+}
+
+/**
+ * @brief Find a member of a list and return the member.
+ *
+ * @param list The list to search for a data.
+ * @param data The data pointer to find in the list.
+ * @return The found member data pointer if foun, @c NULL otherwise.
+ *
+ * This function searches in @p list from beginning to end for the
+ * first member whose data pointer is @p data. If it is found, @p data
+ * will be returned, otherwise NULL will be returned.
+ *
+ * Example:
+ * @code
+ * extern Eina_List *list;
+ * extern void *my_data;
+ *
+ * if (eina_list_data_find(list, my_data) == my_data)
+ *   {
+ *     printf("Found member %p\n", my_data);
+ *   }
+ * @endcode
+ */
+EAPI void *
+eina_list_data_find(const Eina_List *list, const void *data)
+{
+   if (eina_list_data_find_list(list, data))
+      return (void *)data;
+
+   return NULL;
+}
+
+/**
+ * @brief Find a member of a list and return the list node containing that member.
+ *
+ * @param list The list to search for data.
+ * @param data The data pointer to find in the list.
+ * @return The found members list node on success, @c NULL otherwise.
+ *
+ * This function searches in @p list from beginning to end for the
+ * first member whose data pointer is @p data. If it is found, the
+ * list node containing the specified member is returned, otherwise
+ * @c NULL is returned.
+ */
+EAPI Eina_List *
+eina_list_data_find_list(const Eina_List *list, const void *data)
+{
+   const Eina_List *l;
+   void *list_data;
+
+   if (list)
+      EINA_MAGIC_CHECK_LIST(list, NULL);
+
+      EINA_LIST_FOREACH(list, l, list_data)
+   {
+      if (list_data == data)
+         return (Eina_List *)l;
+   }
+
+   return NULL;
+}
+
+/**
+ * @brief Get the nth member's data pointer in a list.
+ *
+ * @param list The list to get the specified member number from.
+ * @param n The number of the element (0 being the first).
+ * @return The data pointer stored in the specified element.
+ *
+ * This function returns the data pointer of element number @p n, in
+ * the @p list. The first element in the array is element number 0. If
+ * the element number @p n does not exist, @c NULL is
+ * returned. Otherwise, the data of the found element is returned.
+ */
+EAPI void *
+eina_list_nth(const Eina_List *list, unsigned int n)
+{
+   Eina_List *l;
+
+   l = eina_list_nth_list(list, n);
+   return l ? l->data : NULL;
+}
+
+/**
+ * @brief Get the nth member's list node in a list.
+ *
+ * @param list The list to get the specfied member number from.
+ * @param n The number of the element (0 being the first).
+ * @return The list node stored in the numbered element.
+ *
+ * This function returns the list node of element number @p n, in
+ * @ list. The first element in the array is element number 0. If the
+ * element number @p n does not exist or @p list is @c NULL or @p n is
+ * greater than the count of elements in @p list minus 1, @c NULL is
+ * returned. Otherwise the list node stored in the numbered element is
+ * returned.
+ */
+EAPI Eina_List *
+eina_list_nth_list(const Eina_List *list, unsigned int n)
+{
+   const Eina_List *l;
+   unsigned int i;
+
+   if (list)
+      EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   /* check for non-existing nodes */
+   if ((!list) || (n > (list->accounting->count - 1)))
+      return NULL;
+
+   /* if the node is in the 2nd half of the list, search from the end
+    * else, search from the beginning.
+    */
+   if (n > (list->accounting->count / 2))
+      for (i = list->accounting->count - 1,
+           l = list->accounting->last;
+           l;
+           l = l->prev, i--)
+        {
+           if (i == n)
+              return (Eina_List *)l;
+        }
+   else
+      for (i = 0, l = list; l; l = l->next, i++)
+        {
+           if (i == n)
+              return (Eina_List *)l;
+        }
+
+   abort();
+}
+
+/**
+ * @brief Reverse all the elements in the list.
+ *
+ * @param list The list to reverse.
+ * @return The list head after it has been reversed.
+ *
+ * This function reverses the order of all elements in @p list, so the
+ * last member is now first, and so on. If @p list is @c NULL, this
+ * functon returns @c NULL.
+ *
+ * @note @b in-place: this will change the given list, so you should
+ * now point to the new list head that is returned by this function.
+ *
+ * @see eina_list_reverse_clone()
+ * @see eina_list_iterator_reversed_new()
+ */
+EAPI Eina_List *
+eina_list_reverse(Eina_List *list)
+{
+   Eina_List *l1, *l2;
+
+   if (!list)
+      return NULL;
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   l1 = list;
+   l2 = list->accounting->last;
+   while (l1 != l2)
+     {
+        void *data;
+
+        data = l1->data;
+        l1->data = l2->data;
+        l2->data = data;
+        l1 = l1->next;
+        if (l1 == l2)
+           break;
+
+        l2 = l2->prev;
+     }
+
+   return list;
+}
+
+/**
+ * @brief Clone (copy) all the elements in the list in reverse order.
+ *
+ * @param list The list to reverse.
+ * @return The new list that has been reversed.
+ *
+ * This function reverses the order of all elements in @p list, so the
+ * last member is now first, and so on. If @p list is @c NULL, this
+ * functon returns @c NULL. This returns a copy of the given list.
+ *
+ * @note @b copy: this will copy the list and you should then
+ * eina_list_free() when it is not required anymore.
+ *
+ * @see eina_list_reverse()
+ * @see eina_list_clone()
+ */
+EAPI Eina_List *
+eina_list_reverse_clone(const Eina_List *list)
+{
+   const Eina_List *l;
+   Eina_List *lclone;
+   void *data;
+
+   if (!list)
+      return NULL;
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   lclone = NULL;
+   EINA_LIST_FOREACH(list, l, data)
+   lclone = eina_list_prepend(lclone, data);
+
+   return lclone;
+}
+
+/**
+ * @brief Clone (copy) all the elements in the list in exact order.
+ *
+ * @param list The list to clone.
+ * @return The new list that has been cloned.
+ *
+ * This function clone in order of all elements in @p list. If @p list
+ * is @c NULL, this functon returns @c NULL. This returns a copy of
+ * the given list.
+ *
+ * @note @b copy: this will copy the list and you should then
+ * eina_list_free() when it is not required anymore.
+ *
+ * @see eina_list_reverse_clone()
+ */
+EAPI Eina_List *
+eina_list_clone(const Eina_List *list)
+{
+   const Eina_List *l;
+   Eina_List *lclone;
+   void *data;
+
+   if (!list)
+      return NULL;
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   lclone = NULL;
+   EINA_LIST_FOREACH(list, l, data)
+   lclone = eina_list_append(lclone, data);
+
+   return lclone;
+}
+
+/**
+ * @brief Sort a list according to the ordering func will return.
+ *
+ * @param list The list handle to sort.
+ * @param size The length of the list to sort.
+ * @param func A function pointer that can handle comparing the list data
+ * nodes.
+ * @return the new head of list.
+ *
+ * This function sorts @p list. @p size if the number of the first
+ * element to sort. If @p size is 0 or greater than the number of
+ * elements in @p list, all the elements are sorted. @p func is used to
+ * compare two elements of @p list. If @p list or @p func are @c NULL,
+ * this function returns @c NULL.
+ *
+ * @note @b in-place: this will change the given list, so you should
+ * now point to the new list head that is returned by this function.
+ *
+ * @note worst case is O(n * log2(n)) comparisons (calls to func()),
+ * O(n) comparisons average case. That means that for 1,000,000 list
+ * elements, sort will usually do 1,000,000 comparisons, but may do up
+ * to 20,000,000.
+ *
+ * Example:
+ * @code
+ * int
+ * sort_cb(const void *d1, const void *d2)
+ * {
+ *    const char *txt = NULL;
+ *    const char *txt2 = NULL;
+ *
+ *    if(!d1) return(1);
+ *    if(!d2) return(-1);
+ *
+ *    return(strcmp((const char*)d1, (const char*)d2));
+ * }
+ * extern Eina_List *list;
+ *
+ * list = eina_list_sort(list, eina_list_count(list), sort_cb);
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_sort(Eina_List *list, unsigned int size, Eina_Compare_Cb func)
+{
+   unsigned int i = 0;
+   unsigned int n = 0;
+   Eina_List *tail = list;
+   Eina_List *unsort = NULL;
+   Eina_List *stack[EINA_LIST_SORT_STACK_SIZE];
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(func, list);
+   if (!list)
+      return NULL;
+
+   EINA_MAGIC_CHECK_LIST(list, NULL);
+
+   /* if the caller specified an invalid size, sort the whole list */
+   if ((size == 0) ||
+       (size > list->accounting->count))
+      size = list->accounting->count;
+
+   if (size != list->accounting->count)
+     {
+        unsort = eina_list_nth_list(list, size);
+        if (unsort)
+           unsort->prev->next = NULL;
+     }
+
+   while (tail)
+     {
+        unsigned int idx, tmp;
+
+        Eina_List *a = tail;
+        Eina_List *b = tail->next;
+
+        if (!b)
+          {
+             stack[i++] = a;
+             break;
+          }
+
+        tail = b->next;
+
+        if (func(a->data, b->data) < 0)
+           ((stack[i++] = a)->next = b)->next = 0;
+        else
+           ((stack[i++] = b)->next = a)->next = 0;
+
+        tmp = n++;
+        for (idx = n ^ tmp; idx &= idx - 1; i--)
+           stack[i - 2] = eina_list_sort_merge(stack[i - 2], stack[i - 1], func);
+     }
+
+   while (i-- > 1)
+      stack[i - 1] = eina_list_sort_merge(stack[i - 1], stack[i], func);
+
+   list = stack[0];
+   tail = eina_list_sort_rebuild_prev(list);
+
+   if (unsort)
+     {
+        tail->next = unsort;
+        unsort->prev = tail;
+     }
+   else
+      list->accounting->last = tail;
+
+   return list;
+}
+
+/**
+ * @brief Merge two list.
+ *
+ * @param left Head list to merge.
+ * @param right Tail list to merge.
+ * @return A new merged list.
+ *
+ * This function put right at the end of left and return the head.
+ *
+ * Both left and right does not exist anymore after the merge.
+ *
+ * @note merge cost is O(n), being @b n the size of the smallest
+ * list. This is due the need to fix accounting of that segment,
+ * making count and last access O(1).
+ */
+EAPI Eina_List *
+eina_list_merge(Eina_List *left, Eina_List *right)
+{
+   unsigned int n_left, n_right;
+
+   if (!left)
+      return right;
+
+   if (!right)
+      return left;
+
+   left->accounting->last->next = right;
+   right->prev = left->accounting->last;
+
+   n_left = left->accounting->count;
+   n_right = right->accounting->count;
+
+   if (n_left >= n_right)
+     {
+        Eina_List *itr = right;
+        left->accounting->last = right->accounting->last;
+        left->accounting->count += n_right;
+
+        _eina_list_mempool_accounting_free(right->accounting);
+
+        do
+          {
+             itr->accounting = left->accounting;
+             itr = itr->next;
+          }
+        while (itr);
+     }
+   else
+     {
+        Eina_List *itr = left->accounting->last;
+        right->accounting->count += n_left;
+
+        _eina_list_mempool_accounting_free(left->accounting);
+
+        do
+          {
+             itr->accounting = right->accounting;
+             itr = itr->prev;
+          }
+        while (itr);
+     }
+
+   return left;
+}
+
+
+/**
+ * @brief Split a list into 2 lists.
+ *
+ * @param list List to split.
+ * @param relative The list will be split after @p relative.
+ * @param right The head of the new right list.
+ * @return The new left list
+ *
+ * This function split @p list into two lists ( left and right ) after the node @p relative. @p Relative
+ * will become the last node of the left list. If @p list or @p right are NULL list is returns.
+ * If @p relative is NULL right is set to @p list and NULL is returns.
+ * If @p relative is the last node of @p list list is returns and @p right is set to NULL.
+ *
+ * list does not exist anymore after the split.
+ *
+ */
+EAPI Eina_List *
+eina_list_split_list(Eina_List *list, Eina_List *relative, Eina_List **right)
+{
+   Eina_List *next;
+   Eina_List *itr;
+
+   if(!right)
+      return list;
+
+   *right = NULL;
+
+   if (!list)
+      return NULL;
+
+   if (!relative)
+     {
+        *right = list;
+        return NULL;
+     }
+
+   if (relative == eina_list_last(list))
+      return list;
+
+   next = eina_list_next(relative);
+   next->prev = NULL;
+   next->accounting = _eina_list_mempool_accounting_new(next);
+   next->accounting->last = list->accounting->last;
+   *right = next;
+
+   itr = next;
+   do
+     {
+        itr->accounting = next->accounting;
+        next->accounting->count++;
+        itr = itr->next;
+     }
+   while (itr);
+
+   relative->next = NULL;
+   list->accounting->last = relative;
+   list->accounting->count = list->accounting->count - next->accounting->count;
+
+   return list;
+}
+
+/**
+ * @brief Merge two sorted list according to the ordering func will return.
+ *
+ * @param left First list to merge.
+ * @param right Second list to merge.
+ * @param func A function pointer that can handle comparing the list data
+ * nodes.
+ * @return A new sorted list.
+ *
+ * This function compare the head of @p left and @p right, and choose the
+ * smallest one to be head of the returned list. It will continue this process
+ * for all entry of both list.
+ *
+ * Both left and right does not exist anymore after the merge.
+ * If @p func is NULL, it will return NULL.
+ *
+ * Example:
+ * @code
+ * int
+ * sort_cb(void *d1, void *d2)
+ * {
+ *   const char *txt = NULL;
+ *    const char *txt2 = NULL;
+ *
+ *    if(!d1) return(1);
+ *    if(!d2) return(-1);
+ *
+ *    return(strcmp((const char*)d1, (const char*)d2));
+ * }
+ * extern Eina_List *sorted1;
+ * extern Eina_List *sorted2;
+ *
+ * list = eina_list_sorted_merge(sorted1, sorted2, sort_cb);
+ * @endcode
+ */
+EAPI Eina_List *
+eina_list_sorted_merge(Eina_List *left, Eina_List *right, Eina_Compare_Cb func)
+{
+   Eina_List *ret;
+   Eina_List *current;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
+
+   if (!left)
+      return right;
+
+   if (!right)
+      return left;
+
+   if (func(left->data, right->data) < 0)
+     {
+        ret = left;
+        current = left;
+        left = left->next;
+        ret->accounting->count += right->accounting->count;
+
+        _eina_list_mempool_accounting_free(right->accounting);
+     }
+   else
+     {
+        ret = right;
+        current = right;
+        right = right->next;
+        ret->accounting->count += left->accounting->count;
+
+        _eina_list_mempool_accounting_free(left->accounting);
+     }
+
+   while (left && right)
+     {
+        if (func(left->data, right->data) < 0)
+          {
+             current->next = left;
+             left->prev = current;
+             left = left->next;
+          }
+        else
+          {
+             current->next = right;
+             right->prev = current;
+             right = right->next;
+          }
+
+        current = current->next;
+        current->accounting = ret->accounting;
+     }
+
+   if (left)
+     {
+        current->next = left;
+        left->prev = current;
+        current->accounting = ret->accounting;
+     }
+
+   if (right)
+     {
+        current->next = right;
+        right->prev = current;
+        current->accounting = ret->accounting;
+     }
+
+   while (current->next)
+     {
+        current = current->next;
+        current->accounting = ret->accounting;
+     }
+
+   ret->accounting->last = current;
+
+   return ret;
+}
+
+/**
+ * @brief Returns node nearest to data is in the sorted list.
+ *
+ * @param list The list to search for data, @b must be sorted.
+ * @param func A function pointer that can handle comparing the list data nodes.
+ * @param data reference value to search.
+ * @param result_cmp if provided returns the result of
+ * func(node->data, data) node being the last (returned) node. If node
+ * was found (exact match), then it is 0. If returned node is smaller
+ * than requested data, it is less than 0 and if it's bigger it's
+ * greater than 0. It is the last value returned by func().
+ * @return the nearest node, NULL if not found.
+ *
+ * This can be used to check if some value is inside the list and get
+ * the nearest container node in this case. It should be used when list is
+ * known to be sorted as it will do binary search for results.
+ *
+ * Example: imagine user gives a string, you check if it's in the list
+ * before duplicating its contents, otherwise you want to insert it
+ * sorted. In this case you get the result of this function and either
+ * append or prepend the value.
+ *
+ * @note O(log2(n)) average/worst case performance, for 1,000,000
+ * elements it will do a maximum of 20 comparisons. This is much
+ * faster than the 1,000,000 comparisons made naively walking the list
+ * from head to tail, so depending on the number of searches and
+ * insertions, it may be worth to eina_list_sort() the list and do the
+ * searches later. As lists do not have O(1) access time, walking to
+ * the correct node can be costly, consider worst case to be almost
+ * O(n) pointer dereference (list walk).
+ *
+ * @see eina_list_search_sorted_list()
+ * @see eina_list_sort()
+ * @see eina_list_sorted_merge()
+ */
+EAPI Eina_List *
+eina_list_search_sorted_near_list(const Eina_List *list,
+                                  Eina_Compare_Cb func,
+                                  const void *data,
+                                  int *result_cmp)
+{
+   const Eina_List *ct;
+   unsigned int inf, sup, cur;
+   int cmp;
+
+   if (!list)
+     {
+        if (result_cmp)
+           *result_cmp = 0;
+
+        return NULL;
+     }
+
+   if (list->accounting->count == 1)
+     {
+        if (result_cmp)
+           *result_cmp = func(list->data, data);
+
+        return (Eina_List *)list;
+     }
+
+   /* list walk is expensive, do quick check: tail */
+   ct = list->accounting->last;
+   cmp = func(ct->data, data);
+   if (cmp <= 0)
+      goto end;
+
+   /* list walk is expensive, do quick check: head */
+   ct = list;
+   cmp = func(ct->data, data);
+   if (cmp >= 0)
+      goto end;
+
+   /* inclusive bounds */
+   inf = 1;
+   sup = list->accounting->count - 2;
+   cur = 1;
+   ct = list->next;
+
+   /* no loop, just compare if comparison value is important to caller */
+   if (inf > sup)
+     {
+        if (result_cmp)
+           cmp = func(ct->data, data);
+
+        goto end;
+     }
+
+   while (inf <= sup)
+     {
+        unsigned int tmp = cur;
+        cur = inf + ((sup - inf) >> 1);
+        if      (tmp < cur)
+           for (; tmp != cur; tmp++, ct = ct->next) ;
+        else if (tmp > cur)
+           for (; tmp != cur; tmp--, ct = ct->prev) ;
+
+        cmp = func(ct->data, data);
+        if (cmp == 0)
+           break;
+        else if (cmp < 0)
+           inf = cur + 1;
+        else if (cmp > 0)
+          {
+             if (cur > 0)
+                sup = cur - 1;
+             else
+                break;
+          }
+        else
+           break;
+     }
+
+end:
+   if (result_cmp)
+      *result_cmp = cmp;
+
+   return (Eina_List *)ct;
+}
+
+/**
+ * @brief Returns node if data is in the sorted list.
+ *
+ * @param list The list to search for data, @b must be sorted.
+ * @param func A function pointer that can handle comparing the list data nodes.
+ * @param data reference value to search.
+ * @return the node if func(node->data, data) == 0, NULL if not found.
+ *
+ * This can be used to check if some value is inside the list and get
+ * the container node in this case. It should be used when list is
+ * known to be sorted as it will do binary search for results.
+ *
+ * Example: imagine user gives a string, you check if it's in the list
+ * before duplicating its contents.
+ *
+ * @note O(log2(n)) average/worst case performance, for 1,000,000
+ * elements it will do a maximum of 20 comparisons. This is much
+ * faster than the 1,000,000 comparisons made by
+ * eina_list_search_unsorted_list(), so depending on the number of
+ * searches and insertions, it may be worth to eina_list_sort() the
+ * list and do the searches later. As said in
+ * eina_list_search_sorted_near_list(), lists do not have O(1) access
+ * time, so walking to the correct node can be costly, consider worst
+ * case to be almost O(n) pointer dereference (list walk).
+ *
+ * @see eina_list_search_sorted()
+ * @see eina_list_sort()
+ * @see eina_list_sorted_merge()
+ * @see eina_list_search_unsorted_list()
+ * @see eina_list_search_sorted_near_list()
+ */
+EAPI Eina_List *
+eina_list_search_sorted_list(const Eina_List *list,
+                             Eina_Compare_Cb func,
+                             const void *data)
+{
+   Eina_List *lnear;
+   int cmp;
+
+   lnear = eina_list_search_sorted_near_list(list, func, data, &cmp);
+   if (!lnear)
+      return NULL;
+
+   if (cmp == 0)
+      return lnear;
+
+   return NULL;
+}
+
+
+/**
+ * @brief Returns node data if it is in the sorted list.
+ *
+ * @param list The list to search for data, @b must be sorted.
+ * @param func A function pointer that can handle comparing the list data nodes.
+ * @param data reference value to search.
+ * @return the node value (@c node->data) if func(node->data, data) == 0,
+ * NULL if not found.
+ *
+ * This can be used to check if some value is inside the list and get
+ * the existing instance in this case. It should be used when list is
+ * known to be sorted as it will do binary search for results.
+ *
+ * Example: imagine user gives a string, you check if it's in the list
+ * before duplicating its contents.
+ *
+ * @note O(log2(n)) average/worst case performance, for 1,000,000
+ * elements it will do a maximum of 20 comparisons. This is much
+ * faster than the 1,000,000 comparisons made by
+ * eina_list_search_unsorted(), so depending on the number of
+ * searches and insertions, it may be worth to eina_list_sort() the
+ * list and do the searches later. As said in
+ * eina_list_search_sorted_near_list(), lists do not have O(1) access
+ * time, so walking to the correct node can be costly, consider worst
+ * case to be almost O(n) pointer dereference (list walk).
+ *
+ * @see eina_list_search_sorted_list()
+ * @see eina_list_sort()
+ * @see eina_list_sorted_merge()
+ * @see eina_list_search_unsorted_list()
+ */
+EAPI void *
+eina_list_search_sorted(const Eina_List *list,
+                        Eina_Compare_Cb func,
+                        const void *data)
+{
+   return eina_list_data_get(eina_list_search_sorted_list(list, func, data));
+}
+
+/**
+ * @brief Returns node if data is in the unsorted list.
+ *
+ * @param list The list to search for data, may be unsorted.
+ * @param func A function pointer that can handle comparing the list data nodes.
+ * @param data reference value to search.
+ * @return the node if func(node->data, data) == 0, NULL if not found.
+ *
+ * This can be used to check if some value is inside the list and get
+ * the container node in this case.
+ *
+ * Example: imagine user gives a string, you check if it's in the list
+ * before duplicating its contents.
+ *
+ * @note this is expensive and may walk the whole list, it's order-N,
+ * that is for 1,000,000 elements list it may walk and compare
+ * 1,000,000 nodes.
+ *
+ * @see eina_list_search_sorted_list()
+ * @see eina_list_search_unsorted()
+ */
+EAPI Eina_List *
+eina_list_search_unsorted_list(const Eina_List *list,
+                               Eina_Compare_Cb func,
+                               const void *data)
+{
+   const Eina_List *l;
+   void *d;
+
+   EINA_LIST_FOREACH(list, l, d)
+   {
+      if (!func(d, data))
+         return (Eina_List *)l;
+   }
+   return NULL;
+}
+
+/**
+ * @brief Returns node data if it is in the unsorted list.
+ *
+ * @param list The list to search for data, may be unsorted.
+ * @param func A function pointer that can handle comparing the list data nodes.
+ * @param data reference value to search.
+ * @return the node value (@c node->data) if func(node->data, data) == 0,
+ * NULL if not found.
+ *
+ * This can be used to check if some value is inside the list and get
+ * the existing instance in this case.
+ *
+ * Example: imagine user gives a string, you check if it's in the list
+ * before duplicating its contents.
+ *
+ * @note this is expensive and may walk the whole list, it's order-N,
+ * that is for 1,000,000 elements list it may walk and compare
+ * 1,000,000 nodes.
+ *
+ * @see eina_list_search_sorted()
+ * @see eina_list_search_unsorted_list()
+ */
+EAPI void *
+eina_list_search_unsorted(const Eina_List *list,
+                          Eina_Compare_Cb func,
+                          const void *data)
+{
+   return eina_list_data_get(eina_list_search_unsorted_list(list, func, data));
+}
+
+
+/**
+ * @brief Returned a new iterator associated to a list.
+ *
+ * @param list The list.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * list. If @p list is @c NULL or the count member of @p list is less
+ * or equal than 0, this function still returns a valid iterator that
+ * will always return false on eina_iterator_next(), thus keeping API
+ * sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the list structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove nodes this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_list_iterator_new(const Eina_List *list)
+{
+   Eina_Iterator_List *it;
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_List));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(it,            EINA_MAGIC_LIST_ITERATOR);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->head = list;
+   it->current = list;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(eina_list_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         eina_list_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(eina_list_iterator_free);
+
+   return &it->iterator;
+}
+
+/**
+ * @brief Returned a new reversed iterator associated to a list.
+ *
+ * @param list The list.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * list. If @p list is @c NULL or the count member of @p list is less
+ * or equal than 0, this function still returns a valid iterator that
+ * will always return false on eina_iterator_next(), thus keeping API
+ * sane.
+ *
+ * Unlike eina_list_iterator_new(), this will walk the list backwards.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the list structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove nodes this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_list_iterator_reversed_new(const Eina_List *list)
+{
+   Eina_Iterator_List *it;
+
+        eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_List));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(it,            EINA_MAGIC_LIST_ITERATOR);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->head = eina_list_last(list);
+   it->current = it->head;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(eina_list_iterator_prev);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         eina_list_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(eina_list_iterator_free);
+
+   return &it->iterator;
+}
+
+/**
+ * @brief Returned a new accessor associated to a list.
+ *
+ * @param list The list.
+ * @return A new accessor.
+ *
+ * This function returns a newly allocated accessor associated to
+ * @p list. If @p list is @c NULL or the count member of @p list is
+ * less or equal than 0, this function returns NULL. If the memory can
+ * not be allocated, NULL is returned and #EINA_ERROR_OUT_OF_MEMORY is
+ * set. Otherwise, a valid accessor is returned.
+ */
+EAPI Eina_Accessor *
+eina_list_accessor_new(const Eina_List *list)
+{
+   Eina_Accessor_List *ac;
+
+        eina_error_set(0);
+   ac = calloc(1, sizeof (Eina_Accessor_List));
+   if (!ac)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(ac,            EINA_MAGIC_LIST_ACCESSOR);
+   EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR);
+
+   ac->head = list;
+   ac->current = list;
+   ac->index = 0;
+
+   ac->accessor.version = EINA_ACCESSOR_VERSION;
+   ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_list_accessor_get_at);
+   ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(
+         eina_list_accessor_get_container);
+   ac->accessor.free = FUNC_ACCESSOR_FREE(eina_list_accessor_free);
+
+   return &ac->accessor;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_log.c b/tests/suite/ecore/src/lib/eina_log.c
new file mode 100644 (file)
index 0000000..aa9b781
--- /dev/null
@@ -0,0 +1,2483 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2009 Jorge Luis Zapata Muga, Cedric Bail, Andre Dieb
+ * Martins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * @page tutorial_log_page Log Tutorial
+ *
+ * @section tutorial_log_introduction Introduction
+ *
+ * The Eina Log module provides logging facilities for libraries and
+ * applications. It provides colored logging, basic logging levels (error,
+ * warning, debug, info, critical) and loggers - called logging domains -
+ * which will be covered on next sections.
+ *
+ * @section tutorial_log_basic_usage Basic Usage
+ *
+ * Log messages can be displayed using the following macros:
+ *
+ * @li EINA_LOG_ERR(),
+ * @li EINA_LOG_INFO(),
+ * @li EINA_LOG_WARN(),
+ * @li EINA_LOG_DBG().
+ *
+ * Here is an example:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <Eina.h>
+ *
+ * void test(int i)
+ * {
+ *    EINA_LOG_DBG("Entering test");
+ *
+ *    if (i < 0)
+ *    {
+ *        EINA_LOG_ERR("Argument is negative");
+ *        return;
+ *    }
+ *
+ *    EINA_LOG_INFO("argument non negative");
+ *
+ *    EINA_LOG_DBG("Exiting test");
+ * }
+ *
+ * int main(void)
+ * {
+ *    if (!eina_init())
+ *    {
+ *        printf("log during the initialization of Eina_Log module\n");
+ *        return EXIT_FAILURE;
+ *    }
+ *
+ *    test(-1);
+ *    test(0);
+ *
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * If you compiled Eina without debug mode, execution will yield only one log
+ * message, which is "argument is negative".
+ *
+ * Here we introduce the concept of logging domains (or loggers), which might
+ * already be familiar to readers. It is basically a way to separate a set of
+ * log messages into a context (e.g. a module) and provide a way of controlling
+ * this set as a whole.
+ *
+ * For example, suppose you have 3 different modules on your application and you
+ * want to get logging only from one of them (e.g. create some sort of filter).
+ * For achieving that, all you need to do is create a logging domain for each
+ * module so that all logging inside a module can be considered as a whole.
+ *
+ * Logging domains are specified by a name, color applied to the name and the
+ * level. The first two (name and color) are set through code, that is, inside
+ * your application/module/library.
+ *
+ * The level is used for controlling which messages should appear. It
+ * specifies the lowest level that should be displayed (e.g. a message
+ * with level 11 being logged on a domain with level set to 10 would be
+ * displayed, while a message with level 9 wouldn't).
+ *
+ * The domain level is set during runtime (in contrast with the name and
+ * color) through the environment variable EINA_LOG_LEVELS. This variable
+ * expects a list in the form domain_name1:level1,domain_name2:level2,... . For
+ * example:
+ *
+ * @code
+ *
+ * EINA_LOG_LEVELS=mymodule1:5,mymodule2:2,mymodule3:0 ./myapp
+ *
+ * @endcode
+ *
+ * This line would set mymodule1 level to 5, mymodule2 level to 2 and mymodule3
+ * level to 0.
+ *
+ *
+ * There's also a global logger to which EINA_LOG_(ERR, DBG, INFO, CRIT, WARN)
+ * macros do log on. It is a logger that is created internally by Eina Log with
+ * an empty name and can be used for general logging (where logging domains do
+ * not apply).
+ *
+ * Since this global logger doesn't have a name, you can't set its level through
+ * EINA_LOG_LEVELS variable. Here we introduce a second environment variable
+ * that is a bit more special: EINA_LOG_LEVEL.
+ *
+ * This variable specifies the level of the global logging domain and the level
+ * of domains that haven't been set through EINA_LOG_LEVELS. Here's an example:
+ *
+ * @code
+ *
+ * EINA_LOG_LEVEL=3 EINA_LOG_LEVELS=module1:10,module3:2 ./myapp
+ *
+ * @endcode
+ *
+ * Supposing you have modules named "module1", "module2" and "module3", this
+ * line would result in module1 with level 10, module2 with level 3 and module3
+ * with level 2. Note that module2's level wasn't specified, so it's level is
+ * set to the global level. This way we can easily apply filters to multiple
+ * domains with only one parameter (EINA_LOG_LEVEL=num).
+ *
+ * The global level (EINA_LOG_LEVEL) can also be set through code, using
+ * eina_log_level_set() function.
+ *
+ *
+ * While developing your libraries or applications, you may notice that
+ * EINA_LOG_DOM_(ERR, DBG, INFO, CRIT, WARN) macros also print out
+ * messages from eina itself. Here we introduce another environment variable
+ * that is a bit more special: EINA_LOG_LEVELS_GLOB.
+ *
+ * This variable allows you to disable the logging of any/all code in eina itself.
+ * This is useful when developing your libraries or applications so that you can
+ * see your own domain's messages easier without having to sift through a lot of
+ * internal eina debug messages. Here's an example:
+ *
+ * @code
+ *
+ * EINA_LOG_LEVEL=3 EINA_LOG_LEVELS_GLOB=eina_*:0 ./myapp
+ *
+ * @endcode
+ *
+ * This will disable eina_log output from all internal eina code thus allowing
+ * you to see your own domain messages easier.
+ *
+ * @section tutorial_log_advanced_display Advanced usage of print callbacks
+ *
+ * The log module allows the user to change the way
+ * eina_log_print() displays the messages. It suffices to pass to
+ * eina_log_print_cb_set() the function used to display the
+ * message. That  function must be of type #Eina_Log_Print_Cb. As a
+ * custom data can be passed to that callback, powerful display
+ * messages can be displayed.
+ *
+ * It is suggested to not use __FILE__, __FUNCTION__ or __LINE__ when
+ * writing that callback, but when defining macros (like
+ * EINA_LOG_ERR() and other macros).
+ *
+ * Here is an example of custom callback, whose behavior can be
+ * changed at runtime:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <eina_log.h>
+ *
+ * #define log(fmt, ...)                                    \
+ *    eina_log_print(EINA_LOG_LEVEL_ERR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
+ *
+ * typedef struct _Data Data;
+ *
+ * struct _Data
+ * {
+ *    int to_stderr;
+ * };
+ *
+ * void print_cb(const Eina_Log_Domain *domain,
+ *               Eina_Log_Level level,
+ *               const char *file,
+ *               const char *fnc,
+ *               int line,
+ *               const char *fmt,
+ *               void *data,
+ *               va_list args)
+ * {
+ *    Data *d;
+ *    FILE *output;
+ *    char *str;
+ *
+ *    d = (Data *)data;
+ *    if (d->to_stderr)
+ *    {
+ *       output = stderr;
+ *       str = "stderr";
+ *    }
+ *    else
+ *    {
+ *       output = stdout;
+ *       str = "stdout";
+ *    }
+ *
+ *    fprintf(output, "%s:%s:%s (%d) %s: ",
+ *            domain->domain_str, file, fnc, line, str);
+ *    vfprintf(output, fmt, args);
+ *    putc('\n', output);
+ * }
+ *
+ * void test(Data *data, int i)
+ * {
+ *    if (i < 0)
+ *       data->to_stderr = 0;
+ *    else
+ *       data->to_stderr = 1;
+ *
+ *    log("log message...");
+ * }
+ *
+ * int main(void)
+ * {
+ *    Data data;
+ *
+ *    if (!eina_init())
+ *    {
+ *       printf("log during the initialization of Eina_Log module\n");
+ *       return EXIT_FAILURE;
+ *    }
+ *
+ *    eina_log_print_cb_set(print_cb, &data);
+ *
+ *    test(&data, -1);
+ *    test(&data, 0);
+ *
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fnmatch.h>
+#include <assert.h>
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#endif
+
+#ifdef EFL_HAVE_POSIX_THREADS
+# include <pthread.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_inlist.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_log.h"
+
+/* TODO
+ * + printing logs to stdout or stderr can be implemented
+ * using a queue, useful for multiple threads printing
+ * + add a wrapper for assert?
+ */
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_LOG_ENV_ABORT "EINA_LOG_ABORT"
+#define EINA_LOG_ENV_ABORT_LEVEL "EINA_LOG_ABORT_LEVEL"
+#define EINA_LOG_ENV_LEVEL "EINA_LOG_LEVEL"
+#define EINA_LOG_ENV_LEVELS "EINA_LOG_LEVELS"
+#define EINA_LOG_ENV_LEVELS_GLOB "EINA_LOG_LEVELS_GLOB"
+#define EINA_LOG_ENV_COLOR_DISABLE "EINA_LOG_COLOR_DISABLE"
+#define EINA_LOG_ENV_FILE_DISABLE "EINA_LOG_FILE_DISABLE"
+#define EINA_LOG_ENV_FUNCTION_DISABLE "EINA_LOG_FUNCTION_DISABLE"
+
+
+// Structure for storing domain level settings passed from the command line
+// that will be matched with application-defined domains.
+typedef struct _Eina_Log_Domain_Level_Pending Eina_Log_Domain_Level_Pending;
+struct _Eina_Log_Domain_Level_Pending
+{
+   EINA_INLIST;
+   unsigned int level;
+   size_t namelen;
+   char name[];
+};
+
+/*
+ * List of levels for domains set by the user before the domains are registered,
+ * updates the domain levels on the first log and clears itself.
+ */
+static Eina_Inlist *_pending_list = NULL;
+static Eina_Inlist *_glob_list = NULL;
+
+// Disable color flag (can be changed through the env var
+// EINA_LOG_ENV_COLOR_DISABLE).
+static Eina_Bool _disable_color = EINA_FALSE;
+static Eina_Bool _disable_file = EINA_FALSE;
+static Eina_Bool _disable_function = EINA_FALSE;
+static Eina_Bool _abort_on_critical = EINA_FALSE;
+static int _abort_level_on_critical = EINA_LOG_LEVEL_CRITICAL;
+
+#ifdef EFL_HAVE_THREADS
+
+static Eina_Bool _threads_enabled = EINA_FALSE;
+
+# ifdef EFL_HAVE_POSIX_THREADS
+
+typedef pthread_t Thread;
+
+static pthread_t _main_thread;
+
+#  define SELF() pthread_self()
+#  define IS_MAIN(t)  pthread_equal(t, _main_thread)
+#  define IS_OTHER(t) EINA_UNLIKELY(!IS_MAIN(t))
+#  define CHECK_MAIN(...)                                         \
+   do {                                                           \
+      if (!IS_MAIN(pthread_self())) {                             \
+         fprintf(stderr,                                          \
+                 "ERR: not main thread! current=%lu, main=%lu\n", \
+                 (unsigned long)pthread_self(),                   \
+                 (unsigned long)_main_thread);                    \
+         return __VA_ARGS__;                                      \
+      }                                                           \
+   } while (0)
+
+#  ifdef EFL_HAVE_POSIX_THREADS_SPINLOCK
+
+static pthread_spinlock_t _log_lock;
+#   define LOG_LOCK()                                                  \
+   if (_threads_enabled)                                               \
+         do {                                                          \
+            if (0) {                                                   \
+               fprintf(stderr, "+++LOG LOG_LOCKED!   [%s, %lu]\n",     \
+                       __FUNCTION__, (unsigned long)pthread_self()); } \
+            if (EINA_UNLIKELY(_threads_enabled)) {                     \
+               pthread_spin_lock(&_log_lock); }                        \
+         } while (0)
+#   define LOG_UNLOCK()                                                \
+   if (_threads_enabled)                                               \
+         do {                                                          \
+            if (EINA_UNLIKELY(_threads_enabled)) {                     \
+               pthread_spin_unlock(&_log_lock); }                      \
+            if (0) {                                                   \
+               fprintf(stderr,                                         \
+                       "---LOG LOG_UNLOCKED! [%s, %lu]\n",             \
+                       __FUNCTION__, (unsigned long)pthread_self()); } \
+         } while (0)
+#   define INIT() pthread_spin_init(&_log_lock, PTHREAD_PROCESS_PRIVATE)
+#   define SHUTDOWN() pthread_spin_destroy(&_log_lock)
+
+#  else /* ! EFL_HAVE_POSIX_THREADS_SPINLOCK */
+
+static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
+#   define LOG_LOCK() if(_threads_enabled) {pthread_mutex_lock(&_log_mutex); }
+#   define LOG_UNLOCK() if(_threads_enabled) {pthread_mutex_unlock(&_log_mutex); }
+#   define INIT() (1)
+#   define SHUTDOWN() do {} while (0)
+
+#  endif /* ! EFL_HAVE_POSIX_THREADS_SPINLOCK */
+
+# else /* EFL_HAVE_WIN32_THREADS */
+
+typedef DWORD Thread;
+
+static DWORD _main_thread;
+
+#  define SELF() GetCurrentThreadId()
+#  define IS_MAIN(t)  (t == _main_thread)
+#  define IS_OTHER(t) EINA_UNLIKELY(!IS_MAIN(t))
+#  define CHECK_MAIN(...)                                         \
+   do {                                                           \
+      if (!IS_MAIN(GetCurrentThreadId())) {                       \
+         fprintf(stderr,                                          \
+                 "ERR: not main thread! current=%lu, main=%lu\n", \
+                 GetCurrentThreadId(), _main_thread);             \
+         return __VA_ARGS__;                                      \
+      }                                                           \
+   } while (0)
+
+static HANDLE _log_mutex = NULL;
+
+#  define LOG_LOCK() if(_threads_enabled) WaitForSingleObject(_log_mutex, INFINITE)
+#  define LOG_UNLOCK() if(_threads_enabled) ReleaseMutex(_log_mutex)
+#  define INIT() ((_log_mutex = CreateMutex(NULL, FALSE, NULL)) ? 1 : 0)
+#  define SHUTDOWN()  if (_log_mutex) CloseHandle(_log_mutex)
+
+# endif /* EFL_HAVE_WIN32_THREADS */
+
+#else /* ! EFL_HAVE_THREADS */
+
+# define LOG_LOCK() do {} while (0)
+# define LOG_UNLOCK() do {} while (0)
+# define IS_MAIN(t)  (1)
+# define IS_OTHER(t) (0)
+# define CHECK_MAIN(...) do {} while (0)
+# define INIT() (1)
+# define SHUTDOWN() do {} while (0)
+
+#endif /* ! EFL_HAVE_THREADS */
+
+
+// List of domains registered
+static Eina_Log_Domain *_log_domains = NULL;
+static unsigned int _log_domains_count = 0;
+static size_t _log_domains_allocated = 0;
+
+// Default function for printing on domains
+static Eina_Log_Print_Cb _print_cb = eina_log_print_cb_stderr;
+static void *_print_cb_data = NULL;
+
+#ifdef DEBUG
+static Eina_Log_Level _log_level = EINA_LOG_LEVEL_DBG;
+#elif DEBUG_CRITICAL
+static Eina_Log_Level _log_level = EINA_LOG_LEVEL_CRITICAL;
+#else
+static Eina_Log_Level _log_level = EINA_LOG_LEVEL_ERR;
+#endif
+
+/* NOTE: if you change this, also change:
+ *   eina_log_print_level_name_get()
+ *   eina_log_print_level_name_color_get()
+ */
+static const char *_names[] = {
+   "CRI",
+   "ERR",
+   "WRN",
+   "INF",
+   "DBG",
+};
+
+#ifdef _WIN32
+static int
+eina_log_win32_color_get(const char *domain_str)
+{
+   char *str;
+   char *tmp;
+   char *tmp2;
+   int code = -1;
+   int lighted = 0;
+   int ret = 0;
+
+   str = strdup(domain_str);
+   if (!str)
+      return 0;
+
+   /* this should not append */
+   if (str[0] != '\033')
+     {
+        free(str);
+        return 0;
+     }
+
+   /* we skip the first char and the [ */
+   tmp = tmp2 = str + 2;
+   while (*tmp != 'm')
+     {
+        if (*tmp == ';')
+          {
+             *tmp = '\0';
+             code = atol(tmp2);
+             tmp++;
+             tmp2 = tmp;
+          }
+
+        tmp++;
+     }
+   *tmp = '\0';
+   if (code < 0)
+      code = atol(tmp2);
+   else
+      lighted = atol(tmp2);
+
+   free(str);
+
+   if (code < lighted)
+     {
+        int c;
+
+        c = code;
+        code = lighted;
+        lighted = c;
+     }
+
+   if (lighted)
+      ret = FOREGROUND_INTENSITY;
+
+   if (code == 31)
+      ret |= FOREGROUND_RED;
+   else if (code == 32)
+      ret |= FOREGROUND_GREEN;
+   else if (code == 33)
+      ret |= FOREGROUND_RED | FOREGROUND_GREEN;
+   else if (code == 34)
+      ret |= FOREGROUND_BLUE;
+   else if (code == 36)
+      ret |= FOREGROUND_GREEN | FOREGROUND_BLUE;
+   else if (code == 37)
+      ret |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+
+   return ret;
+}
+#endif
+
+static inline void
+eina_log_print_level_name_get(int level, const char **p_name)
+{
+   static char buf[4];
+   /* NOTE: if you change this, also change
+    *    eina_log_print_level_name_color_get()
+    *    eina_log_level_name_get() (at eina_inline_log.x)
+    */
+   if (EINA_UNLIKELY(level < 0))
+     {
+        snprintf(buf, sizeof(buf), "%03d", level);
+        *p_name = buf;
+     }
+   else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
+     {
+        snprintf(buf, sizeof(buf), "%03d", level);
+        *p_name = buf;
+     }
+   else
+      *p_name = _names[level];
+}
+
+#ifdef _WIN32
+static inline void
+eina_log_print_level_name_color_get(int level,
+                                    const char **p_name,
+                                    int *p_color)
+{
+   static char buf[4];
+   /* NOTE: if you change this, also change:
+    *   eina_log_print_level_name_get()
+    */
+   if (EINA_UNLIKELY(level < 0))
+     {
+        snprintf(buf, sizeof(buf), "%03d", level);
+        *p_name = buf;
+     }
+   else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
+     {
+        snprintf(buf, sizeof(buf), "%03d", level);
+        *p_name = buf;
+     }
+   else
+      *p_name = _names[level];
+
+   *p_color = eina_log_win32_color_get(eina_log_level_color_get(level));
+}
+#else
+static inline void
+eina_log_print_level_name_color_get(int level,
+                                    const char **p_name,
+                                    const char **p_color)
+{
+   static char buf[4];
+   /* NOTE: if you change this, also change:
+    *   eina_log_print_level_name_get()
+    */
+   if (EINA_UNLIKELY(level < 0))
+     {
+        snprintf(buf, sizeof(buf), "%03d", level);
+        *p_name = buf;
+     }
+   else if (EINA_UNLIKELY(level >= EINA_LOG_LEVELS))
+     {
+        snprintf(buf, sizeof(buf), "%03d", level);
+        *p_name = buf;
+     }
+   else
+      *p_name = _names[level];
+
+   *p_color = eina_log_level_color_get(level);
+}
+#endif
+
+#define DECLARE_LEVEL_NAME(level) const char *name; \
+   eina_log_print_level_name_get(level, &name)
+#ifdef _WIN32
+# define DECLARE_LEVEL_NAME_COLOR(level) const char *name; int color; \
+   eina_log_print_level_name_color_get(level, &name, &color)
+#else
+# define DECLARE_LEVEL_NAME_COLOR(level) const char *name, *color; \
+   eina_log_print_level_name_color_get(level, &name, &color)
+#endif
+
+/** No threads, No color */
+static void
+eina_log_print_prefix_NOthreads_NOcolor_file_func(FILE *fp,
+                                                  const Eina_Log_Domain *d,
+                                                  Eina_Log_Level level,
+                                                  const char *file,
+                                                  const char *fnc,
+                                                  int line)
+{
+   DECLARE_LEVEL_NAME(level);
+   fprintf(fp, "%s:%s %s:%d %s() ", name, d->domain_str, file, line, fnc);
+}
+
+static void
+eina_log_print_prefix_NOthreads_NOcolor_NOfile_func(FILE *fp,
+                                                    const Eina_Log_Domain *d,
+                                                    Eina_Log_Level level,
+                                                    const char *file __UNUSED__,
+                                                    const char *fnc,
+                                                    int line __UNUSED__)
+{
+   DECLARE_LEVEL_NAME(level);
+   fprintf(fp, "%s:%s %s() ", name, d->domain_str, fnc);
+}
+
+static void
+eina_log_print_prefix_NOthreads_NOcolor_file_NOfunc(FILE *fp,
+                                                    const Eina_Log_Domain *d,
+                                                    Eina_Log_Level level,
+                                                    const char *file,
+                                                    const char *fnc __UNUSED__,
+                                                    int line)
+{
+   DECLARE_LEVEL_NAME(level);
+   fprintf(fp, "%s:%s %s:%d ", name, d->domain_str, file, line);
+}
+
+/* No threads, color */
+static void
+eina_log_print_prefix_NOthreads_color_file_func(FILE *fp,
+                                                const Eina_Log_Domain *d,
+                                                Eina_Log_Level level,
+                                                const char *file,
+                                                const char *fnc,
+                                                int line)
+{
+   DECLARE_LEVEL_NAME_COLOR(level);
+#ifdef _WIN32
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           color);
+   fprintf(fp, "%s", name);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, ":");
+   SetConsoleTextAttribute(GetStdHandle(
+                              STD_OUTPUT_HANDLE),
+                           eina_log_win32_color_get(d->domain_str));
+   fprintf(fp, "%s", d->name);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, " %s:%d ", file, line);
+   SetConsoleTextAttribute(GetStdHandle(
+                              STD_OUTPUT_HANDLE),
+                           FOREGROUND_INTENSITY | FOREGROUND_RED |
+                           FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, "%s()", fnc);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, " ");
+#else
+   fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d "
+           EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
+           color, name, d->domain_str, file, line, fnc);
+#endif
+}
+
+static void
+eina_log_print_prefix_NOthreads_color_NOfile_func(FILE *fp,
+                                                  const Eina_Log_Domain *d,
+                                                  Eina_Log_Level level,
+                                                  const char *file __UNUSED__,
+                                                  const char *fnc,
+                                                  int line __UNUSED__)
+{
+   DECLARE_LEVEL_NAME_COLOR(level);
+#ifdef _WIN32
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           color);
+   fprintf(fp, "%s", name);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, ":");
+   SetConsoleTextAttribute(GetStdHandle(
+                              STD_OUTPUT_HANDLE),
+                           eina_log_win32_color_get(d->domain_str));
+   fprintf(fp, "%s", d->name);
+   SetConsoleTextAttribute(GetStdHandle(
+                              STD_OUTPUT_HANDLE),
+                           FOREGROUND_INTENSITY | FOREGROUND_RED |
+                           FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, "%s()", fnc);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, " ");
+#else
+   fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s "
+           EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
+           color, name, d->domain_str, fnc);
+#endif
+}
+
+static void
+eina_log_print_prefix_NOthreads_color_file_NOfunc(FILE *fp,
+                                                  const Eina_Log_Domain *d,
+                                                  Eina_Log_Level level,
+                                                  const char *file,
+                                                  const char *fnc __UNUSED__,
+                                                  int line)
+{
+   DECLARE_LEVEL_NAME_COLOR(level);
+#ifdef _WIN32
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           color);
+   fprintf(fp, "%s", name);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, ":");
+   SetConsoleTextAttribute(GetStdHandle(
+                              STD_OUTPUT_HANDLE),
+                           eina_log_win32_color_get(d->domain_str));
+   fprintf(fp, "%s", d->name);
+   SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                           FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+   fprintf(fp, " %s:%d ", file, line);
+#else
+   fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d ",
+           color, name, d->domain_str, file, line);
+#endif
+}
+
+/** threads, No color */
+#ifdef EFL_HAVE_THREADS
+static void
+eina_log_print_prefix_threads_NOcolor_file_func(FILE *fp,
+                                                const Eina_Log_Domain *d,
+                                                Eina_Log_Level level,
+                                                const char *file,
+                                                const char *fnc,
+                                                int line)
+{
+   Thread cur;
+
+   DECLARE_LEVEL_NAME(level);
+   cur = SELF();
+   if (IS_OTHER(cur))
+     {
+        fprintf(fp, "%s:%s[T:%lu] %s:%d %s() ",
+                name, d->domain_str, (unsigned long)cur, file, line, fnc);
+        return;
+     }
+
+        fprintf(fp, "%s:%s %s:%d %s() ", name, d->domain_str, file, line, fnc);
+}
+
+static void
+eina_log_print_prefix_threads_NOcolor_NOfile_func(FILE *fp,
+                                                  const Eina_Log_Domain *d,
+                                                  Eina_Log_Level level,
+                                                  const char *file __UNUSED__,
+                                                  const char *fnc,
+                                                  int line __UNUSED__)
+{
+   Thread cur;
+
+   DECLARE_LEVEL_NAME(level);
+   cur = SELF();
+   if (IS_OTHER(cur))
+     {
+        fprintf(fp, "%s:%s[T:%lu] %s() ",
+                name, d->domain_str, (unsigned long)cur, fnc);
+        return;
+     }
+
+        fprintf(fp, "%s:%s %s() ", name, d->domain_str, fnc);
+}
+
+static void
+eina_log_print_prefix_threads_NOcolor_file_NOfunc(FILE *fp,
+                                                  const Eina_Log_Domain *d,
+                                                  Eina_Log_Level level,
+                                                  const char *file,
+                                                  const char *fnc __UNUSED__,
+                                                  int line)
+{
+   Thread cur;
+
+   DECLARE_LEVEL_NAME(level);
+   cur = SELF();
+   if (IS_OTHER(cur))
+     {
+        fprintf(fp, "%s:%s[T:%lu] %s:%d ",
+                name, d->domain_str, (unsigned long)cur, file, line);
+        return;
+     }
+
+        fprintf(fp, "%s:%s %s:%d ", name, d->domain_str, file, line);
+}
+
+/* threads, color */
+static void
+eina_log_print_prefix_threads_color_file_func(FILE *fp,
+                                              const Eina_Log_Domain *d,
+                                              Eina_Log_Level level,
+                                              const char *file,
+                                              const char *fnc,
+                                              int line)
+{
+   Thread cur;
+
+   DECLARE_LEVEL_NAME_COLOR(level);
+   cur = SELF();
+   if (IS_OTHER(cur))
+     {
+# ifdef _WIN32
+        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                                color);
+        fprintf(fp, "%s", name);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, ":");
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                eina_log_win32_color_get(d->domain_str));
+        fprintf(fp, "%s[T:", d->name);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, "[T:");
+        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                                FOREGROUND_GREEN | FOREGROUND_BLUE);
+        fprintf(fp, "%lu", (unsigned long)cur);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, "] %s:%d ", file, line);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_INTENSITY | FOREGROUND_RED |
+                                FOREGROUND_GREEN | FOREGROUND_BLUE);
+        fprintf(fp, "%s()", fnc);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, " ");
+# else
+        fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s[T:"
+                EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] %s:%d "
+                EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
+                color, name, d->domain_str, (unsigned long)cur, file,
+               line, fnc);
+# endif
+        return;
+     }
+
+# ifdef _WIN32
+   eina_log_print_prefix_NOthreads_color_file_func(fp,
+                                                   d,
+                                                   level,
+                                                   file,
+                                                   fnc,
+                                                   line);
+# else
+   fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d "
+           EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
+           color, name, d->domain_str, file, line, fnc);
+# endif
+}
+
+static void
+eina_log_print_prefix_threads_color_NOfile_func(FILE *fp,
+                                                const Eina_Log_Domain *d,
+                                                Eina_Log_Level level,
+                                                const char *file __UNUSED__,
+                                                const char *fnc,
+                                                int line __UNUSED__)
+{
+   Thread cur;
+
+   DECLARE_LEVEL_NAME_COLOR(level);
+   cur = SELF();
+   if (IS_OTHER(cur))
+     {
+# ifdef _WIN32
+        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                                color);
+        fprintf(fp, "%s", name);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, ":");
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                eina_log_win32_color_get(d->domain_str));
+        fprintf(fp, "%s[T:", d->name);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, "[T:");
+        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                                FOREGROUND_GREEN | FOREGROUND_BLUE);
+        fprintf(fp, "%lu", (unsigned long)cur);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_INTENSITY | FOREGROUND_RED |
+                                FOREGROUND_GREEN | FOREGROUND_BLUE);
+        fprintf(fp, "%s()", fnc);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, " ");
+# else
+        fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s[T:"
+                EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] "
+                EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
+                color, name, d->domain_str, (unsigned long)cur, fnc);
+# endif
+        return;
+     }
+
+# ifdef _WIN32
+   eina_log_print_prefix_NOthreads_color_NOfile_func(fp,
+                                                     d,
+                                                     level,
+                                                     file,
+                                                     fnc,
+                                                     line);
+# else
+   fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s "
+           EINA_COLOR_HIGH "%s()" EINA_COLOR_RESET " ",
+           color, name, d->domain_str, fnc);
+# endif
+}
+
+static void
+eina_log_print_prefix_threads_color_file_NOfunc(FILE *fp,
+                                                const Eina_Log_Domain *d,
+                                                Eina_Log_Level level,
+                                                const char *file,
+                                                const char *fnc __UNUSED__,
+                                                int line)
+{
+   Thread cur;
+
+   DECLARE_LEVEL_NAME_COLOR(level);
+   cur = SELF();
+   if (IS_OTHER(cur))
+     {
+# ifdef _WIN32
+        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                                color);
+        fprintf(fp, "%s", name);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, ":");
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                eina_log_win32_color_get(d->domain_str));
+        fprintf(fp, "%s[T:", d->name);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, "[T:");
+        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
+                                FOREGROUND_GREEN | FOREGROUND_BLUE);
+        fprintf(fp, "%lu", (unsigned long)cur);
+        SetConsoleTextAttribute(GetStdHandle(
+                                   STD_OUTPUT_HANDLE),
+                                FOREGROUND_RED | FOREGROUND_GREEN |
+                                FOREGROUND_BLUE);
+        fprintf(fp, "] %s:%d ", file, line);
+# else
+        fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s[T:"
+                EINA_COLOR_ORANGE "%lu" EINA_COLOR_RESET "] %s:%d ",
+                color, name, d->domain_str, (unsigned long)cur, file, line);
+# endif
+        return;
+     }
+
+# ifdef _WIN32
+   eina_log_print_prefix_NOthreads_color_file_NOfunc(fp,
+                                                     d,
+                                                     level,
+                                                     file,
+                                                     fnc,
+                                                     line);
+# else
+        fprintf(fp, "%s%s" EINA_COLOR_RESET ":%s %s:%d ",
+           color, name, d->domain_str, file, line);
+# endif
+}
+#endif /* EFL_HAVE_THREADS */
+
+static void (*_eina_log_print_prefix)(FILE *fp, const Eina_Log_Domain *d,
+                                      Eina_Log_Level level, const char *file,
+                                      const char *fnc,
+                                      int line) =
+   eina_log_print_prefix_NOthreads_color_file_func;
+
+static inline void
+eina_log_print_prefix_update(void)
+{
+   if (_disable_file && _disable_function)
+     {
+        fprintf(stderr, "ERROR: cannot have " EINA_LOG_ENV_FILE_DISABLE " and "
+                EINA_LOG_ENV_FUNCTION_DISABLE " set at the same time, will "
+                                              "just disable function.\n");
+        _disable_file = 0;
+     }
+
+#define S(NOthread, NOcolor, NOfile, NOfunc) \
+   _eina_log_print_prefix = \
+      eina_log_print_prefix_ ## NOthread ## threads_ ## NOcolor ## color_ ## \
+      NOfile \
+      ## file_ ## NOfunc ## func
+
+#ifdef EFL_HAVE_THREADS
+   if (_threads_enabled)
+     {
+        if (_disable_color)
+          {
+             if (_disable_file)
+                S(,NO,NO,);
+             else if (_disable_function)
+                S(,NO,,NO);
+             else
+                S(,NO,,);
+          }
+        else
+          {
+             if (_disable_file)
+                S(,,NO,);
+             else if (_disable_function)
+                S(,,,NO);
+             else
+                S(,,,);
+          }
+
+        return;
+     }
+
+#endif
+
+   if (_disable_color)
+     {
+        if (_disable_file)
+                S(NO,NO,NO,);
+        else if (_disable_function)
+                S(NO,NO,,NO);
+        else
+                S(NO,NO,,);
+     }
+   else
+     {
+        if (_disable_file)
+                S(NO,,NO,);
+        else if (_disable_function)
+                S(NO,,,NO);
+        else
+                S(NO,,,);
+     }
+
+#undef S
+}
+
+/*
+ * Creates a colored domain name string.
+ */
+static const char *
+eina_log_domain_str_get(const char *name, const char *color)
+{
+   const char *d;
+
+   if (color)
+     {
+        size_t name_len;
+        size_t color_len;
+
+        name_len = strlen(name);
+        color_len = strlen(color);
+        d =
+           malloc(sizeof(char) *
+                  (color_len + name_len + strlen(EINA_COLOR_RESET) + 1));
+        if (!d)
+           return NULL;
+
+               memcpy((char *)d,                          color, color_len);
+               memcpy((char *)(d + color_len),            name,  name_len);
+               memcpy((char *)(d + color_len + name_len), EINA_COLOR_RESET,
+               strlen(EINA_COLOR_RESET));
+        ((char *)d)[color_len + name_len + strlen(EINA_COLOR_RESET)] = '\0';
+     }
+   else
+      d = strdup(name);
+
+   return d;
+}
+
+/*
+ * Setups a new logging domain to the name and color specified. Note that this
+ * constructor acts upon an pre-allocated object.
+ */
+static Eina_Log_Domain *
+eina_log_domain_new(Eina_Log_Domain *d, const char *name, const char *color)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(d,    NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
+
+   d->level = EINA_LOG_LEVEL_UNKNOWN;
+   d->deleted = EINA_FALSE;
+
+   if (name)
+     {
+        if ((color) && (!_disable_color))
+           d->domain_str = eina_log_domain_str_get(name, color);
+        else
+           d->domain_str = eina_log_domain_str_get(name, NULL);
+
+        d->name = strdup(name);
+        d->namelen = strlen(name);
+     }
+   else
+     {
+        d->domain_str = NULL;
+        d->name = NULL;
+        d->namelen = 0;
+     }
+
+   return d;
+}
+
+/*
+ * Frees internal strings of a log domain, keeping the log domain itself as a
+ * slot for next domain registers.
+ */
+static void
+eina_log_domain_free(Eina_Log_Domain *d)
+{
+   EINA_SAFETY_ON_NULL_RETURN(d);
+
+   if (d->domain_str)
+      free((char *)d->domain_str);
+
+   if (d->name)
+      free((char *)d->name);
+}
+
+/*
+ * Parses domain levels passed through the env var.
+ */
+static void
+eina_log_domain_parse_pendings(void)
+{
+   const char *start;
+
+   if (!(start = getenv(EINA_LOG_ENV_LEVELS)))
+      return;
+
+   // name1:level1,name2:level2,name3:level3,...
+   while (1)
+     {
+        Eina_Log_Domain_Level_Pending *p;
+        char *end = NULL;
+        char *tmp = NULL;
+        long int level;
+
+        end = strchr(start, ':');
+        if (!end)
+           break;
+
+        // Parse level, keep going if failed
+        level = strtol((char *)(end + 1), &tmp, 10);
+        if (tmp == (end + 1))
+           goto parse_end;
+
+        // Parse name
+        p = malloc(sizeof(Eina_Log_Domain_Level_Pending) + end - start + 1);
+        if (!p)
+           break;
+
+        p->namelen = end - start;
+        memcpy((char *)p->name, start, end - start);
+        ((char *)p->name)[end - start] = '\0';
+        p->level = level;
+
+        _pending_list = eina_inlist_append(_pending_list, EINA_INLIST_GET(p));
+
+parse_end:
+        start = strchr(tmp, ',');
+        if (start)
+           start++;
+        else
+           break;
+     }
+}
+
+static void
+eina_log_domain_parse_pending_globs(void)
+{
+   const char *start;
+
+   if (!(start = getenv(EINA_LOG_ENV_LEVELS_GLOB)))
+      return;
+
+   // name1:level1,name2:level2,name3:level3,...
+   while (1)
+     {
+        Eina_Log_Domain_Level_Pending *p;
+        char *end = NULL;
+        char *tmp = NULL;
+        long int level;
+
+        end = strchr(start, ':');
+        if (!end)
+           break;
+
+        // Parse level, keep going if failed
+        level = strtol((char *)(end + 1), &tmp, 10);
+        if (tmp == (end + 1))
+           goto parse_end;
+
+        // Parse name
+        p = malloc(sizeof(Eina_Log_Domain_Level_Pending) + end - start + 1);
+        if (!p)
+           break;
+
+        p->namelen = 0; /* not that useful */
+        memcpy((char *)p->name, start, end - start);
+        ((char *)p->name)[end - start] = '\0';
+        p->level = level;
+
+        _glob_list = eina_inlist_append(_glob_list, EINA_INLIST_GET(p));
+
+parse_end:
+        start = strchr(tmp, ',');
+        if (start)
+           start++;
+        else
+           break;
+     }
+}
+
+static inline int
+eina_log_domain_register_unlocked(const char *name, const char *color)
+{
+   Eina_Log_Domain_Level_Pending *pending = NULL;
+   size_t namelen;
+   unsigned int i;
+
+   for (i = 0; i < _log_domains_count; i++)
+     {
+        if (_log_domains[i].deleted)
+          {
+             // Found a flagged slot, free domain_str and replace slot
+             eina_log_domain_new(&_log_domains[i], name, color);
+             goto finish_register;
+          }
+     }
+
+   if (_log_domains_count >= _log_domains_allocated)
+     {
+        Eina_Log_Domain *tmp;
+        size_t size;
+
+        if (!_log_domains)
+           // special case for init, eina itself will allocate a dozen of domains
+           size = 24;
+        else
+           // grow 8 buckets to minimize reallocs
+           size = _log_domains_allocated + 8;
+
+        tmp = realloc(_log_domains, sizeof(Eina_Log_Domain) * size);
+
+        if (tmp)
+          {
+             // Success!
+             _log_domains = tmp;
+             _log_domains_allocated = size;
+          }
+        else
+           return -1;
+     }
+
+   // Use an allocated slot
+             eina_log_domain_new(&_log_domains[i], name, color);
+   _log_domains_count++;
+
+finish_register:
+   namelen = _log_domains[i].namelen;
+
+   EINA_INLIST_FOREACH(_pending_list, pending)
+   {
+      if ((namelen == pending->namelen) && (strcmp(pending->name, name) == 0))
+        {
+           _log_domains[i].level = pending->level;
+           _pending_list =
+              eina_inlist_remove(_pending_list, EINA_INLIST_GET(pending));
+           free(pending);
+           break;
+        }
+   }
+
+   if (_log_domains[i].level == EINA_LOG_LEVEL_UNKNOWN)
+     {
+        EINA_INLIST_FOREACH(_glob_list, pending)
+        {
+           if (!fnmatch(pending->name, name, 0))
+             {
+                _log_domains[i].level = pending->level;
+                break;
+             }
+        }
+     }
+
+   // Check if level is still UNKNOWN, set it to global
+   if (_log_domains[i].level == EINA_LOG_LEVEL_UNKNOWN)
+      _log_domains[i].level = _log_level;
+
+   return i;
+}
+
+static inline Eina_Bool
+eina_log_term_color_supported(const char *term)
+{
+   const char *tail;
+
+   if (!term)
+      return EINA_FALSE;
+
+   tail = term + 1;
+   switch (term[0])
+     {
+      /* list of known to support color terminals,
+       * take from gentoo's portage.
+       */
+
+      case 'x': /* xterm and xterm-color */
+         return ((strncmp(tail, "term", sizeof("term") - 1) == 0) &&
+                 ((tail[sizeof("term") - 1] == '\0') ||
+                  (strcmp(tail + sizeof("term") - 1, "-color") == 0)));
+
+      case 'E': /* Eterm */
+      case 'a': /* aterm */
+      case 'k': /* kterm */
+         return (strcmp(tail, "term") == 0);
+
+      case 'r': /* xrvt or rxvt-unicode */
+         return ((strncmp(tail, "xvt", sizeof("xvt") - 1) == 0) &&
+                 ((tail[sizeof("xvt") - 1] == '\0') ||
+                  (strcmp(tail + sizeof("xvt") - 1, "-unicode") == 0)));
+
+      case 's': /* screen */
+         return (strcmp(tail, "creen") == 0);
+
+      case 'g': /* gnome */
+         return (strcmp(tail, "nome") == 0);
+
+      case 'i': /* interix */
+         return (strcmp(tail, "nterix") == 0);
+
+      default:
+         return EINA_FALSE;
+     }
+}
+
+static inline void
+eina_log_domain_unregister_unlocked(int domain)
+{
+   Eina_Log_Domain *d;
+
+   if ((unsigned int)domain >= _log_domains_count)
+      return;
+
+   d = &_log_domains[domain];
+   eina_log_domain_free(d);
+   d->deleted = 1;
+}
+
+static inline void
+eina_log_print_unlocked(int domain,
+                        Eina_Log_Level level,
+                        const char *file,
+                        const char *fnc,
+                        int line,
+                        const char *fmt,
+                        va_list args)
+{
+   Eina_Log_Domain *d;
+
+#ifdef EINA_SAFETY_CHECKS
+   if (EINA_UNLIKELY((unsigned int)domain >= _log_domains_count) ||
+       EINA_UNLIKELY(domain < 0))
+     {
+        if (file && fnc && fmt)
+           fprintf(
+              stderr,
+              "CRI: %s:%d %s() eina_log_print() unknown domain %d, original message format '%s'\n",
+              file,
+              line,
+              fnc,
+              domain,
+              fmt);
+        else
+           fprintf(
+              stderr,
+              "CRI: eina_log_print() unknown domain %d, original message format '%s'\n",
+              domain,
+              fmt ? fmt : "");
+
+        if (_abort_on_critical)
+           abort();
+
+        return;
+     }
+
+#endif
+   d = _log_domains + domain;
+#ifdef EINA_SAFETY_CHECKS
+   if (EINA_UNLIKELY(d->deleted))
+     {
+           fprintf(stderr,
+                "ERR: eina_log_print() domain %d is deleted\n",
+                domain);
+        return;
+     }
+
+#endif
+
+   if (level > d->level)
+      return;
+
+#ifdef _WIN32
+   {
+      char *wfmt;
+      char *tmp;
+
+      wfmt = strdup(fmt);
+      if (!wfmt)
+        {
+           fprintf(stderr, "ERR: %s: can not allocate memory\n", __FUNCTION__);
+           return;
+        }
+
+      tmp = wfmt;
+      while (strchr(tmp, "%"))
+        {
+           tmp++;
+           if (*tmp == 'z')
+              *tmp = 'I';
+        }
+      _print_cb(d, level, file, fnc, line, wfmt, _print_cb_data, args);
+      free(wfmt);
+   }
+#else
+   _print_cb(d, level, file, fnc, line, fmt, _print_cb_data, args);
+#endif
+
+   if (EINA_UNLIKELY(_abort_on_critical) &&
+       EINA_UNLIKELY(level <= _abort_level_on_critical))
+      abort();
+}
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the log module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the log module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ *
+ * @warning Not-MT: just call this function from main thread! The
+ *          place where this function was called the first time is
+ *          considered the main thread.
+ */
+Eina_Bool
+eina_log_init(void)
+{
+   const char *level, *tmp;
+   int color_disable;
+
+   assert((sizeof(_names) / sizeof(_names[0])) == EINA_LOG_LEVELS);
+
+   if ((tmp = getenv(EINA_LOG_ENV_COLOR_DISABLE)))
+      color_disable = atoi(tmp);
+   else
+      color_disable = -1;
+
+   /* Check if color is explicitly disabled */
+   if (color_disable == 1)
+      _disable_color = EINA_TRUE;
+
+#ifndef _WIN32
+   /* color was not explicitly disabled or enabled, guess it */
+   else if (color_disable == -1)
+     {
+        if (!eina_log_term_color_supported(getenv("TERM")))
+           _disable_color = EINA_TRUE;
+        else
+          {
+             /* if not a terminal, but redirected to a file, disable color */
+             int fd;
+
+             if (_print_cb == eina_log_print_cb_stderr)
+                fd = STDERR_FILENO;
+             else if (_print_cb == eina_log_print_cb_stdout)
+                fd = STDOUT_FILENO;
+             else
+                fd = -1;
+
+             if ((fd >= 0) && (!isatty(fd)))
+                _disable_color = EINA_TRUE;
+          }
+     }
+#endif
+
+   if ((tmp = getenv(EINA_LOG_ENV_FILE_DISABLE)) && (atoi(tmp) == 1))
+      _disable_file = EINA_TRUE;
+
+   if ((tmp = getenv(EINA_LOG_ENV_FUNCTION_DISABLE)) && (atoi(tmp) == 1))
+      _disable_function = EINA_TRUE;
+
+   if ((tmp = getenv(EINA_LOG_ENV_ABORT)) && (atoi(tmp) == 1))
+      _abort_on_critical = EINA_TRUE;
+
+   if ((tmp = getenv(EINA_LOG_ENV_ABORT_LEVEL)))
+      _abort_level_on_critical = atoi(tmp);
+
+   eina_log_print_prefix_update();
+
+   // Global log level
+   if ((level = getenv(EINA_LOG_ENV_LEVEL)))
+      _log_level = atoi(level);
+
+   // Register UNKNOWN domain, the default logger
+   EINA_LOG_DOMAIN_GLOBAL = eina_log_domain_register("", NULL);
+
+   if (EINA_LOG_DOMAIN_GLOBAL < 0)
+     {
+        fprintf(stderr, "Failed to create global logging domain.\n");
+        return EINA_FALSE;
+     }
+
+   // Parse pending domains passed through EINA_LOG_LEVELS_GLOB
+   eina_log_domain_parse_pending_globs();
+
+   // Parse pending domains passed through EINA_LOG_LEVELS
+   eina_log_domain_parse_pendings();
+
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the log module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the log module set up by
+ * eina_log_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ *
+ * @warning Not-MT: just call this function from main thread! The
+ *          place where eina_log_init() (eina_init()) was called the
+ *          first time is considered the main thread.
+ */
+Eina_Bool
+eina_log_shutdown(void)
+{
+   Eina_Inlist *tmp;
+
+   while (_log_domains_count--)
+     {
+        if (_log_domains[_log_domains_count].deleted)
+           continue;
+
+        eina_log_domain_free(&_log_domains[_log_domains_count]);
+     }
+
+        free(_log_domains);
+
+   _log_domains = NULL;
+   _log_domains_count = 0;
+   _log_domains_allocated = 0;
+
+   while (_glob_list)
+     {
+        tmp = _glob_list;
+        _glob_list = _glob_list->next;
+        free(tmp);
+     }
+
+   while (_pending_list)
+     {
+        tmp = _pending_list;
+        _pending_list = _pending_list->next;
+        free(tmp);
+     }
+
+   return EINA_TRUE;
+}
+
+#ifdef EFL_HAVE_THREADS
+
+/**
+ * @internal
+ * @brief Activate the log mutex.
+ *
+ * This function activate the mutex in the eina log module. It is called by
+ * eina_threads_init().
+ *
+ * @see eina_threads_init()
+ */
+void
+eina_log_threads_init(void)
+{
+   _main_thread = SELF();
+   if (INIT())
+      _threads_enabled = EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the log mutex.
+ *
+ * This function shuts down the mutex in the log module.
+ * It is called by eina_threads_shutdown().
+ *
+ * @see eina_threads_shutdown()
+ */
+void
+eina_log_threads_shutdown(void)
+{
+   CHECK_MAIN();
+   SHUTDOWN();
+   _threads_enabled = EINA_FALSE;
+}
+
+#endif
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Log_Group Log
+ *
+ * @brief Full-featured logging system.
+ *
+ * Eina provides eina_log_print(), a standard function to manage all
+ * logging messages. This function may be called directly or using the
+ * helper macros such as EINA_LOG_DBG(), EINA_LOG_ERR() or those that
+ * take a specific domain as argument EINA_LOG_DOM_DBG(),
+ * EINA_LOG_DOM_ERR().  Internally, eina_log_print() will call the
+ * function defined with eina_log_print_cb_set(), that defaults to
+ * eina_log_print_cb_stderr(), but may be changed to do whatever you
+ * need, such as networking or syslog logging.
+ *
+ * The logging system is thread safe once initialized with
+ * eina_log_threads_enable(). The thread that calls this function
+ * first is considered "main thread" and other threads will have their
+ * thread id (pthread_self()) printed in the log message so it is easy
+ * to detect from where it is coming.
+ *
+ * Log domains is the Eina way to differentiate messages. There might
+ * be different domains to represent different modules, different
+ * feature-set, different categories and so on. Filtering can be
+ * applied to domain names by means of @c EINA_LOG_LEVELS environment
+ * variable or eina_log_domain_level_set().
+ *
+ * The different logging levels serve to customize the amount of
+ * debugging one want to take and may be used to automatically call
+ * abort() once some given level message is printed. This is
+ * controlled by environment variable @c EINA_LOG_ABORT and the level
+ * to be considered critical with @c EINA_LOG_ABORT_LEVEL. These can
+ * be changed with eina_log_abort_on_critical_set() and
+ * eina_log_abort_on_critical_level_set().
+ *
+ * The default maximum level to print is defined by environment
+ * variable @c EINA_LOG_LEVEL, but may be set per-domain with @c
+ * EINA_LOG_LEVELS. It will default to #EINA_LOG_ERR. This can be
+ * changed with eina_log_level_set().
+ *
+ * To use the log system Eina must be initialized with eina_init() and
+ * later shut down with eina_shutdown(). Here is a straightforward
+ * example:
+ *
+ * @code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ *
+ * #include <eina_log.h>
+ *
+ * void test_warn(void)
+ * {
+ *    EINA_LOG_WARN("Here is a warning message");
+ * }
+ *
+ * int main(void)
+ * {
+ *    if (!eina_init())
+ *    {
+ *        printf("log during the initialization of Eina_Log module\n");
+ *        return EXIT_FAILURE;
+ *    }
+ *
+ *    test_warn();
+ *
+ *    eina_shutdown();
+ *
+ *    return EXIT_SUCCESS;
+ * }
+ * @endcode
+ *
+ * Compile this code with the following command:
+ *
+ * @code
+ * gcc -Wall -o test_Eina_Log test_eina.c `pkg-config --cflags --libs eina`
+ * @endcode
+ *
+ * Now execute the program with:
+ *
+ * @code
+ * EINA_LOG_LEVEL=2 ./test_eina_log
+ * @endcode
+ *
+ * You should see a message displayed in the terminal.
+ *
+ * For more information, you can look at the @ref tutorial_log_page.
+ *
+ * @{
+ */
+
+
+/**
+ * @cond LOCAL
+ */
+
+EAPI int EINA_LOG_DOMAIN_GLOBAL = 0;
+
+/**
+ * @endcond
+ */
+
+
+/**
+ * Enable logging module to handle threads.
+ *
+ * There is no disable option on purpose, if it is enabled, there is
+ * no way back until you call the last eina_shutdown().
+ *
+ * There is no function to retrieve if threads are enabled as one is
+ * not supposed to know this from outside.
+ *
+ * After this call is executed at least once, if Eina was compiled
+ * with threads support then logging will lock around debug messages
+ * and threads that are not the main thread will have its identifier
+ * printed.
+ *
+ * The main thread is considered the thread where the first
+ * eina_init() was called.
+ */
+EAPI void
+eina_log_threads_enable(void)
+{
+#ifdef EFL_HAVE_THREADS
+   _threads_enabled = 1;
+   eina_log_print_prefix_update();
+#endif
+}
+
+/**
+ * Sets logging method to use.
+ *
+ * @param cb The callback to call when printing a log.
+ * @param data The data to pass to the callback.
+ *
+ * By default, eina_log_print_cb_stderr() is used.
+ *
+ * @note MT: safe to call from any thread.
+ *
+ * @note MT: given function @a cb will be called protected by mutex.
+ *       This means you're safe from other calls but you should never
+ *       call eina_log_print(), directly or indirectly.
+ */
+EAPI void
+eina_log_print_cb_set(Eina_Log_Print_Cb cb, void *data)
+{
+   LOG_LOCK();
+   _print_cb = cb;
+   _print_cb_data = data;
+   eina_log_print_prefix_update();
+   LOG_UNLOCK();
+}
+
+/**
+ * @brief Set the default log level.
+ *
+ * @param level The log level.
+ *
+ * This function sets the log level @p level. It is used in
+ * eina_log_print().
+ *
+ * @note this is initially set to envvar EINA_LOG_LEVEL by eina_init().
+ *
+ * @see eina_log_level_get()
+ */
+EAPI void
+eina_log_level_set(int level)
+{
+   _log_level = level;
+   if (EINA_LIKELY((EINA_LOG_DOMAIN_GLOBAL >= 0) &&
+                   ((unsigned int)EINA_LOG_DOMAIN_GLOBAL < _log_domains_count)))
+      _log_domains[EINA_LOG_DOMAIN_GLOBAL].level = level;
+}
+
+/**
+ * @brief Get the default log level.
+ *
+ * @return the log level that limits eina_log_print().
+ *
+ * @see eina_log_level_set()
+ */
+EAPI int
+eina_log_level_get(void)
+{
+   return _log_level;
+}
+
+/**
+ * Checks if current thread is the main thread.
+ *
+ * @return #EINA_TRUE if threads were enabled and the current thread
+ *         is the one that called eina_log_threads_init(). If there is
+ *         no thread support (compiled with --disable-pthreads) or
+ *         they were not enabled, then #EINA_TRUE is also
+ *         returned. The only case where #EINA_FALSE is returned is
+ *         when threads were successfully enabled but the current
+ *         thread is not the main (one that called
+ *         eina_log_threads_init()).
+ */
+EAPI Eina_Bool
+eina_log_main_thread_check(void)
+{
+#ifdef EFL_HAVE_THREADS
+   return ((!_threads_enabled) || IS_MAIN(SELF()));
+#else
+   return EINA_TRUE;
+#endif
+}
+
+/**
+ * @brief Set if color logging should be disabled.
+ *
+ * @param disabled if #EINA_TRUE, color logging should be disabled.
+ *
+ * @note this is initially set to envvar EINA_LOG_COLOR_DISABLE by eina_init().
+ *
+ * @see eina_log_color_disable_get()
+ */
+EAPI void
+eina_log_color_disable_set(Eina_Bool disabled)
+{
+   _disable_color = disabled;
+}
+
+/**
+ * @brief Get if color logging should be disabled.
+ *
+ * @return if #EINA_TRUE, color logging should be disabled.
+ *
+ * @see eina_log_color_disable_set()
+ */
+EAPI Eina_Bool
+eina_log_color_disable_get(void)
+{
+   return _disable_color;
+}
+
+/**
+ * @brief Set if originating file name logging should be disabled.
+ *
+ * @param disabled if #EINA_TRUE, file name logging should be disabled.
+ *
+ * @note this is initially set to envvar EINA_LOG_FILE_DISABLE by eina_init().
+ *
+ * @see eina_log_file_disable_get()
+ */
+EAPI void
+eina_log_file_disable_set(Eina_Bool disabled)
+{
+   _disable_file = disabled;
+}
+
+/**
+ * @brief Get if originating file name logging should be disabled.
+ *
+ * @return if #EINA_TRUE, file name logging should be disabled.
+ *
+ * @see eina_log_file_disable_set()
+ */
+EAPI Eina_Bool
+eina_log_file_disable_get(void)
+{
+   return _disable_file;
+}
+
+/**
+ * @brief Set if originating function name logging should be disabled.
+ *
+ * @param disabled if #EINA_TRUE, function name logging should be disabled.
+ *
+ * @note this is initially set to envvar EINA_LOG_FUNCTION_DISABLE by
+ *       eina_init().
+ *
+ * @see eina_log_function_disable_get()
+ */
+EAPI void
+eina_log_function_disable_set(Eina_Bool disabled)
+{
+   _disable_function = disabled;
+}
+
+/**
+ * @brief Get if originating function name logging should be disabled.
+ *
+ * @return if #EINA_TRUE, function name logging should be disabled.
+ *
+ * @see eina_log_function_disable_set()
+ */
+EAPI Eina_Bool
+eina_log_function_disable_get(void)
+{
+   return _disable_function;
+}
+
+/**
+ * @brief Set if critical messages should abort the program.
+ *
+ * @param abort_on_critical if #EINA_TRUE, messages with level equal
+ *        or smaller than eina_log_abort_on_critical_level_get() will
+ *        abort the program.
+ *
+ * @note this is initially set to envvar EINA_LOG_ABORT by
+ *       eina_init().
+ *
+ * @see eina_log_abort_on_critical_get()
+ * @see eina_log_abort_on_critical_level_set()
+ */
+EAPI void
+eina_log_abort_on_critical_set(Eina_Bool abort_on_critical)
+{
+   _abort_on_critical = abort_on_critical;
+}
+
+/**
+ * @brief Get if critical messages should abort the program.
+ *
+ * @return if #EINA_TRUE, any messages with level equal or smaller
+ *         than eina_log_abort_on_critical_level_get() will abort the
+ *         program.
+ *
+ * @see eina_log_abort_on_critical_set()
+ * @see eina_log_abort_on_critical_level_set()
+ */
+EAPI Eina_Bool
+eina_log_abort_on_critical_get(void)
+{
+   return _abort_on_critical;
+}
+
+/**
+ * @brief Set level that triggers abort if abort-on-critical is set.
+ *
+ * @param critical_level levels equal or smaller than the given value
+ *        will trigger program abortion if
+ *        eina_log_abort_on_critical_get() returns #EINA_TRUE.
+ *
+ * @note this is initially set to envvar EINA_LOG_ABORT_LEVEL by
+ *       eina_init().
+ *
+ * @see eina_log_abort_on_critical_level_get()
+ * @see eina_log_abort_on_critical_get()
+ */
+EAPI void
+eina_log_abort_on_critical_level_set(int critical_level)
+{
+   _abort_level_on_critical = critical_level;
+}
+
+/**
+ * @brief Get level that triggers abort if abort-on-critical is set.
+ *
+ * @return critical level equal or smaller than value will trigger
+ *        program abortion if eina_log_abort_on_critical_get() returns
+ *        #EINA_TRUE.
+ *
+ * @see eina_log_abort_on_critical_level_set()
+ * @see eina_log_abort_on_critical_get()
+ */
+EAPI int
+eina_log_abort_on_critical_level_get(void)
+{
+   return _abort_level_on_critical;
+}
+
+/**
+ * @param name Domain name
+ * @param color Color of the domain name
+ *
+ * @return Domain index that will be used as the DOMAIN parameter on log
+ *         macros. A negative return value means an log occurred.
+ *
+ * @note MT: safe to call from any thread.
+ */
+EAPI int
+eina_log_domain_register(const char *name, const char *color)
+{
+   int r;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(name, -1);
+
+   LOG_LOCK();
+   r = eina_log_domain_register_unlocked(name, color);
+   LOG_UNLOCK();
+   return r;
+}
+
+/**
+ * Forget about a logging domain registered by eina_log_domain_register()
+ *
+ * @param domain domain identifier as reported by eina_log_domain_register(),
+ *        must be >= 0.
+ *
+ * @note MT: safe to call from any thread.
+ */
+EAPI void
+eina_log_domain_unregister(int domain)
+{
+   EINA_SAFETY_ON_FALSE_RETURN(domain >= 0);
+   LOG_LOCK();
+   eina_log_domain_unregister_unlocked(domain);
+   LOG_UNLOCK();
+}
+
+/**
+ * Set the domain level given its name.
+ *
+ * This call has the same effect as setting
+ * EINA_LOG_LEVELS=&lt;@p domain_name&gt;:&lt;@p level&gt;
+ *
+ * @param domain_name domain name to change the level. It may be of a
+ *        still not registered domain. If the domain is not registered
+ *        yet, it will be saved as a pending set and applied upon
+ *        registration.
+ * @param level level to use to limit eina_log_print() for given domain.
+ */
+EAPI void
+eina_log_domain_level_set(const char *domain_name, int level)
+{
+   Eina_Log_Domain_Level_Pending *pending;
+   size_t namelen;
+   unsigned int i;
+
+   EINA_SAFETY_ON_NULL_RETURN(domain_name);
+
+   namelen = strlen(domain_name);
+
+   for (i = 0; i < _log_domains_count; i++)
+     {
+        if (_log_domains[i].deleted)
+           continue;
+
+        if ((namelen != _log_domains[i].namelen) ||
+            (strcmp(_log_domains[i].name, domain_name) != 0))
+           continue;
+
+        _log_domains[i].level = level;
+        return;
+     }
+
+   EINA_INLIST_FOREACH(_pending_list, pending)
+   {
+      if ((namelen == pending->namelen) &&
+          (strcmp(pending->name, domain_name) == 0))
+        {
+           pending->level = level;
+           return;
+        }
+   }
+
+   pending = malloc(sizeof(Eina_Log_Domain_Level_Pending) + namelen + 1);
+   if (!pending)
+      return;
+
+   pending->level = level;
+   pending->namelen = namelen;
+   memcpy(pending->name, domain_name, namelen + 1);
+
+   _pending_list = eina_inlist_append(_pending_list, EINA_INLIST_GET(pending));
+}
+
+/**
+ * Get the domain level given its name.
+ *
+ * @param domain_name domain name to retrieve the level. It may be of
+ *        a still not registered domain. If the domain is not
+ *        registered yet, but there is a pending value, either from
+ *        eina_log_domain_level_set(),EINA_LOG_LEVELS environment
+ *        variable or from EINA_LOG_LEVELS_GLOB, these are
+ *        returned. If nothing else was found, then the global/default
+ *        level (eina_log_level_get()) is returned.
+ *
+ * @return level to use to limit eina_log_print() for given
+ *         domain. On error (@p domain_name == NULL),
+ *         EINA_LOG_LEVEL_UNKNOWN is returned.
+ *
+ * @see eina_log_domain_level_set()
+ * @see eina_log_domain_registered_level_get()
+ */
+EAPI int
+eina_log_domain_level_get(const char *domain_name)
+{
+   Eina_Log_Domain_Level_Pending *pending;
+   size_t namelen;
+   unsigned int i;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(domain_name, EINA_LOG_LEVEL_UNKNOWN);
+
+   namelen = strlen(domain_name);
+
+   for (i = 0; i < _log_domains_count; i++)
+     {
+        if (_log_domains[i].deleted)
+           continue;
+
+        if ((namelen != _log_domains[i].namelen) ||
+            (strcmp(_log_domains[i].name, domain_name) != 0))
+           continue;
+
+        return _log_domains[i].level;
+     }
+
+   EINA_INLIST_FOREACH(_pending_list, pending)
+   {
+      if ((namelen == pending->namelen) &&
+          (strcmp(pending->name, domain_name) == 0))
+         return pending->level;
+   }
+
+   EINA_INLIST_FOREACH(_glob_list, pending)
+   {
+      if (!fnmatch(pending->name, domain_name, 0))
+         return pending->level;
+   }
+
+   return _log_level;
+}
+
+/**
+ * Get the domain level given its identifier.
+ *
+ * @param domain identifier, so it must be previously registered with
+ *        eina_log_domain_register(). It's a much faster version of
+ *        eina_log_domain_level_get(), but relies on domain being
+ *        present.
+ *
+ * @return level to use to limit eina_log_print() for given domain. On
+ *         error EINA_LOG_LEVEL_UNKNOWN is returned.
+ */
+EAPI int
+eina_log_domain_registered_level_get(int domain)
+{
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(domain >= 0, EINA_LOG_LEVEL_UNKNOWN);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL((unsigned int)domain < _log_domains_count,
+                                   EINA_LOG_LEVEL_UNKNOWN);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(_log_domains[domain].deleted,
+                                  EINA_LOG_LEVEL_UNKNOWN);
+   return _log_domains[domain].level;
+}
+
+/**
+ * Default logging method, this will output to standard error stream.
+ *
+ * This method will colorize output based on domain provided color and
+ * message logging level.
+ *
+ * To disable color, set environment variable
+ * EINA_LOG_COLOR_DISABLE=1. To enable color, even if directing to a
+ * file or when using a non-supported color terminal, use
+ * EINA_LOG_COLOR_DISABLE=0. If EINA_LOG_COLOR_DISABLE is unset (or
+ * -1), then Eina will disable color if terminal ($TERM) is
+ * unsupported or if redirecting to a file.
+
+   . Similarly, to disable file and line
+ * information, set EINA_LOG_FILE_DISABLE=1 or
+ * EINA_LOG_FUNCTION_DISABLE=1 to avoid function name in output. It is
+ * not acceptable to have both EINA_LOG_FILE_DISABLE and
+ * EINA_LOG_FUNCTION_DISABLE at the same time, in this case just
+ * EINA_LOG_FUNCTION_DISABLE will be considered and file information
+ * will be printed anyways.
+ *
+ * @note MT: if threads are enabled, this function is called within locks.
+ * @note MT: Threads different from main thread will have thread id
+ *       appended to domain name.
+ */
+EAPI void
+eina_log_print_cb_stderr(const Eina_Log_Domain *d,
+                         Eina_Log_Level level,
+                         const char *file,
+                         const char *fnc,
+                         int line,
+                         const char *fmt,
+                         __UNUSED__ void *data,
+                         va_list args)
+{
+   _eina_log_print_prefix(stderr, d, level, file, fnc, line);
+   vfprintf(stderr, fmt, args);
+   putc('\n', stderr);
+}
+
+/**
+ * Alternative logging method, this will output to standard output stream.
+ *
+ * @param d The domain.
+ * @param level The level.
+ * @param file The file which is logged.
+ * @param fnc The function which is logged.
+ * @param line The line which is logged.
+ * @param fmt The ouptut format to use.
+ * @param data Not used.
+ * @param args The arguments needed by the format.
+ *
+ * This method will colorize output based on domain provided color and
+ * message logging level. To disable color, set environment variable
+ * EINA_LOG_COLOR_DISABLE=1. Similarly, to disable file and line
+ * information, set EINA_LOG_FILE_DISABLE=1 or
+ * EINA_LOG_FUNCTION_DISABLE=1 to avoid function name in output. It is
+ * not acceptable to have both EINA_LOG_FILE_DISABLE and
+ * EINA_LOG_FUNCTION_DISABLE at the same time, in this case just
+ * EINA_LOG_FUNCTION_DISABLE will be considered and file information
+ * will be printed anyways.
+ *
+ * @note MT: if threads are enabled, this function is called within locks.
+ * @note MT: Threads different from main thread will have thread id
+ *       appended to domain name.
+ */
+EAPI void
+eina_log_print_cb_stdout(const Eina_Log_Domain *d,
+                         Eina_Log_Level level,
+                         const char *file,
+                         const char *fnc,
+                         int line,
+                         const char *fmt,
+                         __UNUSED__ void *data,
+                         va_list args)
+{
+   _eina_log_print_prefix(stdout, d, level, file, fnc, line);
+   vprintf(fmt, args);
+   putchar('\n');
+}
+
+/**
+ * Alternative logging method, this will output to given file stream.
+ *
+ * @param d The domain.
+ * @param level Not used.
+ * @param file The file which is logged.
+ * @param fnc The function which is logged.
+ * @param line The line which is logged.
+ * @param fmt The ouptut format to use.
+ * @param data The file which will store the output (as a FILE *).
+ * @param args The arguments needed by the format.
+ *
+ * This method will never output color.
+ *
+ * @note MT: if threads are enabled, this function is called within locks.
+ * @note MT: Threads different from main thread will have thread id
+ *       appended to domain name.
+ */
+EAPI void
+eina_log_print_cb_file(const Eina_Log_Domain *d,
+                       __UNUSED__ Eina_Log_Level level,
+                       const char *file,
+                       const char *fnc,
+                       int line,
+                       const char *fmt,
+                       void *data,
+                       va_list args)
+{
+   FILE *f = data;
+#ifdef EFL_HAVE_THREADS
+   if (_threads_enabled)
+     {
+        Thread cur;
+
+        cur = SELF();
+        if (IS_OTHER(cur))
+          {
+             fprintf(f, "%s[T:%lu] %s:%d %s() ", d->name, (unsigned long)cur,
+               file, line, fnc);
+             goto end;
+          }
+     }
+
+#endif
+             fprintf(f, "%s %s:%d %s() ", d->name, file, line, fnc);
+#ifdef EFL_HAVE_THREADS
+end:
+#endif
+   vfprintf(f, fmt, args);
+   putc('\n', f);
+}
+
+/**
+ * Print out log message using given domain and level.
+ *
+ * @note Usually you'll not use this function directly but the helper
+ *       macros EINA_LOG(), EINA_LOG_DOM_CRIT(), EINA_LOG_CRIT() and
+ *       so on. See eina_log.h
+ *
+ * @param domain logging domain to use or @c EINA_LOG_DOMAIN_GLOBAL if
+ *        you registered none. It is recommended that modules and
+ *        applications have their own logging domain.
+ * @param level message level, those with level greater than user
+ *        specified value (eina_log_level_set() or environment
+ *        variables EINA_LOG_LEVEL, EINA_LOG_LEVELS) will be ignored.
+ * @param file filename that originated the call, must @b not be @c NULL.
+ * @param fnc function that originated the call, must @b not be @c NULL.
+ * @param line originating line in @a file.
+ * @param fmt printf-like format to use. Should not provide trailing
+ *        '\n' as it is automatically included.
+ *
+ * @note MT: this function may be called from different threads if
+ *       eina_log_threads_enable() was called before.
+ */
+EAPI void
+eina_log_print(int domain, Eina_Log_Level level, const char *file,
+               const char *fnc, int line, const char *fmt, ...)
+{
+   va_list args;
+
+#ifdef EINA_SAFETY_CHECKS
+   if (EINA_UNLIKELY(!file))
+     {
+        fputs("ERR: eina_log_print() file == NULL\n", stderr);
+        return;
+     }
+
+   if (EINA_UNLIKELY(!fnc))
+     {
+        fputs("ERR: eina_log_print() fnc == NULL\n", stderr);
+        return;
+     }
+
+   if (EINA_UNLIKELY(!fmt))
+     {
+        fputs("ERR: eina_log_print() fmt == NULL\n", stderr);
+        return;
+     }
+
+#endif
+   va_start(args, fmt);
+   LOG_LOCK();
+   eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args);
+   LOG_UNLOCK();
+   va_end(args);
+}
+
+/**
+ * Print out log message using given domain and level.
+ *
+ * @note Usually you'll not use this function directly but the helper
+ *       macros EINA_LOG(), EINA_LOG_DOM_CRIT(), EINA_LOG_CRIT() and
+ *       so on. See eina_log.h
+ *
+ * @param domain logging domain to use or @c EINA_LOG_DOMAIN_GLOBAL if
+ *        you registered none. It is recommended that modules and
+ *        applications have their own logging domain.
+ * @param level message level, those with level greater than user
+ *        specified value (eina_log_level_set() or environment
+ *        variables EINA_LOG_LEVEL, EINA_LOG_LEVELS) will be ignored.
+ * @param file filename that originated the call, must @b not be @c NULL.
+ * @param fnc function that originated the call, must @b not be @c NULL.
+ * @param line originating line in @a file.
+ * @param fmt printf-like format to use. Should not provide trailing
+ *        '\n' as it is automatically included.
+ * @param args the arguments needed by the format.
+ *
+ * @note MT: this function may be called from different threads if
+ *       eina_log_threads_enable() was called before.
+ *
+ * @see eina_log_print()
+ */
+EAPI void
+eina_log_vprint(int domain, Eina_Log_Level level, const char *file,
+                const char *fnc, int line, const char *fmt, va_list args)
+{
+#ifdef EINA_SAFETY_CHECKS
+   if (EINA_UNLIKELY(!file))
+     {
+        fputs("ERR: eina_log_print() file == NULL\n", stderr);
+        return;
+     }
+
+   if (EINA_UNLIKELY(!fnc))
+     {
+        fputs("ERR: eina_log_print() fnc == NULL\n", stderr);
+        return;
+     }
+
+   if (EINA_UNLIKELY(!fmt))
+     {
+        fputs("ERR: eina_log_print() fmt == NULL\n", stderr);
+        return;
+     }
+
+#endif
+   LOG_LOCK();
+   eina_log_print_unlocked(domain, level, file, fnc, line, fmt, args);
+   LOG_UNLOCK();
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_magic.c b/tests/suite/ecore/src/lib/eina_magic.c
new file mode 100644 (file)
index 0000000..83cd247
--- /dev/null
@@ -0,0 +1,487 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_error.h"
+#include "eina_log.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_magic.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+typedef struct _Eina_Magic_String Eina_Magic_String;
+struct _Eina_Magic_String
+{
+   Eina_Magic magic;
+   Eina_Bool string_allocated;
+   const char *string;
+};
+
+static int _eina_magic_string_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_magic_string_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_magic_string_log_dom, __VA_ARGS__)
+
+static Eina_Magic_String *_eina_magic_strings = NULL;
+static size_t _eina_magic_strings_count = 0;
+static size_t _eina_magic_strings_allocated = 0;
+static Eina_Bool _eina_magic_strings_dirty = 0;
+
+static int
+_eina_magic_strings_sort_cmp(const void *p1, const void *p2)
+{
+   const Eina_Magic_String *a = p1, *b = p2;
+   return a->magic - b->magic;
+}
+
+static int
+_eina_magic_strings_find_cmp(const void *p1, const void *p2)
+{
+   Eina_Magic a = (long)p1;
+   const Eina_Magic_String *b = p2;
+   return a - b->magic;
+}
+
+static Eina_Magic_String *
+_eina_magic_strings_alloc(void)
+{
+   size_t idx;
+
+   if (_eina_magic_strings_count == _eina_magic_strings_allocated)
+     {
+        void *tmp;
+        size_t size;
+
+        if (EINA_UNLIKELY(_eina_magic_strings_allocated == 0))
+           size = 48;
+        else
+           size = _eina_magic_strings_allocated + 16;
+
+        tmp = realloc(_eina_magic_strings, sizeof(Eina_Magic_String) * size);
+        if (!tmp)
+          {
+#ifdef _WIN32
+             ERR("could not realloc magic_strings from %Iu to %Iu buckets.",
+#else
+             ERR("could not realloc magic_strings from %zu to %zu buckets.",
+#endif
+                 _eina_magic_strings_allocated, size);
+             return NULL;
+          }
+
+        _eina_magic_strings = tmp;
+        _eina_magic_strings_allocated = size;
+     }
+
+   idx = _eina_magic_strings_count;
+   _eina_magic_strings_count++;
+   return _eina_magic_strings + idx;
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the magic string module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the magic string module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_magic_string_init(void)
+{
+   _eina_magic_string_log_dom = eina_log_domain_register
+         ("eina_magic_string", EINA_LOG_COLOR_DEFAULT);
+   if (_eina_magic_string_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_magic_string");
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the magic string module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the magic string module set up by
+ * eina_magic string_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_magic_string_shutdown(void)
+{
+   Eina_Magic_String *ems, *ems_end;
+
+   ems = _eina_magic_strings;
+   ems_end = ems + _eina_magic_strings_count;
+
+   for (; ems < ems_end; ems++)
+      if (ems->string_allocated)
+         free((char *)ems->string);
+
+         free(_eina_magic_strings);
+   _eina_magic_strings = NULL;
+   _eina_magic_strings_count = 0;
+   _eina_magic_strings_allocated = 0;
+
+   eina_log_domain_unregister(_eina_magic_string_log_dom);
+   _eina_magic_string_log_dom = -1;
+
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Magic_Group Magic
+ *
+ * @brief These functions provide runtime type-checking (magic checks)
+ * management for projects.
+ *
+ * C is a weak statically typed language, in other words, it will just
+ * check for types during compile time and any cast will make the
+ * compiler believe the type is correct.
+ *
+ * In real world projects we often need to deal with casts, either
+ * explicit or implicit by means of @c void*. We also need to resort
+ * to casts when doing inheritance in C, as seen in the example below:
+ *
+ * @code
+ * struct base {
+ *    int id;
+ *    char *name;
+ * };
+ * int base_id_get(struct base *ptr) {
+ *    return ptr->id;
+ * }
+ *
+ * struct subtype {
+ *    struct base base;
+ *    time_t date;
+ * };
+ * @endcode
+ *
+ * It is perfectly valid to use @c {struct subtype} blobs for functions
+ * that expect @c {struct base}, since the fields will have the same
+ * offset (as base member is the first, at offset 0). We could give
+ * the functions the @c {&subtype->base} and avoid the cast, but often
+ * we just cast.
+ *
+ * In any case, we might be safe and check if the given pointer is
+ * actually of the expected type. We can do so by using eina_magic,
+ * that is nothing more than attaching an unique type identifier to
+ * the members and check for it elsewhere.
+ *
+ * @code
+ * #define BASE_MAGIC 0x12345
+ * #define SUBTYPE_MAGIC 0x3333
+ * struct base {
+ *    int id;
+ *    char *name;
+ *    EINA_MAGIC;
+ * };
+ * int base_id_get(struct base *ptr) {
+ *    if (!EINA_MAGIC_CHECK(ptr, BASE_MAGIC)) {
+ *       EINA_MAGIC_FAIL(ptr, BASE_MAGIC);
+ *       return -1;
+ *    }
+ *    return ptr->id;
+ * }
+ * void base_free(struct base *ptr) {
+ *    if (!EINA_MAGIC_CHECK(ptr, BASE_MAGIC)) {
+ *       EINA_MAGIC_FAIL(ptr, BASE_MAGIC);
+ *       return;
+ *    }
+ *    EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE);
+ *    free(ptr->name);
+ *    free(ptr);
+ * }
+ * struct base *base_new(int id, const char *name) {
+ *    struct base *ptr = malloc(sizeof(struct base));
+ *    EINA_MAGIC_SET(ptr, BASE_MAGIC);
+ *    ptr->id = id;
+ *    ptr->name = strdup(name);
+ * }
+ *
+ * struct subtype {
+ *    struct base base;
+ *    EINA_MAGIC;
+ *    time_t date;
+ * };
+ *
+ * int my_init(void) {
+ *    eina_init();
+ *    eina_magic_string_set(BASE_MAGIC, "base type");
+ *    eina_magic_string_set(SUBTYPE_MAGIC, "subtype");
+ * }
+ * @endcode
+ *
+ * This code also shows that it is a good practice to set magic to
+ * #EINA_MAGIC_NONE before freeing pointer. Sometimes the pointers are
+ * in pages that are still live in memory, so kernel will not send
+ * SEGV signal to the process and it may go unnoticed that you're
+ * using already freed pointers. By setting them to #EINA_MAGIC_NONE
+ * you avoid using the bogus pointer any further and gets a nice error
+ * message.
+ *
+ * @{
+ */
+
+/**
+ * @brief Return the string associated to the given magic identifier.
+ *
+ * @param magic The magic identifier.
+ * @return The string associated to the identifier.
+ *
+ * This function returns the string associated to @p magic. If none
+ * are found, the this function still returns non @c NULL, in this
+ * case an identifier such as "(none)", "(undefined)" or
+ * "(unknown)". The returned value must not be freed.
+ *
+ * The following identifiers may be returned whenever magic is
+ * invalid, with their meanings:
+ *
+ *   - (none): no magic was registered exists at all.
+ *   - (undefined): magic was registered and found, but no string associated.
+ *   - (unknown): magic was not found in the registry.
+ */
+EAPI const char *
+eina_magic_string_get(Eina_Magic magic)
+{
+   Eina_Magic_String *ems;
+
+   if (!_eina_magic_strings)
+      return "(none)";
+
+   if (_eina_magic_strings_dirty)
+     {
+        qsort(_eina_magic_strings, _eina_magic_strings_count,
+              sizeof(Eina_Magic_String), _eina_magic_strings_sort_cmp);
+        _eina_magic_strings_dirty = 0;
+     }
+
+   ems = bsearch((void *)(long)magic, _eina_magic_strings,
+                 _eina_magic_strings_count, sizeof(Eina_Magic_String),
+                 _eina_magic_strings_find_cmp);
+   if (ems)
+      return ems->string ? ems->string : "(undefined)";
+
+   return "(unknown)";
+}
+
+/**
+ * @brief Set the string associated to the given magic identifier.
+ *
+ * @param magic The magic identifier.
+ * @param magic_name The string associated to the identifier, must not
+ *        be @c NULL.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets the string @p magic_name to @p magic. It is not
+ * checked if number or string are already set, then you might end
+ * with duplicates in that case.
+ *
+ * @see eina_magic_string_static_set()
+ */
+EAPI Eina_Bool
+eina_magic_string_set(Eina_Magic magic, const char *magic_name)
+{
+   Eina_Magic_String *ems;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(magic_name, EINA_FALSE);
+
+   ems = _eina_magic_strings_alloc();
+   if (!ems)
+      return EINA_FALSE;
+
+   ems->magic = magic;
+   ems->string_allocated = EINA_TRUE;
+   ems->string = strdup(magic_name);
+   if (!ems->string)
+   {
+      ERR("could not allocate string '%s'", magic_name);
+      _eina_magic_strings_count--;
+      return EINA_FALSE;
+   }
+
+   _eina_magic_strings_dirty = 1;
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Set the string associated to the given magic identifier.
+ *
+ * @param magic The magic identifier.
+ * @param magic_name The string associated to the identifier, must not be
+ *        @c NULL, it will not be duplcated, just referenced thus it must
+ *        be live during magic number usage.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets the string @p magic_name to @p magic. It is not
+ * checked if number or string are already set, then you might end
+ * with duplicates in that case.
+ *
+ * @see eina_magic_string_set()
+ */
+EAPI Eina_Bool
+eina_magic_string_static_set(Eina_Magic magic, const char *magic_name)
+{
+   Eina_Magic_String *ems;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(magic_name, EINA_FALSE);
+
+   ems = _eina_magic_strings_alloc();
+   if (!ems)
+      return EINA_FALSE;
+
+   ems->magic = magic;
+   ems->string_allocated = EINA_FALSE;
+   ems->string = magic_name;
+
+   _eina_magic_strings_dirty = 1;
+   return EINA_TRUE;
+}
+
+#ifdef eina_magic_fail
+# undef eina_magic_fail
+#endif
+
+/**
+ * @brief Display a message or abort is a magic check failed.
+ *
+ * @param d The checked data pointer.
+ * @param m The magic identifer to check.
+ * @param req_m The requested magic identifier to check.
+ * @param file The file in which the magic check failed.
+ * @param fnc The function in which the magic check failed.
+ * @param line The line at which the magic check failed.
+ *
+ * This function displays an error message if a magic check has
+ * failed, using the following logic in the following order:
+ * @li If @p d is @c NULL, a message warns about a @c NULL pointer.
+ * @li Otherwise, if @p m is equal to #EINA_MAGIC_NONE, a message
+ * warns about a handle that was already freed.
+ * @li Otherwise, if @p m is equal to @p req_m, a message warns about
+ * a handle that is of wrong type.
+ * @li Otherwise, a message warns you about ab-using that function...
+ *
+ * If the environment variable EINA_ERROR_ABORT is set, abort() is
+ * called and the program stops. It is useful for debugging programs
+ * with gdb.
+ */
+EAPI void
+eina_magic_fail(void *d,
+                Eina_Magic m,
+                Eina_Magic req_m,
+                const char *file,
+                const char *fnc,
+                int line)
+{
+   if (!d)
+      eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
+                     file, fnc, line,
+                     "*** Eina Magic Check Failed !!!\n"
+                     "    Input handle pointer is NULL !\n"
+                     "*** NAUGHTY PROGRAMMER!!!\n"
+                     "*** SPANK SPANK SPANK!!!\n"
+                     "*** Now go fix your code. Tut tut tut!\n"
+                     "\n");
+   else
+   if (m == EINA_MAGIC_NONE)
+      eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
+                     file, fnc, line,
+                     "*** Eina Magic Check Failed !!!\n"
+                     "    Input handle has already been freed!\n"
+                     "*** NAUGHTY PROGRAMMER!!!\n"
+                     "*** SPANK SPANK SPANK!!!\n"
+                     "*** Now go fix your code. Tut tut tut!\n"
+                     "\n");
+   else
+   if (m != req_m)
+      eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
+                     file, fnc, line,
+                     "*** Eina Magic Check Failed !!!\n"
+                     "    Input handle is wrong type\n"
+                     "    Expected: %08x - %s\n"
+                     "    Supplied: %08x - %s\n"
+                     "*** NAUGHTY PROGRAMMER!!!\n"
+                     "*** SPANK SPANK SPANK!!!\n"
+                     "*** Now go fix your code. Tut tut tut!\n"
+                     "\n",
+                     req_m, eina_magic_string_get(req_m),
+                     m, eina_magic_string_get(m));
+   else
+      eina_log_print(EINA_LOG_DOMAIN_GLOBAL, EINA_LOG_LEVEL_CRITICAL,
+                     file, fnc, line,
+                     "*** Eina Magic Check Failed !!!\n"
+                     "    Why did you call me !\n"
+                     "*** NAUGHTY PROGRAMMER!!!\n"
+                     "*** SPANK SPANK SPANK!!!\n"
+                     "*** Now go fix your code. Tut tut tut!\n"
+                     "\n");
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_main.c b/tests/suite/ecore/src/lib/eina_main.c
new file mode 100644 (file)
index 0000000..ceae398
--- /dev/null
@@ -0,0 +1,377 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef EFL_HAVE_POSIX_THREADS
+# include <pthread.h>
+#endif
+
+#ifdef EFL_HAVE_WIN32_THREADS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_types.h"
+#include "eina_main.h"
+#include "eina_error.h"
+#include "eina_log.h"
+#include "eina_hash.h"
+#include "eina_binshare.h"
+#include "eina_stringshare.h"
+#include "eina_ustringshare.h"
+#include "eina_list.h"
+#include "eina_matrixsparse.h"
+#include "eina_array.h"
+#include "eina_counter.h"
+#include "eina_benchmark.h"
+#include "eina_magic.h"
+#include "eina_rectangle.h"
+#include "eina_safety_checks.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static Eina_Version _version = { VMAJ, VMIN, VMIC, VREV };
+
+static int _eina_main_count = 0;
+#ifdef EFL_HAVE_THREADS
+static int _eina_main_thread_count = 0;
+#endif
+static int _eina_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_log_dom, __VA_ARGS__)
+
+#ifdef EFL_HAVE_THREADS
+static Eina_Bool _threads_activated = EINA_FALSE;
+# ifdef EFL_HAVE_POSIX_THREADS
+static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
+#  define LOCK() if(_threads_activated) pthread_mutex_lock(&_mutex)
+#  define UNLOCK() if(_threads_activated) pthread_mutex_unlock(&_mutex)
+#  define UNLOCK_FORCE() pthread_mutex_unlock(&_mutex)
+# else /* EFL_HAVE_WIN32_THREADS */
+static HANDLE _mutex = NULL;
+#  define LOCK() if(_threads_activated) WaitForSingleObject(_mutex, INFINITE)
+#  define UNLOCK() if(_threads_activated) ReleaseMutex(_mutex)
+#  define UNLOCK_FORCE() ReleaseMutex(_mutex)
+# endif
+#else
+# define LOCK() do {} while (0)
+# define UNLOCK() do {} while (0)
+# define UNLOCK_FORCE() do {} while (0)
+#endif
+
+/* place module init/shutdown functions here to avoid other modules
+ * calling them by mistake.
+ */
+#define S(x) extern Eina_Bool eina_ ## x ## _init(void); \
+   extern Eina_Bool eina_ ## x ## _shutdown(void)
+   S(log);
+   S(error);
+   S(safety_checks);
+   S(magic_string);
+   S(iterator);
+   S(accessor);
+   S(array);
+   S(module);
+   S(mempool);
+   S(list);
+   S(binshare);
+   S(stringshare);
+   S(ustringshare);
+   S(matrixsparse);
+   S(convert);
+   S(counter);
+   S(benchmark);
+   S(rectangle);
+   S(strbuf);
+   S(ustrbuf);
+   S(quadtree);
+#undef S
+
+struct eina_desc_setup
+{
+   const char *name;
+   Eina_Bool (*init)(void);
+   Eina_Bool (*shutdown)(void);
+};
+
+static const struct eina_desc_setup _eina_desc_setup[] = {
+#define S(x) {# x, eina_ ## x ## _init, eina_ ## x ## _shutdown}
+   /* log is a special case as it needs printf */
+   S(error),
+   S(safety_checks),
+   S(magic_string),
+   S(iterator),
+   S(accessor),
+   S(array),
+   S(module),
+   S(mempool),
+   S(list),
+   S(binshare),
+   S(stringshare),
+   S(ustringshare),
+   S(matrixsparse),
+   S(convert),
+   S(counter),
+   S(benchmark),
+   S(rectangle),
+   S(strbuf),
+   S(ustrbuf),
+   S(quadtree)
+#undef S
+};
+static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) /
+   sizeof(_eina_desc_setup[0]);
+
+static void
+_eina_shutdown_from_desc(const struct eina_desc_setup *itr)
+{
+   for (itr--; itr >= _eina_desc_setup; itr--)
+     {
+        if (!itr->shutdown())
+           ERR("Problems shutting down eina module '%s', ignored.", itr->name);
+     }
+
+   eina_log_domain_unregister(_eina_log_dom);
+   _eina_log_dom = -1;
+   eina_log_shutdown();
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Main_Group Main
+ *
+ * @brief These functions provide general initialisation and shut down
+ * functions.
+ *
+ * @{
+ */
+
+/**
+ * @var eina_version
+ * @brief Eina version (defined at configuration time)
+ */
+EAPI Eina_Version *eina_version = &_version;
+
+/**
+ * @brief Initialize the Eina library.
+ *
+ * @return 1 or greater on success, 0 on error.
+ *
+ * This function sets up all the eina modules. It returns 0 on
+ * failure (that is, when one of the module fails to initialize),
+ * otherwise it returns the number of times it has already been
+ * called.
+ *
+ * When Eina is not used anymore, call eina_shutdown() to shut down
+ * the Eina library.
+ */
+EAPI int
+eina_init(void)
+{
+   const struct eina_desc_setup *itr, *itr_end;
+
+   if (EINA_LIKELY(_eina_main_count > 0))
+      return ++_eina_main_count;
+
+   if (!eina_log_init())
+     {
+        fprintf(stderr, "Could not initialize eina logging system.\n");
+        return 0;
+     }
+
+   _eina_log_dom = eina_log_domain_register("eina", EINA_LOG_COLOR_DEFAULT);
+   if (_eina_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina");
+        eina_log_shutdown();
+        return 0;
+     }
+
+   itr = _eina_desc_setup;
+   itr_end = itr + _eina_desc_setup_len;
+   for (; itr < itr_end; itr++)
+     {
+        if (!itr->init())
+          {
+             ERR("Could not initialize eina module '%s'.", itr->name);
+             _eina_shutdown_from_desc(itr);
+             return 0;
+          }
+     }
+
+   _eina_main_count = 1;
+   return 1;
+}
+
+/**
+ * @brief Shut down the Eina library.
+ *
+ * @return 0 when all the modules is completely shut down, 1 or
+ * greater otherwise.
+ *
+ * This function shuts down the Eina library. It returns 0 when it has
+ * been called the same number of times than eina_init(). In that case
+ * it shut down all the Eina modules.
+ *
+ * Once this function succeeds (that is, @c 0 is returned), you must
+ * not call any of the Eina function anymore. You must call
+ * eina_init() again to use the Eina functions again.
+ */
+EAPI int
+eina_shutdown(void)
+{
+   _eina_main_count--;
+   if (EINA_UNLIKELY(_eina_main_count == 0))
+             _eina_shutdown_from_desc(_eina_desc_setup + _eina_desc_setup_len);
+
+   return _eina_main_count;
+}
+
+
+/**
+ * @brief Initialize the mutexes of the Eina library.
+ *
+ * @return 1 or greater on success, 0 on error.
+ *
+ * This function sets up all the mutexes in all eina modules. It returns 0 on
+ * failure (that is, when one of the module fails to initialize),
+ * otherwise it returns the number of times it has already been
+ * called.
+ *
+ * When the mutexes are not used anymore, call eina_threads_shutdown() to shut down
+ * the mutexes.
+ */
+EAPI int
+eina_threads_init(void)
+{
+#ifdef EFL_HAVE_THREADS
+   int ret;
+
+# ifdef EFL_HAVE_WIN32_THREADS
+   if (!_mutex)
+      _mutex = CreateMutex(NULL, FALSE, NULL);
+
+   if (!_mutex)
+      return 0;
+
+# endif
+
+   LOCK();
+   ++_eina_main_thread_count;
+   ret = _eina_main_thread_count;
+
+   if(_eina_main_thread_count > 1)
+     {
+        UNLOCK();
+        return ret;
+     }
+
+   eina_share_common_threads_init();
+   eina_log_threads_init();
+   _threads_activated = EINA_TRUE;
+
+   return ret;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @brief Shut down mutexes in the Eina library.
+ *
+ * @return 0 when all mutexes are completely shut down, 1 or
+ * greater otherwise.
+ *
+ * This function shuts down the mutexes in the Eina library. It returns 0 when it has
+ * been called the same number of times than eina_threads_init(). In that case
+ * it shut down all the mutexes.
+ *
+ * Once this function succeeds (that is, @c 0 is returned), you must
+ * not call any of the Eina function in a thread anymore. You must call
+ * eina_threads_init() again to use the Eina functions in a thread again.
+ */
+EAPI int
+eina_threads_shutdown(void)
+{
+#ifdef EFL_HAVE_THREADS
+   int ret;
+
+   LOCK();
+   ret = --_eina_main_thread_count;
+   if(_eina_main_thread_count > 0)
+     {
+        UNLOCK();
+        return ret;
+     }
+
+   eina_share_common_threads_shutdown();
+   eina_log_threads_shutdown();
+
+   _threads_activated = EINA_FALSE;
+
+   UNLOCK_FORCE();
+
+# ifdef EFL_HAVE_WIN32_THREADS
+   if (_mutex)
+      CloseHandle(_mutex);
+
+# endif
+
+   return ret;
+#else
+   return 0;
+#endif
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_matrixsparse.c b/tests/suite/ecore/src/lib/eina_matrixsparse.c
new file mode 100644 (file)
index 0000000..e19f24a
--- /dev/null
@@ -0,0 +1,1692 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2009 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/**
+ * @page tutorial_matrixsparse_page Sparse Matrix Tutorial
+ *
+ * to be written...
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_error.h"
+#include "eina_log.h"
+#include "eina_magic.h"
+#include "eina_mempool.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_matrixsparse.h"
+
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char EINA_MAGIC_MATRIXSPARSE_STR[] = "Eina Matrixsparse";
+static const char EINA_MAGIC_MATRIXSPARSE_ROW_STR[] = "Eina Matrixsparse Row";
+static const char EINA_MAGIC_MATRIXSPARSE_CELL_STR[] = "Eina Matrixsparse Cell";
+static const char EINA_MAGIC_MATRIXSPARSE_ITERATOR_STR[] =
+   "Eina Matrixsparse Iterator";
+static const char EINA_MAGIC_MATRIXSPARSE_ROW_ACCESSOR_STR[] =
+   "Eina Matrixsparse Row Accessor";
+static const char EINA_MAGIC_MATRIXSPARSE_ROW_ITERATOR_STR[] =
+   "Eina Matrixsparse Row Iterator";
+static const char EINA_MAGIC_MATRIXSPARSE_CELL_ACCESSOR_STR[] =
+   "Eina Matrixsparse Cell Accessor";
+static const char EINA_MAGIC_MATRIXSPARSE_CELL_ITERATOR_STR[] =
+   "Eina Matrixsparse Cell Iterator";
+
+
+#define EINA_MAGIC_CHECK_MATRIXSPARSE(d, ...)           \
+   do {                                                  \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE)) \
+          {                                                \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE);  \
+             return __VA_ARGS__;                           \
+          }                                                \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(d, ...)               \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE_ROW))     \
+          {                                                        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE_ROW);      \
+             return __VA_ARGS__;                                   \
+          }                                                        \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(d, ...)              \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE_CELL))    \
+          {                                                        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE_CELL);     \
+             return __VA_ARGS__;                                   \
+          }                                                        \
+     } while(0)
+
+#define EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(d, ...)                  \
+   do {                                                                  \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_MATRIXSPARSE_ITERATOR))        \
+          {                                                                \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_MATRIXSPARSE_ITERATOR);         \
+             return __VA_ARGS__;                                           \
+          }                                                                \
+     } while(0)
+
+struct _Eina_Matrixsparse_Cell
+{
+   Eina_Matrixsparse_Cell *next;
+   Eina_Matrixsparse_Cell *prev;
+
+   void *data;
+   unsigned long col;
+
+   Eina_Matrixsparse_Row *parent;
+
+   EINA_MAGIC
+};
+
+struct _Eina_Matrixsparse_Row
+{
+   Eina_Matrixsparse_Row *next;
+   Eina_Matrixsparse_Row *prev;
+
+   Eina_Matrixsparse_Cell *cols;
+   Eina_Matrixsparse_Cell *last_col;
+   Eina_Matrixsparse_Cell *last_used; /* fast sequential access */
+   unsigned long row;
+
+   Eina_Matrixsparse *parent;
+
+   EINA_MAGIC
+};
+
+struct _Eina_Matrixsparse
+{
+   Eina_Matrixsparse_Row *rows;
+   Eina_Matrixsparse_Row *last_row;
+   Eina_Matrixsparse_Row *last_used; /* fast sequential access */
+
+   struct
+   {
+      unsigned long rows;
+      unsigned long cols;
+   } size;
+
+   struct
+   {
+      void (*func)(void *user_data, void *cell_data);
+      void *user_data;
+   } free;
+
+   EINA_MAGIC
+};
+
+typedef struct _Eina_Matrixsparse_Iterator Eina_Matrixsparse_Iterator;
+typedef struct _Eina_Matrixsparse_Iterator_Complete
+Eina_Matrixsparse_Iterator_Complete;
+
+struct _Eina_Matrixsparse_Iterator
+{
+   Eina_Iterator iterator;
+
+   const Eina_Matrixsparse *m;
+   struct
+   {
+      const Eina_Matrixsparse_Row *row;
+      const Eina_Matrixsparse_Cell *col;
+   } ref;
+
+   EINA_MAGIC
+};
+
+struct _Eina_Matrixsparse_Iterator_Complete
+{
+   Eina_Iterator iterator;
+
+   const Eina_Matrixsparse *m;
+   struct
+   {
+      const Eina_Matrixsparse_Row *row;
+      const Eina_Matrixsparse_Cell *col;
+   } ref;
+
+   struct
+   {
+      unsigned long row, col;
+   } idx;
+
+   struct
+   {
+      Eina_Matrixsparse_Row row;
+      Eina_Matrixsparse_Cell col;
+   } dummy;
+
+   EINA_MAGIC
+};
+
+/**
+ * @todo Eina_Matrixsparse_Row_Iterator: iterator over rows in matrix
+ * @todo Eina_Matrixsparse_Row_Accessor: accessor over rows in matrix
+ * @todo Eina_Matrixsparse_Cell_Iterator: iterator over cells in row
+ * @todo Eina_Matrixsparse_Cell_Accessor: accessor over cells in row
+ */
+
+static int _eina_matrixsparse_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_matrixsparse_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_matrixsparse_log_dom, __VA_ARGS__)
+
+static Eina_Mempool *_eina_matrixsparse_cell_mp = NULL;
+static Eina_Mempool *_eina_matrixsparse_row_mp = NULL;
+
+static inline void
+_eina_matrixsparse_cell_free(Eina_Matrixsparse_Cell *c, void (*free_func)(
+                                void *,
+                                void *), void *user_data)
+{
+   if (free_func)
+      free_func(user_data, c->data);
+
+   EINA_MAGIC_SET(c, EINA_MAGIC_NONE);
+   eina_mempool_free(_eina_matrixsparse_cell_mp, c);
+}
+
+static inline void
+_eina_matrixsparse_cell_unlink(Eina_Matrixsparse_Cell *c)
+{
+   Eina_Matrixsparse_Row *r = c->parent;
+
+   if (r->last_used == c)
+     {
+        if (c->next)
+           r->last_used = c->next;
+        else
+           r->last_used = c->prev;
+     }
+
+   if (r->last_col == c)
+      r->last_col = c->prev;
+
+   if (r->cols == c)
+      r->cols = c->next;
+
+   if (c->next && c->prev)
+     {
+        c->next->prev = c->prev;
+        c->prev->next = c->next;
+     }
+   else if (c->next)
+      c->next->prev = NULL;
+   else if (c->prev)
+      c->prev->next = NULL;
+}
+
+static inline void
+_eina_matrixsparse_row_cells_free(Eina_Matrixsparse_Row *r, void (*free_func)(
+                                     void *,
+                                     void *), void *user_data)
+{
+   Eina_Matrixsparse_Cell *c = r->cols;
+   while (c)
+     {
+        Eina_Matrixsparse_Cell *c_aux = c;
+        c = c->next;
+        _eina_matrixsparse_cell_free(c_aux, free_func, user_data);
+     }
+}
+
+static inline void
+_eina_matrixsparse_row_free(Eina_Matrixsparse_Row *r, void (*free_func)(void *,
+                                                                        void *),
+                            void *user_data)
+{
+   _eina_matrixsparse_row_cells_free(r, free_func, user_data);
+   EINA_MAGIC_SET(r, EINA_MAGIC_NONE);
+   eina_mempool_free(_eina_matrixsparse_row_mp, r);
+}
+
+static inline void
+_eina_matrixsparse_row_unlink(Eina_Matrixsparse_Row *r)
+{
+   Eina_Matrixsparse *m = r->parent;
+
+   if (m->last_used == r)
+     {
+        if (r->next)
+           m->last_used = r->next;
+        else
+           m->last_used = r->prev;
+     }
+
+   if (m->last_row == r)
+      m->last_row = r->prev;
+
+   if (m->rows == r)
+      m->rows = r->next;
+
+   if (r->next && r->prev)
+     {
+        r->next->prev = r->prev;
+        r->prev->next = r->next;
+     }
+   else if (r->next)
+      r->next->prev = NULL;
+   else if (r->prev)
+      r->prev->next = NULL;
+}
+
+static inline void
+_eina_matrixsparse_row_find_parms_get(const Eina_Matrixsparse *m,
+                                      unsigned long row,
+                                      Eina_Matrixsparse_Row **p_r,
+                                      int *p_dir)
+{
+   Eina_Matrixsparse_Row *r;
+   unsigned long dist;
+   int dir;
+
+   dist = row - m->rows->row;
+   r = m->rows;
+   dir = 1;
+   if (dist > m->last_row->row - row)
+     {
+        dist = m->last_row->row - row;
+        r = m->last_row;
+        dir = -1;
+     }
+
+   if (m->last_used)
+     {
+        if (m->last_used->row < row)
+          {
+             if (dist > row - m->last_used->row)
+               {
+/*      dist = row = m->last_used->row; */
+                  r = m->last_used;
+                  dir = 1;
+               }
+          }
+        else if (dist > m->last_used->row - row)
+          {
+/*      dist = m->last_used->row - row; */
+             r = m->last_used;
+             dir = -1;
+          }
+     }
+
+   *p_r = r;
+   *p_dir = dir;
+}
+
+static inline void
+_eina_matrixsparse_row_cell_find_parms_get(const Eina_Matrixsparse_Row *r,
+                                           unsigned long col,
+                                           Eina_Matrixsparse_Cell **p_c,
+                                           int *p_dir)
+{
+   Eina_Matrixsparse_Cell *c;
+   unsigned long dist;
+   int dir;
+
+   dist = col - r->cols->col;
+   c = r->cols;
+   dir = 1;
+   if (dist > r->last_col->col - col)
+     {
+        dist = r->last_col->col - col;
+        c = r->last_col;
+        dir = -1;
+     }
+
+   if (r->last_used)
+     {
+        if (r->last_used->col < col)
+          {
+             if (dist > col - r->last_used->col)
+               {
+/*      dist = col = r->last_used->col; */
+                  c = r->last_used;
+                  dir = 1;
+               }
+          }
+        else if (dist > r->last_used->col - col)
+          {
+/*      dist = r->last_used->col - col; */
+             c = r->last_used;
+             dir = -1;
+          }
+     }
+
+   *p_c = c;
+   *p_dir = dir;
+}
+
+static inline Eina_Matrixsparse_Row *
+_eina_matrixsparse_row_idx_get(const Eina_Matrixsparse *m, unsigned long row)
+{
+   Eina_Matrixsparse_Row *r;
+   int dir;
+
+   if (!m->rows)
+      return NULL;
+
+   if      (m->rows->row == row)
+      return m->rows;
+   else if (m->rows->row > row)
+      return NULL;
+
+   if      (m->last_row->row == row)
+      return m->last_row;
+   else if (m->last_row->row < row)
+      return NULL;
+
+   if ((m->last_used) && (m->last_used->row == row))
+      return m->last_used;
+
+   _eina_matrixsparse_row_find_parms_get(m, row, &r, &dir);
+   assert(dir != 0);
+   if (dir > 0)
+     {
+        for (; r; r = r->next)
+           if (r->row == row)
+             {
+                ((Eina_Matrixsparse *)m)->last_used = r;
+                return r;
+             }
+           else if (r->row > row)
+              return NULL;
+
+     }
+   else if (dir < 0)
+     {
+        for (; r; r = r->prev)
+          if (r->row == row)
+            {
+               ((Eina_Matrixsparse *)m)->last_used = r;
+               return r;
+            }
+          else if (r->row < row)
+            return NULL;
+     }
+
+   return NULL;
+}
+
+static inline Eina_Matrixsparse_Cell *
+_eina_matrixsparse_row_cell_idx_get(const Eina_Matrixsparse_Row *r,
+                                    unsigned long col)
+{
+   Eina_Matrixsparse_Cell *c;
+   int dir;
+
+   if (!r->cols)
+      return NULL;
+
+   if      (r->cols->col == col)
+      return r->cols;
+   else if (r->cols->col > col)
+      return NULL;
+
+   if      (r->last_col->col == col)
+      return r->last_col;
+   else if (r->last_col->col < col)
+      return NULL;
+
+   if ((r->last_used) && (r->last_used->col == col))
+      return r->last_used;
+
+   _eina_matrixsparse_row_cell_find_parms_get(r, col, &c, &dir);
+   assert(dir != 0);
+   if (dir > 0)
+     {
+        for (; r; c = c->next)
+           if (c->col == col)
+             {
+                ((Eina_Matrixsparse_Row *)r)->last_used = c;
+                return c;
+             }
+           else if (c->col > col)
+              return NULL;
+
+     }
+   else if (dir < 0)
+     {
+        for (; r; c = c->prev)
+          if (c->col == col)
+            {
+               ((Eina_Matrixsparse_Row *)r)->last_used = c;
+               return c;
+            }
+          else if (c->col < col)
+            return NULL;
+     }
+
+   return NULL;
+}
+
+static inline Eina_Matrixsparse_Cell *
+_eina_matrixsparse_cell_idx_get(const Eina_Matrixsparse *m,
+                                unsigned long row,
+                                unsigned long col)
+{
+   Eina_Matrixsparse_Row *r = _eina_matrixsparse_row_idx_get(m, row);
+   if (!r)
+      return NULL;
+
+   return _eina_matrixsparse_row_cell_idx_get(r, col);
+}
+
+static inline void
+_eina_matrixsparse_row_idx_siblings_find(const Eina_Matrixsparse *m,
+                                         unsigned long row,
+                                         Eina_Matrixsparse_Row **p_prev,
+                                         Eina_Matrixsparse_Row **p_next)
+{
+   Eina_Matrixsparse_Row *r;
+   int dir;
+
+   _eina_matrixsparse_row_find_parms_get(m, row, &r, &dir);
+        assert(dir != 0);
+   if (dir > 0)
+     {
+        for (; r; r = r->next)
+           if (r->row > row)
+              break;
+
+        assert(r != NULL);
+        *p_prev = r->prev;
+        *p_next = r;
+     }
+   else if (dir < 0)
+     {
+        for (; r; r = r->prev)
+           if (r->row < row)
+              break;
+
+        assert(r != NULL);
+        *p_prev = r;
+        *p_next = r->next;
+     }
+}
+
+static inline void
+_eina_matrixsparse_row_cell_idx_siblings_find(const Eina_Matrixsparse_Row *r,
+                                              unsigned long col,
+                                              Eina_Matrixsparse_Cell **p_prev,
+                                              Eina_Matrixsparse_Cell **p_next)
+{
+   Eina_Matrixsparse_Cell *c;
+   int dir;
+
+   _eina_matrixsparse_row_cell_find_parms_get(r, col, &c, &dir);
+        assert(dir != 0);
+   if (dir > 0)
+     {
+        for (; c; c = c->next)
+           if (c->col > col)
+              break;
+
+        assert(c != NULL);
+        *p_prev = c->prev;
+        *p_next = c;
+     }
+   else if (dir < 0)
+     {
+        for (; c; c = c->prev)
+           if (c->col < col)
+              break;
+
+        assert(c != NULL);
+        *p_prev = c;
+        *p_next = c->next;
+     }
+}
+
+static inline Eina_Matrixsparse_Row *
+_eina_matrixsparse_row_idx_add(Eina_Matrixsparse *m, unsigned long row)
+{
+   Eina_Matrixsparse_Row *r = eina_mempool_malloc
+         (_eina_matrixsparse_row_mp, sizeof(Eina_Matrixsparse_Row));
+   if (!r)
+      return NULL;
+
+   if (!m->rows)
+     {
+        r->prev = NULL;
+        r->next = NULL;
+        m->rows = r;
+        m->last_row = r;
+     }
+   else if (row < m->rows->row)
+     {
+        r->prev = NULL;
+        r->next = m->rows;
+        m->rows->prev = r;
+        m->rows = r;
+     }
+   else if (row > m->last_row->row)
+     {
+        r->prev = m->last_row;
+        m->last_row->next = r;
+        r->next = NULL;
+        m->last_row = r;
+     }
+   else
+     {
+        Eina_Matrixsparse_Row *prev = NULL, *next = NULL;
+        _eina_matrixsparse_row_idx_siblings_find(m, row, &prev, &next);
+        assert(prev != NULL);
+        assert(next != NULL);
+        r->prev = prev;
+        r->next = next;
+        prev->next = r;
+        next->prev = r;
+     }
+
+   r->cols = NULL;
+   r->last_col = NULL;
+   r->last_used = NULL;
+   r->row = row;
+   r->parent = m;
+   EINA_MAGIC_SET(r, EINA_MAGIC_MATRIXSPARSE_ROW);
+   m->last_used = r;
+   return r;
+}
+
+static inline Eina_Matrixsparse_Cell *
+_eina_matrixsparse_row_cell_idx_add(Eina_Matrixsparse_Row *r,
+                                    unsigned long col,
+                                    const void *data)
+{
+   Eina_Matrixsparse_Cell *c = eina_mempool_malloc
+         (_eina_matrixsparse_cell_mp, sizeof(Eina_Matrixsparse_Cell));
+   if (!c)
+      return NULL;
+
+   if (!r->cols)
+     {
+        c->prev = NULL;
+        c->next = NULL;
+        r->cols = c;
+        r->last_col = c;
+     }
+   else if (col < r->cols->col)
+     {
+        c->prev = NULL;
+        c->next = r->cols;
+        r->cols->prev = c;
+        r->cols = c;
+     }
+   else if (col > r->last_col->col)
+     {
+        c->prev = r->last_col;
+        r->last_col->next = c;
+        c->next = NULL;
+        r->last_col = c;
+     }
+   else
+     {
+        Eina_Matrixsparse_Cell *prev = NULL, *next = NULL;
+        _eina_matrixsparse_row_cell_idx_siblings_find(r, col, &prev, &next);
+        assert(prev != NULL);
+        assert(next != NULL);
+        c->prev = prev;
+        c->next = next;
+        prev->next = c;
+        next->prev = c;
+     }
+
+   c->data = (void *)data;
+   c->col = col;
+   c->parent = r;
+   EINA_MAGIC_SET(c, EINA_MAGIC_MATRIXSPARSE_CELL);
+   r->last_used = c;
+   return c;
+}
+
+static inline Eina_Bool
+_eina_matrixsparse_cell_idx_add(Eina_Matrixsparse *m,
+                                unsigned long row,
+                                unsigned long col,
+                                const void *data)
+{
+   Eina_Matrixsparse_Row *r = _eina_matrixsparse_row_idx_get(m, row);
+   if (!r)
+      r = _eina_matrixsparse_row_idx_add(m, row);
+
+   if (!r)
+      return 0;
+
+   if (_eina_matrixsparse_row_cell_idx_add(r, col, data))
+      return 1;
+
+   if (r->cols)
+      return 0;
+
+   _eina_matrixsparse_row_unlink(r);
+   _eina_matrixsparse_row_free(r, m->free.func, m->free.user_data);
+   return 0;
+}
+
+/*============================================================================*
+*                Iterators                                    *
+*============================================================================*/
+static Eina_Bool
+_eina_matrixsparse_iterator_next(Eina_Matrixsparse_Iterator *it, void **data)
+{
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, EINA_FALSE);
+
+   /* do not touch it->idx */
+
+   if (!it->ref.col)
+      return 0;
+
+   *data = (Eina_Matrixsparse_Cell *)it->ref.col;
+
+   it->ref.col = it->ref.col->next;
+   if (!it->ref.col)
+     {
+        it->ref.row = it->ref.row->next;
+        if (it->ref.row)
+           it->ref.col = it->ref.row->cols;
+     }
+
+   return 1;
+}
+
+static Eina_Matrixsparse *
+_eina_matrixsparse_iterator_get_container(Eina_Matrixsparse_Iterator *it)
+{
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, NULL);
+   return (Eina_Matrixsparse *)it->m;
+}
+
+static void
+_eina_matrixsparse_iterator_free(Eina_Matrixsparse_Iterator *it)
+{
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it);
+   EINA_MAGIC_SET(it,            EINA_MAGIC_NONE);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE);
+   free(it);
+}
+
+static Eina_Bool
+_eina_matrixsparse_iterator_complete_next(
+   Eina_Matrixsparse_Iterator_Complete *it,
+   void **data)
+{
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, EINA_FALSE);
+
+   if (it->idx.row >= it->m->size.rows)
+      return 0;
+
+   if (it->dummy.col.data)
+      ERR("Last iterator call changed dummy cell!");
+
+   if ((it->ref.col) &&
+       (it->ref.col->col == it->idx.col) &&
+       (it->ref.row->row == it->idx.row))
+     {
+        *data = (Eina_Matrixsparse_Cell *)it->ref.col;
+        it->ref.col = it->ref.col->next;
+        if (!it->ref.col)
+          {
+             it->ref.row = it->ref.row->next;
+             if (it->ref.row)
+                it->ref.col = it->ref.row->cols;
+          }
+     }
+   else
+     {
+        it->dummy.col.data = NULL;
+        it->dummy.col.col = it->idx.col;
+        it->dummy.row.row = it->idx.row;
+        *data = &it->dummy.col;
+     }
+
+   it->idx.col++;
+   if (it->idx.col == it->m->size.cols)
+     {
+        it->idx.col = 0;
+        it->idx.row++;
+     }
+
+   return 1;
+}
+
+static Eina_Matrixsparse *
+_eina_matrixsparse_iterator_complete_get_container(
+   Eina_Matrixsparse_Iterator_Complete *it)
+{
+      EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it, NULL);
+   return (Eina_Matrixsparse *)it->m;
+}
+
+static void
+_eina_matrixsparse_iterator_complete_free(
+   Eina_Matrixsparse_Iterator_Complete *it)
+{
+      EINA_MAGIC_CHECK_MATRIXSPARSE_ITERATOR(it);
+
+   if (it->dummy.col.data)
+      ERR("Last iterator call changed dummy cell!");
+
+   EINA_MAGIC_SET(it,            EINA_MAGIC_NONE);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE);
+   free(it);
+}
+
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the matrixsparse module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the matrixsparse module of Eina. It is called by
+ * eina_init().
+ *
+ * This function creates mempool to speed up matrix rows and cells
+ * management, using EINA_MEMPOOL environment variable if it is set to
+ * choose the memory pool type to use.
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_matrixsparse_init(void)
+{
+   const char *choice, *tmp;
+
+   _eina_matrixsparse_log_dom = eina_log_domain_register("eina_matrixsparse",
+                                                         EINA_LOG_COLOR_DEFAULT);
+   if (_eina_matrixsparse_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_matrixsparse");
+        return EINA_FALSE;
+     }
+
+#ifdef EINA_DEFAULT_MEMPOOL
+   choice = "pass_through";
+#else
+   choice = "chained_mempool";
+#endif
+   tmp = getenv("EINA_MEMPOOL");
+   if (tmp && tmp[0])
+      choice = tmp;
+
+   _eina_matrixsparse_cell_mp = eina_mempool_add
+         (choice,
+         "matrixsparse_cell",
+         NULL,
+         sizeof (Eina_Matrixsparse_Cell),
+         120);
+   if (!_eina_matrixsparse_cell_mp)
+     {
+        ERR(
+           "Mempool for matrixsparse_cell cannot be allocated in matrixsparse init.");
+        goto on_init_fail;
+     }
+
+   _eina_matrixsparse_row_mp = eina_mempool_add
+         (choice, "matrixsparse_row", NULL, sizeof (Eina_Matrixsparse_Row), 120);
+   if (!_eina_matrixsparse_row_mp)
+     {
+        ERR(
+           "Mempool for matrixsparse_row cannot be allocated in matrixsparse init.");
+        goto on_init_fail;
+     }
+
+#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
+   EMS(EINA_MAGIC_MATRIXSPARSE);
+   EMS(EINA_MAGIC_MATRIXSPARSE_ROW);
+   EMS(EINA_MAGIC_MATRIXSPARSE_CELL);
+   EMS(EINA_MAGIC_MATRIXSPARSE_ITERATOR);
+   EMS(EINA_MAGIC_MATRIXSPARSE_ROW_ACCESSOR);
+   EMS(EINA_MAGIC_MATRIXSPARSE_ROW_ITERATOR);
+   EMS(EINA_MAGIC_MATRIXSPARSE_CELL_ACCESSOR);
+   EMS(EINA_MAGIC_MATRIXSPARSE_CELL_ITERATOR);
+#undef EMS
+
+   return EINA_TRUE;
+
+on_init_fail:
+   eina_log_domain_unregister(_eina_matrixsparse_log_dom);
+   _eina_matrixsparse_log_dom = -1;
+   return EINA_FALSE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the matrixsparse module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the matrixsparse module set up by
+ * eina_matrixsparse_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_matrixsparse_shutdown(void)
+{
+   eina_mempool_del(_eina_matrixsparse_row_mp);
+   eina_mempool_del(_eina_matrixsparse_cell_mp);
+
+   eina_log_domain_unregister(_eina_matrixsparse_log_dom);
+   _eina_matrixsparse_log_dom = -1;
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Matrixsparse_Group Sparse Matrix
+ *
+ * @brief These functions provide matrix sparse management.
+ *
+ * For more information, you can look at the @ref tutorial_matrixsparse_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new Sparse Matrix.
+ *
+ * @param rows number of rows in matrix. Operations with rows greater than this
+ *        value will fail.
+ * @param cols number of columns in matrix. Operations with columns greater
+ *        than this value will fail.
+ * @param free_func used to delete cell data contents, used by
+ *        eina_matrixsparse_free(), eina_matrixsparse_size_set(),
+ *        eina_matrixsparse_row_idx_clear(),
+ *        eina_matrixsparse_column_idx_clear(),
+ *        eina_matrixsparse_cell_idx_clear() and possible others.
+ * @param user_data given to @a free_func as first parameter.
+ *
+ * @return newly allocated matrix or NULL if allocation failed and eina_error
+ *         is set.
+ */
+EAPI Eina_Matrixsparse *
+eina_matrixsparse_new(unsigned long rows, unsigned long cols, void (*free_func)(
+                         void *user_data,
+                         void *cell_data), const void *user_data)
+{
+   Eina_Matrixsparse *m;
+
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(rows > 0, NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(cols > 0, NULL);
+
+   m = malloc(sizeof(Eina_Matrixsparse));
+   if (!m)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(m, EINA_MAGIC_MATRIXSPARSE);
+   
+   m->rows = NULL;
+   m->last_row = NULL;
+   m->last_used = NULL;
+
+   m->size.rows = rows;
+   m->size.cols = cols;
+   m->free.func = free_func;
+   m->free.user_data = (void *)user_data;
+
+   eina_error_set(0);
+   return m;
+}
+
+/**
+ * @brief Free resources allocated to Sparse Matrix.
+ *
+ * @param m The Sparse Matrix instance to free, must @b not be @c NULL.
+ */
+EAPI void
+eina_matrixsparse_free(Eina_Matrixsparse *m)
+{
+   void (*free_func)(void *, void *);
+   void *user_data;
+
+   Eina_Matrixsparse_Row *r;
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m);
+
+   free_func = m->free.func;
+   user_data = m->free.user_data;
+
+   r = m->rows;
+   while (r)
+     {
+        Eina_Matrixsparse_Row *r_aux = r;
+        r = r->next;
+        _eina_matrixsparse_row_free(r_aux, free_func, user_data);
+     }
+
+   EINA_MAGIC_SET(m, EINA_MAGIC_NONE);
+   free(m);
+}
+
+/**
+ * @brief Get the current size of Sparse Matrix.
+ *
+ * The given parameters are guaranteed to be set if they're not NULL,
+ * even if this function fails (ie: @a m is not a valid matrix instance).
+ *
+ * @param m the sparse matrix to operate on.
+ * @param rows returns the number of rows, may be NULL. If @a m is invalid,
+ *        returned value is zero, otherwise it's a positive integer.
+ * @param cols returns the number of columns, may be NULL. If @a m is
+ *        invalid, returned value is zero, otherwise it's a positive integer.
+ */
+EAPI void
+eina_matrixsparse_size_get(const Eina_Matrixsparse *m,
+                           unsigned long *rows,
+                           unsigned long *cols)
+{
+   if (rows)
+      *rows = 0;
+
+   if (cols)
+      *cols = 0;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m);
+   if (rows)
+      *rows = m->size.rows;
+
+   if (cols)
+      *cols = m->size.cols;
+}
+
+/**
+ * @brief Resize the Sparse Matrix.
+ *
+ * This will resize the sparse matrix, possibly freeing cells on rows
+ * and columns that will cease to exist.
+ *
+ * @param m the sparse matrix to operate on.
+ * @param rows the new number of rows, must be greater than zero.
+ * @param cols the new number of columns, must be greater than zero.
+ * @return 1 on success, 0 on failure.
+ *
+ * @warning cells, rows or columns are not reference counted and thus
+ *     after this call any reference might be invalid if instance were
+ *     freed.
+ */
+EAPI Eina_Bool
+eina_matrixsparse_size_set(Eina_Matrixsparse *m,
+                           unsigned long rows,
+                           unsigned long cols)
+{
+   Eina_Bool update_last_used_row;
+   Eina_Matrixsparse_Row *r;
+   void (*free_func)(void *, void *);
+   void *user_data;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(rows > 0, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(cols > 0, 0);
+
+   if ((rows == m->size.rows) && (cols == m->size.cols))
+      return 1;
+
+   update_last_used_row = ((m->last_used) && (m->last_used->row >= rows));
+   free_func = m->free.func;
+   user_data = m->free.user_data;
+
+   r = m->last_row;
+   while (r && r->row >= rows)
+     {
+        Eina_Matrixsparse_Row *r_aux = r;
+        r = r->prev;
+        _eina_matrixsparse_row_free(r_aux, free_func, user_data);
+     }
+   if (!r)
+     {
+        m->last_row = NULL;
+        m->rows = NULL;
+     }
+   else if (r != m->last_row)
+     {
+        r->next = NULL;
+        m->last_row = r;
+     }
+
+   if (update_last_used_row)
+      m->last_used = m->last_row;
+
+   r = m->rows;
+   while (r)
+     {
+        Eina_Matrixsparse_Cell *c = r->last_col;
+        Eina_Bool update_last_used_col;
+        update_last_used_col = ((r->last_used) && (r->last_used->col >= cols));
+        while (c && c->col >= cols)
+          {
+             Eina_Matrixsparse_Cell *c_aux = c;
+             c = c->prev;
+             _eina_matrixsparse_cell_free(c_aux, free_func, user_data);
+          }
+        if (!c)
+          {
+             Eina_Matrixsparse_Row *r_aux = r;
+             r->cols = NULL;
+             r->last_col = NULL;
+             if (r->next)
+                r->next->prev = r->prev;
+             else
+                m->last_row = r->prev;
+
+             if (r->prev)
+                r->prev->next = r->next;
+             else
+                m->rows = r->next;
+
+             r = r->next;
+             _eina_matrixsparse_row_free(r_aux, free_func, user_data);
+          }
+        else
+          {
+             if (c != r->last_col)
+               {
+                  c->next = NULL;
+                  r->last_col = c;
+               }
+
+             if (update_last_used_col)
+                r->last_used = r->last_col;
+
+             r = r->next;
+          }
+     }
+
+   update_last_used_row = 0;
+   if (m->last_used)
+     {
+        if (m->last_row)
+           update_last_used_row = m->last_used->row > m->last_row->row;
+        else
+           update_last_used_row = 1;
+     }
+
+   if (update_last_used_row)
+      m->last_used = m->last_row;
+
+   m->size.rows = rows;
+   m->size.cols = cols;
+   return 1;
+}
+
+/**
+ * Get the cell reference inside Sparse Matrix.
+ *
+ * @param m the sparse matrix to operate on.
+ * @param row the new number of row to clear.
+ * @param col the new number of column to clear.
+ * @param cell pointer to return cell reference, if any exists.
+ *
+ * @return 1 on success, 0 on failure. It is considered success if did not
+ *     exist but index is inside matrix size, in this case @c *cell == NULL
+ *
+ * @see eina_matrixsparse_cell_data_get()
+ * @see eina_matrixsparse_data_idx_get()
+ */
+EAPI Eina_Bool
+eina_matrixsparse_cell_idx_get(const Eina_Matrixsparse *m,
+                               unsigned long row,
+                               unsigned long col,
+                               Eina_Matrixsparse_Cell **cell)
+{
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cell, 0);
+   *cell = NULL;
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0);
+   *cell = _eina_matrixsparse_cell_idx_get(m, row, col);
+   return 1;
+}
+
+/**
+ * Get data associated with given cell reference.
+ *
+ * @param cell given cell reference, must @b not be @c NULL.
+ *
+ * @return data associated with given cell.
+ *
+ * @see eina_matrixsparse_cell_idx_get()
+ * @see eina_matrixsparse_data_idx_get()
+ */
+EAPI void *
+eina_matrixsparse_cell_data_get(const Eina_Matrixsparse_Cell *cell)
+{
+   EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, NULL);
+   return cell->data;
+}
+
+/**
+ * Get data associated with given cell given its indexes.
+ *
+ * @param m the sparse matrix to operate on.
+ * @param row the new number of row to clear.
+ * @param col the new number of column to clear.
+ *
+ * @return data associated with given cell or NULL if nothing is associated.
+ *
+ * @see eina_matrixsparse_cell_idx_get()
+ * @see eina_matrixsparse_cell_data_get()
+ */
+EAPI void *
+eina_matrixsparse_data_idx_get(const Eina_Matrixsparse *m,
+                               unsigned long row,
+                               unsigned long col)
+{
+   Eina_Matrixsparse_Cell *c;
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, NULL);
+   c = _eina_matrixsparse_cell_idx_get(m, row, col);
+   if (c)
+      return c->data;
+   else
+      return NULL;
+}
+
+/**
+ * Get position (indexes) of the given cell.
+ *
+ * @param cell the cell reference, must @b not be @c NULL.
+ * @param row where to store cell row number, may be @c NULL.
+ * @param col where to store cell column number, may be @c NULL.
+ *
+ * @return 1 on success, 0 otherwise (@c cell is @c NULL).
+ */
+EAPI Eina_Bool
+eina_matrixsparse_cell_position_get(const Eina_Matrixsparse_Cell *cell,
+                                    unsigned long *row,
+                                    unsigned long *col)
+{
+   if (row)
+      *row = 0;
+
+   if (col)
+      *col = 0;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0);
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(cell->parent, 0);
+   if (row)
+      *row = cell->parent->row;
+
+   if (col)
+      *col = cell->col;
+
+   return 1;
+}
+
+/**
+ * Change cell reference value without freeing the possibly existing old value.
+ *
+ * @param cell the cell reference, must @b not be @c NULL.
+ * @param data new data to set.
+ * @param p_old returns the old value intact (not freed).
+ *
+ * @return 1 on success, 0 otherwise (@a cell is @c NULL).
+ *
+ * @see eina_matrixsparse_cell_data_set()
+ * @see eina_matrixsparse_data_idx_replace()
+ */
+EAPI Eina_Bool
+eina_matrixsparse_cell_data_replace(Eina_Matrixsparse_Cell *cell,
+                                    const void *data,
+                                    void **p_old)
+{
+   if (p_old)
+      *p_old = NULL;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0);
+
+   if (p_old)
+      *p_old = cell->data;
+
+   cell->data = (void *)data;
+   return 1;
+}
+
+/**
+ * Change cell value freeing the possibly existing old value.
+ *
+ * In contrast to eina_matrixsparse_cell_data_replace(), this function will
+ * call @c free_func() on existing value.
+ *
+ * @param cell the cell reference, must @b not be @c NULL.
+ * @param data new data to set.
+ *
+ * @return 1 on success, 0 otherwise (@a cell is @c NULL).
+ *
+ * @see eina_matrixsparse_cell_data_replace()
+ * @see eina_matrixsparse_data_idx_set()
+ */
+EAPI Eina_Bool
+eina_matrixsparse_cell_data_set(Eina_Matrixsparse_Cell *cell, const void *data)
+{
+   Eina_Matrixsparse *m;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0);
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(cell->parent, 0);
+   EINA_MAGIC_CHECK_MATRIXSPARSE(cell->parent->parent, 0);
+
+   m = cell->parent->parent;
+
+   if (m->free.func)
+      m->free.func(m->free.user_data, cell->data);
+
+   cell->data = (void *)data;
+   return 1;
+}
+
+/**
+ * Change cell value without freeing the possibly existing old value, using
+ * indexes.
+ *
+ * @param m the sparse matrix, must @b not be @c NULL.
+ * @param row the row number to set the value.
+ * @param col the column number to set the value.
+ * @param data new data to set.
+ * @param p_old returns the old value intact (not freed).
+ *
+ * @return 1 on success, 0 otherwise (@a m is @c NULL, indexes are not valid).
+ *
+ * @see eina_matrixsparse_cell_data_replace()
+ * @see eina_matrixsparse_data_idx_set()
+ */
+EAPI Eina_Bool
+eina_matrixsparse_data_idx_replace(Eina_Matrixsparse *m,
+                                   unsigned long row,
+                                   unsigned long col,
+                                   const void *data,
+                                   void **p_old)
+{
+   Eina_Matrixsparse_Cell *cell;
+
+   if (p_old)
+      *p_old = NULL;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0);
+
+   cell = _eina_matrixsparse_cell_idx_get(m, row, col);
+   if (cell)
+     {
+        if (p_old)
+           *p_old = cell->data;
+
+        cell->data = (void *)data;
+        return 1;
+     }
+
+   return _eina_matrixsparse_cell_idx_add(m, row, col, data);
+}
+
+/**
+ * Change cell value freeing the possibly existing old value, using
+ * indexes.
+ *
+ * In contrast to eina_matrixsparse_data_idx_replace(), this function will
+ * call @c free_func() on existing value.
+ *
+ * @param m the sparse matrix, must @b not be @c NULL.
+ * @param row the row number to set the value.
+ * @param col the column number to set the value.
+ * @param data new data to set.
+ *
+ * @return 1 on success, 0 otherwise (@a m is @c NULL, indexes are not valid).
+ *
+ * @see eina_matrixsparse_cell_data_replace()
+ */
+EAPI Eina_Bool
+eina_matrixsparse_data_idx_set(Eina_Matrixsparse *m,
+                               unsigned long row,
+                               unsigned long col,
+                               const void *data)
+{
+   Eina_Matrixsparse_Cell *cell;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0);
+
+   cell = _eina_matrixsparse_cell_idx_get(m, row, col);
+   if (cell)
+     {
+        if (m->free.func)
+           m->free.func(m->free.user_data, cell->data);
+
+        cell->data = (void *)data;
+        return 1;
+     }
+
+   return _eina_matrixsparse_cell_idx_add(m, row, col, data);
+}
+
+/**
+ * Clear (erase all cells) of row given its index.
+ *
+ * Existing cells will be cleared with @c free_func() given to
+ * eina_matrixsparse_new().
+ *
+ * @param m the sparse matrix to operate on.
+ * @param row the new number of row to clear.
+ *
+ * @return 1 on success, 0 on failure. It is considered success if row
+ *     had no cells filled. Failure is asking for clear row outside
+ *     matrix size.
+ *
+ * @warning cells, rows or columns are not reference counted and thus
+ *     after this call any reference might be invalid if instance were
+ *     freed.
+ */
+EAPI Eina_Bool
+eina_matrixsparse_row_idx_clear(Eina_Matrixsparse *m, unsigned long row)
+{
+   Eina_Matrixsparse_Row *r;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0);
+
+   r = _eina_matrixsparse_row_idx_get(m, row);
+   if (!r)
+      return 1;
+
+   _eina_matrixsparse_row_unlink(r);
+   _eina_matrixsparse_row_free(r, m->free.func, m->free.user_data);
+
+   return 1;
+}
+
+/**
+ * Clear (erase all cells) of column given its index.
+ *
+ * Existing cells will be cleared with @c free_func() given to
+ * eina_matrixsparse_new().
+ *
+ * @param m the sparse matrix to operate on.
+ * @param col the new number of column to clear.
+ *
+ * @return 1 on success, 0 on failure. It is considered success if column
+ *     had no cells filled. Failure is asking for clear column outside
+ *     matrix size.
+ *
+ * @warning cells, rows or columns are not reference counted and thus
+ *     after this call any reference might be invalid if instance were
+ *     freed.
+ */
+EAPI Eina_Bool
+eina_matrixsparse_column_idx_clear(Eina_Matrixsparse *m, unsigned long col)
+{
+   Eina_Matrixsparse_Row *r;
+   void (*free_func)(void *, void *);
+   void *user_data;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0);
+
+   free_func = m->free.func;
+   user_data = m->free.user_data;
+
+   for (r = m->rows; r; )
+     {
+        Eina_Matrixsparse_Row *r_aux = r;
+        Eina_Matrixsparse_Cell *c;
+
+        c = _eina_matrixsparse_row_cell_idx_get(r, col);
+        r = r->next;
+
+        if (!c)
+           continue;
+
+        if ((r_aux->cols != c) || (r_aux->last_col != c))
+          {
+             _eina_matrixsparse_cell_unlink(c);
+             _eina_matrixsparse_cell_free(c, free_func, user_data);
+          }
+        else
+          {
+             _eina_matrixsparse_row_unlink(r_aux);
+             _eina_matrixsparse_row_free(r_aux, free_func, user_data);
+          }
+     }
+
+   return 1;
+}
+
+/**
+ * Clear (erase) cell given its indexes.
+ *
+ * Existing cell will be cleared with @c free_func() given to
+ * eina_matrixsparse_new().
+ *
+ * @param m the sparse matrix to operate on.
+ * @param row the new number of row to clear.
+ * @param col the new number of column to clear.
+ *
+ * @return 1 on success, 0 on failure. It is considered success if did not
+ *     exist but index is inside matrix size.
+ *
+ * @warning cells, rows or columns are not reference counted and thus
+ *     after this call any reference might be invalid if instance were
+ *     freed. Note that this call might delete container column and
+ *     row if this cell was the last remainder.
+ */
+EAPI Eina_Bool
+eina_matrixsparse_cell_idx_clear(Eina_Matrixsparse *m,
+                                 unsigned long row,
+                                 unsigned long col)
+{
+   Eina_Matrixsparse_Cell *c;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE(m, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(row < m->size.rows, 0);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(col < m->size.cols, 0);
+
+   c = _eina_matrixsparse_cell_idx_get(m, row, col);
+   if (!c)
+      return 1;
+
+   _eina_matrixsparse_cell_unlink(c);
+   _eina_matrixsparse_cell_free(c, m->free.func, m->free.user_data);
+
+   return 1;
+}
+
+/**
+ * Clear (erase) cell given its reference.
+ *
+ * @param cell the cell reference, must @b not be @c NULL.
+ *
+ * @return 1 on success, 0 on failure.
+ *
+ * @warning cells, rows or columns are not reference counted and thus
+ *     after this call any reference might be invalid if instance were
+ *     freed. Note that this call might delete container column and
+ *     row if this cell was the last remainder.
+ */
+EAPI Eina_Bool
+eina_matrixsparse_cell_clear(Eina_Matrixsparse_Cell *cell)
+{
+   Eina_Matrixsparse *m;
+
+   EINA_MAGIC_CHECK_MATRIXSPARSE_CELL(cell, 0);
+   EINA_MAGIC_CHECK_MATRIXSPARSE_ROW(cell->parent, 0);
+   EINA_MAGIC_CHECK_MATRIXSPARSE(cell->parent->parent, 0);
+
+   m = cell->parent->parent;
+
+   _eina_matrixsparse_cell_unlink(cell);
+   _eina_matrixsparse_cell_free(cell, m->free.func, m->free.user_data);
+   return 1;
+}
+
+/**
+ * Creates a new iterator over existing matrix cells.
+ *
+ * This is a cheap walk, it will just report existing cells and holes
+ * in the sparse matrix will be ignored. That means the reported
+ * indexes will not be sequential.
+ *
+ * The iterator data will be the cell reference, one may query current
+ * position with eina_matrixsparse_cell_position_get() and cell value
+ * with eina_matrixsparse_cell_data_get().
+ *
+ * @param m The Sparse Matrix reference, must @b not be @c NULL.
+ * @return A new iterator.
+ *
+ * @warning if the matrix structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove cells this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_matrixsparse_iterator_new(const Eina_Matrixsparse *m)
+{
+   Eina_Matrixsparse_Iterator *it;
+
+   it = calloc(1, sizeof(*it));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(it,            EINA_MAGIC_MATRIXSPARSE_ITERATOR);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->m = m;
+   it->ref.row = m->rows;
+   it->ref.col = m->rows ? m->rows->cols : NULL;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_matrixsparse_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_matrixsparse_iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_matrixsparse_iterator_free);
+   return &it->iterator;
+}
+
+/**
+ * Creates a new iterator over all matrix cells.
+ *
+ * Unlike eina_matrixsparse_iterator_new() this one will report all
+ * matrix cells, even those that are still empty (holes). These will
+ * be reported as dummy cells that contains no data.
+ *
+ * Be aware that iterating a big matrix (1000x1000) will call your
+ * function that number of times (1000000 times in that case) even if
+ * your matrix have no elements at all!
+ *
+ * The iterator data will be the cell reference, one may query current
+ * position with eina_matrixsparse_cell_position_get() and cell value
+ * with eina_matrixsparse_cell_data_get(). If cell is empty then the
+ * reference will be a dummy/placeholder, thus setting value with
+ * eina_matrixsparse_cell_data_set() will leave pointer unreferenced.
+ *
+ * @param m The Sparse Matrix reference, must @b not be @c NULL.
+ * @return A new iterator.
+ *
+ * @warning if the matrix structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove cells this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_matrixsparse_iterator_complete_new(const Eina_Matrixsparse *m)
+{
+   Eina_Matrixsparse_Iterator_Complete *it;
+
+   it = calloc(1, sizeof(*it));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(it,            EINA_MAGIC_MATRIXSPARSE_ITERATOR);
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->m = m;
+   it->idx.row = 0;
+   it->idx.col = 0;
+   it->ref.row = m->rows;
+   it->ref.col = m->rows ? m->rows->cols : NULL;
+
+   it->dummy.row.next = it->dummy.row.prev = NULL;
+   it->dummy.row.cols = it->dummy.row.last_col = it->dummy.row.last_used = NULL;
+   it->dummy.row.parent = (Eina_Matrixsparse *)m;
+   EINA_MAGIC_SET(&it->dummy.row, EINA_MAGIC_MATRIXSPARSE_ROW);
+
+   it->dummy.col.next = it->dummy.col.prev = NULL;
+   it->dummy.col.data = NULL;
+   it->dummy.col.parent = &it->dummy.row;
+   EINA_MAGIC_SET(&it->dummy.col, EINA_MAGIC_MATRIXSPARSE_CELL);
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(
+         _eina_matrixsparse_iterator_complete_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_matrixsparse_iterator_complete_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(
+         _eina_matrixsparse_iterator_complete_free);
+   return &it->iterator;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_mempool.c b/tests/suite/ecore/src/lib/eina_mempool.c
new file mode 100644 (file)
index 0000000..b9062be
--- /dev/null
@@ -0,0 +1,399 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_hash.h"
+#include "eina_module.h"
+#include "eina_log.h"
+#include "eina_main.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_mempool.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static Eina_Hash *_backends;
+static Eina_Array *_modules;
+static int _eina_mempool_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_mempool_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_mempool_log_dom, __VA_ARGS__)
+
+
+static Eina_Mempool *
+_new_va(const char *name,
+        const char *context,
+        const char *options,
+        va_list args)
+{
+   Eina_Mempool_Backend *be;
+   Eina_Mempool *mp;
+
+   Eina_Error err = EINA_ERROR_NOT_MEMPOOL_MODULE;
+
+   eina_error_set(0);
+   be = eina_hash_find(_backends, name);
+   if (!be)
+      goto on_error;
+
+   err = EINA_ERROR_OUT_OF_MEMORY;
+   mp = calloc(1, sizeof(Eina_Mempool));
+   if (!mp)
+      goto on_error;
+
+   /* FIXME why backend is not a pointer? */
+   mp->backend = *be;
+   mp->backend_data = mp->backend.init(context, options, args);
+
+   return mp;
+
+on_error:
+   eina_error_set(err);
+   return NULL;
+}
+
+/* Built-in backend's prototypes */
+#ifdef EINA_STATIC_BUILD_CHAINED_POOL
+Eina_Bool chained_init(void);
+void      chained_shutdown(void);
+#endif
+
+#ifdef EINA_STATIC_BUILD_PASS_THROUGH
+Eina_Bool pass_through_init(void);
+void      pass_through_shutdown(void);
+#endif
+
+#ifdef EINA_STATIC_BUILD_EMEMOA_UNKNOWN
+Eina_Bool ememoa_unknown_init(void);
+void      ememoa_unknown_shutdown(void);
+#endif
+
+#ifdef EINA_STATIC_BUILD_EMEMOA_FIXED
+Eina_Bool ememoa_fixed_init(void);
+void      ememoa_fixed_shutdown(void);
+#endif
+
+#ifdef EINA_STATIC_BUILD_FIXED_BITMAP
+Eina_Bool fixed_bitmap_init(void);
+void      fixed_bitmap_shutdown(void);
+#endif
+
+#ifdef EINA_STATIC_BUILD_BUDDY
+Eina_Bool buddy_init(void);
+void      buddy_shutdown(void);
+#endif
+
+#ifdef EINA_STATIC_BUILD_ONE_BIG
+Eina_Bool one_big_init(void);
+void      one_big_shutdown(void);
+#endif
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+EAPI Eina_Error EINA_ERROR_NOT_MEMPOOL_MODULE = 0;
+
+static const char EINA_ERROR_NOT_MEMPOOL_MODULE_STR[] =
+   "Not a memory pool module.";
+
+/**
+ * @endcond
+ */
+
+EAPI Eina_Bool
+eina_mempool_register(Eina_Mempool_Backend *be)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(be, 0);
+   DBG("be=%p, name=%p", be, be->name);
+   return eina_hash_add(_backends, be->name, be);
+}
+
+EAPI void
+eina_mempool_unregister(Eina_Mempool_Backend *be)
+{
+   EINA_SAFETY_ON_NULL_RETURN(be);
+   DBG("be=%p, name=%p", be, be->name);
+   eina_hash_del(_backends, be->name, be);
+}
+
+Eina_Bool
+eina_mempool_init(void)
+{
+   char *path;
+
+   _eina_mempool_log_dom = eina_log_domain_register("eina_mempool",
+                                                    EINA_LOG_COLOR_DEFAULT);
+   if (_eina_mempool_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_mempool");
+        return 0;
+     }
+
+   EINA_ERROR_NOT_MEMPOOL_MODULE = eina_error_msg_static_register(
+         EINA_ERROR_NOT_MEMPOOL_MODULE_STR);
+   _backends = eina_hash_string_superfast_new(NULL);
+
+   /* dynamic backends */
+   _modules = eina_module_arch_list_get(NULL,
+                                        PACKAGE_LIB_DIR "/eina/modules/mp",
+                                        MODULE_ARCH);
+
+   path = eina_module_environment_path_get("HOME", "/.eina/mp/modules/mp");
+   _modules = eina_module_arch_list_get(_modules, path, MODULE_ARCH);
+   if (path)
+      free(path);
+
+   path = eina_module_environment_path_get("EINA_MODULES_MEMPOOL_DIR",
+                                           "/eina/modules/mp");
+   _modules = eina_module_arch_list_get(_modules, path, MODULE_ARCH);
+   if (path)
+      free(path);
+
+   path = eina_module_symbol_path_get((const void *)eina_init,
+                                      "/eina/modules/mp");
+   _modules = eina_module_arch_list_get(_modules, path, MODULE_ARCH);
+   if (path)
+      free(path);
+
+   if (!_modules)
+     {
+        ERR("no mempool modules able to be loaded.");
+        eina_hash_free(_backends);
+        goto mempool_init_error;
+     }
+
+   eina_module_list_load(_modules);
+
+   /* builtin backends */
+#ifdef EINA_STATIC_BUILD_CHAINED_POOL
+   chained_init();
+#endif
+#ifdef EINA_STATIC_BUILD_PASS_THROUGH
+   pass_through_init();
+#endif
+#ifdef EINA_STATIC_BUILD_EMEMOA_UNKNOWN
+   ememoa_unknown_init();
+#endif
+#ifdef EINA_STATIC_BUILD_EMEMOA_FIXED
+   ememoa_fixed_init();
+#endif
+#ifdef EINA_STATIC_BUILD_FIXED_BITMAP
+   fixed_bitmap_init();
+#endif
+#ifdef EINA_STATIC_BUILD_BUDDY
+   buddy_init();
+#endif
+#ifdef EINA_STATIC_BUILD_ONE_BIG
+   one_big_init();
+#endif
+
+   return EINA_TRUE;
+
+mempool_init_error:
+   eina_log_domain_unregister(_eina_mempool_log_dom);
+   _eina_mempool_log_dom = -1;
+
+   return EINA_FALSE;
+}
+
+Eina_Bool
+eina_mempool_shutdown(void)
+{
+   /* builtin backends */
+#ifdef EINA_STATIC_BUILD_CHAINED_POOL
+   chained_shutdown();
+#endif
+#ifdef EINA_STATIC_BUILD_PASS_THROUGH
+   pass_through_shutdown();
+#endif
+#ifdef EINA_STATIC_BUILD_EMEMOA_UNKNOWN
+   ememoa_unknown_shutdown();
+#endif
+#ifdef EINA_STATIC_BUILD_EMEMOA_FIXED
+   ememoa_fixed_shutdown();
+#endif
+#ifdef EINA_STATIC_BUILD_FIXED_BITMAP
+   fixed_bitmap_shutdown();
+#endif
+#ifdef EINA_STATIC_BUILD_BUDDY
+   buddy_shutdown();
+#endif
+#ifdef EINA_STATIC_BUILD_ONE_BIG
+   one_big_shutdown();
+#endif
+   /* dynamic backends */
+   eina_module_list_free(_modules);
+   if (_modules)
+      eina_array_free(_modules);
+
+   if (_backends)
+      eina_hash_free(_backends);
+
+   eina_log_domain_unregister(_eina_mempool_log_dom);
+   _eina_mempool_log_dom = -1;
+
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Memory_Pool_Group Memory Pool
+ *
+ * @brief These functions provide memory pool management.
+ *
+ * Several mempool are available:
+ *
+ * @li @c buddy: It uses the
+ * <a href="http://en.wikipedia.org/wiki/Buddy_memory_allocation">"buddy
+ * allocator" algorithm</a> but the Eina implementation differs in the
+ * sense that the chunk information is not stored on the chunk itself,
+ * but on another memory area. This is useful for cases where the
+ * memory to manage might be slower to access, or limited (like video
+ * memory).
+ * @li @c chained_pool: It is the default one. It allocates a big
+ * chunk of memory with malloc() and split the result in chunks of the
+ * requested size that are pushed inside a stack. When requested, it
+ * takes this pointer from the stack to give them to whoever wants
+ * them.
+ * @li @c ememoa_fixed and @c ememoa_unknown: experimental allocators
+ * which could be useful when a fixed amount of memory is needed.
+ * @li @c fixed_bitmap: It allocates with malloc) 32* the requested
+ * size and push the pool pointer in an rbtree. To find empty space in
+ * a pool, it will just search for the first bit set in an int (32
+ * bits). Then, when a pointer is freed, it will do a search inside
+ * the rbtree.
+ * @li @c pass_through: it just call malloc() and free(). It may be
+ * faster on some computers than using our own allocators (like having
+ * a huge L2 cache, over 4MB).
+ * @li @c one_big: It call just one time malloc for the requested number
+ * of items. Usefull when you know in advance how many object of some
+ * type will live during the life of the mempool.
+ *
+ * @{
+ */
+
+EAPI Eina_Mempool *
+eina_mempool_add(const char *name,
+                 const char *context,
+                 const char *options,
+                 ...)
+{
+   Eina_Mempool *mp;
+   va_list args;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
+   DBG("name=%s, context=%s, options=%s",
+       name, context ? context : "", options ? options : "");
+
+   va_start(args, options);
+   mp = _new_va(name, context, options, args);
+   va_end(args);
+
+   DBG("name=%s, context=%s, options=%s, mp=%p",
+       name, context ? context : "", options ? options : "", mp);
+
+   return mp;
+}
+
+EAPI void eina_mempool_del(Eina_Mempool *mp)
+{
+   EINA_SAFETY_ON_NULL_RETURN(mp);
+   EINA_SAFETY_ON_NULL_RETURN(mp->backend.shutdown);
+   DBG("mp=%p", mp);
+   mp->backend.shutdown(mp->backend_data);
+   free(mp);
+}
+
+EAPI void eina_mempool_gc(Eina_Mempool *mp)
+{
+   EINA_SAFETY_ON_NULL_RETURN(mp);
+   EINA_SAFETY_ON_NULL_RETURN(mp->backend.garbage_collect);
+   DBG("mp=%p", mp);
+   mp->backend.garbage_collect(mp->backend_data);
+}
+
+EAPI void eina_mempool_statistics(Eina_Mempool *mp)
+{
+   EINA_SAFETY_ON_NULL_RETURN(mp);
+   EINA_SAFETY_ON_NULL_RETURN(mp->backend.statistics);
+   DBG("mp=%p", mp);
+   mp->backend.statistics(mp->backend_data);
+}
+
+EAPI unsigned int
+eina_mempool_alignof(unsigned int size)
+{
+   int align;
+
+   if (size <= 2)
+      align = 2;
+   else if (size < 8)
+      align = 4;
+   else
+#if __WORDSIZE == 32
+      align = 8;
+
+#else
+   if (size < 16)
+      align = 8;
+   else
+      align = 16;
+#endif
+
+   return ((size / align) + 1) * align;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_module.c b/tests/suite/ecore/src/lib/eina_module.c
new file mode 100644 (file)
index 0000000..7ae4bdb
--- /dev/null
@@ -0,0 +1,762 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Jorge Luis Zapata Muga, Cedric BAIL
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_DLADDR
+# define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+#ifndef _MSC_VER
+# include <libgen.h>
+#else
+# include <Evil.h>
+#endif
+
+#include <dlfcn.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_error.h"
+#include "eina_file.h"
+#include "eina_log.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_module.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static int EINA_MODULE_LOG_DOM = -1;
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(EINA_MODULE_LOG_DOM, __VA_ARGS__)
+
+#ifdef WRN
+#undef WRN
+#endif
+#define WRN(...) EINA_LOG_DOM_WARN(EINA_MODULE_LOG_DOM, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(EINA_MODULE_LOG_DOM, __VA_ARGS__)
+
+#define EINA_MODULE_SYMBOL_INIT "__eina_module_init"
+#define EINA_MODULE_SYMBOL_SHUTDOWN "__eina_module_shutdown"
+
+struct _Eina_Module
+{
+   void *handle;
+   int ref;
+   const char file[];
+};
+
+typedef struct _Dir_List_Get_Cb_Data
+{
+   Eina_Module_Cb cb;
+   void *data;
+   Eina_Array *array;
+} Dir_List_Get_Cb_Data;
+
+typedef struct _Dir_List_Cb_Data
+{
+   Eina_Module_Cb cb;
+   void *data;
+} Dir_List_Cb_Data;
+
+static Eina_Bool _dir_list_get_cb(Eina_Module *m, void *data)
+{
+   Dir_List_Get_Cb_Data *cb_data = data;
+   Eina_Bool ret = EINA_TRUE;
+
+   if (cb_data->cb)
+      ret = cb_data->cb(m, cb_data->data);
+
+   if (ret)
+      eina_array_push(cb_data->array, m);
+
+   return ret;
+}
+
+static void _dir_list_cb(const char *name, const char *path, void *data)
+{
+   Dir_List_Cb_Data *cb_data = data;
+   size_t length;
+
+   length = strlen(name);
+   if (length < sizeof(SHARED_LIB_SUFFIX)) /* x.so */
+      return;
+
+   if (!strcmp(name + length - sizeof(SHARED_LIB_SUFFIX) + 1,
+               SHARED_LIB_SUFFIX))
+     {
+        char *file;
+        Eina_Module *m;
+
+        length = strlen(path) + strlen(name) + 2;
+
+        file = alloca(sizeof (char) * length);
+        if (!file)
+           return;
+
+        snprintf(file, length, "%s/%s", path, name);
+        m = eina_module_new(file);
+        if (!m)
+          {
+             return; /* call the user provided cb on this module */
+
+          }
+
+        if (!cb_data->cb(m, cb_data->data))
+           eina_module_free(m);
+     }
+}
+
+static void _dir_arch_list_cb(const char *name, const char *path, void *data)
+{
+   Dir_List_Get_Cb_Data *cb_data = data;
+   Eina_Module *m;
+   char *file = NULL;
+   size_t length;
+
+   length = strlen(path) + 1 + strlen(name) + 1 +
+      strlen((char *)(cb_data->data)) + 1 + sizeof("module") +
+      sizeof(SHARED_LIB_SUFFIX) + 1;
+
+   file = alloca(length);
+   snprintf(file, length, "%s/%s/%s/module" SHARED_LIB_SUFFIX,
+            path, name, (char *)(cb_data->data));
+   m = eina_module_new(file);
+   if (!m)
+      return;
+
+   eina_array_push(cb_data->array, m);
+}
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+static const char EINA_ERROR_WRONG_MODULE_STR[] =
+   "Wrong file format or no file module found";
+static const char EINA_ERROR_MODULE_INIT_FAILED_STR[] =
+   "Module initialisation function failed";
+
+EAPI Eina_Error EINA_ERROR_WRONG_MODULE = 0;
+EAPI Eina_Error EINA_ERROR_MODULE_INIT_FAILED = 0;
+
+/**
+ * @endcond
+ */
+
+/**
+ * @internal
+ * @brief Initialize the module loader module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the module loader module of Eina. It is
+ * called by eina_init().
+ *
+ * This function sets up the module module of Eina. It also registers
+ * the errors #EINA_ERROR_WRONG_MODULE and
+ * #EINA_ERROR_MODULE_INIT_FAILED.
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_module_init(void)
+{
+   EINA_MODULE_LOG_DOM = eina_log_domain_register
+         ("eina_module", EINA_LOG_COLOR_DEFAULT);
+   if (EINA_MODULE_LOG_DOM < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_module");
+        return EINA_FALSE;
+     }
+
+#define EEMR(n) n = eina_error_msg_static_register(n ## _STR)
+   EEMR(EINA_ERROR_WRONG_MODULE);
+   EEMR(EINA_ERROR_MODULE_INIT_FAILED);
+#undef EEMR
+
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the module loader module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the module loader module set up by
+ * eina_module_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_module_shutdown(void)
+{
+   /* TODO should we store every module when "new" is called and
+    * delete the list of modules here
+    */
+
+   eina_log_domain_unregister(EINA_MODULE_LOG_DOM);
+   EINA_MODULE_LOG_DOM = -1;
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Module_Group Module
+ *
+ * @brief These functions provide module management.
+ *
+ * @{
+ */
+
+/**
+ * @brief Return a new module.
+ *
+ * @param file The name of the file module to load.
+ *
+ * This function returns a new module. If @p file is @c NULL, the
+ * function returns @c NULL, otherwise, it allocates an Eina_Module,
+ * stores a duplicate string of @p file, sets its reference to @c 0
+ * and its handle to @c NULL.
+ *
+ * When the new module is not needed anymore, use eina_module_free()
+ * to free the allocated memory.
+ *
+ * @see eina_module_load
+ */
+EAPI Eina_Module *eina_module_new(const char *file)
+{
+   Eina_Module *m;
+   size_t len;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
+   /* TODO check that the file exists. Update doc too */
+
+   len = strlen(file);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(len > 0, NULL);
+
+   m = malloc(sizeof(Eina_Module) + len + 1);
+   if (!m)
+     {
+        ERR("could not malloc(%lu)",
+            (unsigned long)(sizeof(Eina_Module) + len + 1));
+        return NULL;
+     }
+
+   memcpy((char *)m->file, file, len + 1);
+   m->ref = 0;
+   m->handle = NULL;
+   DBG("m=%p, file=%s", m, file);
+
+   return m;
+}
+
+/**
+ * @brief Delete a module.
+ *
+ * @param m The module to delete.
+ * @return EINA_TRUE on success, EINA_FALSE otherwise.
+ *
+ * This function calls eina_module_unload() if @p m has been previously
+ * loaded and frees the allocated memory. On success this function
+ * returns EINA_TRUE and EINA_FALSE otherwise. If @p m is @c NULL, the
+ * function returns immediately.
+ */
+EAPI Eina_Bool eina_module_free(Eina_Module *m)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE);
+
+   DBG("m=%p, handle=%p, file=%s, refs=%d", m, m->handle, m->file, m->ref);
+
+   if (m->handle)
+      if (eina_module_unload(m) == EINA_FALSE)
+         return EINA_FALSE;
+
+   free(m);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Load a module.
+ *
+ * @param m The module to load.
+ * @return EINA_TRUE on success, EINA_FALSE otherwise.
+ *
+ * This function load the shared file object passed in
+ * eina_module_new(). If it is a internal Eina module (like the
+ * mempools), it also initialize it. It the shared file object can not
+ * be loaded, the error #EINA_ERROR_WRONG_MODULE is set and
+ * #EINA_FALSE is returned. If it is a internal Eina module and the
+ * module can not be initialized, the error
+ * #EINA_ERROR_MODULE_INIT_FAILED is set and #EINA_FALSE is
+ * returned. If the module has already been loaded, it's refeence
+ * counter is increased by one and #EINA_TRUE is returned. If @p m is
+ * @c NULL, the function returns immediately #EINA_FALSE.
+ *
+ * When the symbols of the shared file objetcts are not needed
+ * anymore, call eina_module_unload() to unload the module.
+ */
+EAPI Eina_Bool eina_module_load(Eina_Module *m)
+{
+   void *dl_handle;
+   Eina_Module_Init *initcall;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE);
+
+   DBG("m=%p, handle=%p, file=%s, refs=%d", m, m->handle, m->file, m->ref);
+
+   if (m->handle)
+      goto loaded;
+
+   dl_handle = dlopen(m->file, RTLD_NOW);
+   if (!dl_handle)
+     {
+        WRN("could not dlopen(\"%s\", RTLD_NOW): %s", m->file, dlerror());
+        eina_error_set(EINA_ERROR_WRONG_MODULE);
+        return EINA_FALSE;
+     }
+
+   initcall = dlsym(dl_handle, EINA_MODULE_SYMBOL_INIT);
+   if ((!initcall) || (!(*initcall)))
+      goto ok;
+
+   if ((*initcall)() == EINA_TRUE)
+      goto ok;
+
+   WRN("could not find eina's entry symbol %s inside module %s",
+       EINA_MODULE_SYMBOL_INIT, m->file);
+   eina_error_set(EINA_ERROR_MODULE_INIT_FAILED);
+   dlclose(dl_handle);
+   return EINA_FALSE;
+ok:
+   DBG("successfully loaded %s", m->file);
+   m->handle = dl_handle;
+loaded:
+   m->ref++;
+   DBG("ref %d", m->ref);
+
+   eina_error_set(0);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Unload a module.
+ *
+ * @param m The module to load.
+ * @return EINA_TRUE on success, EINA_FALSE otherwise.
+ *
+ * This function unload the module @p m that has been previously
+ * loaded by eina_module_load(). If the reference counter of @p m is
+ * strictly greater than @c 1, #EINA_FALSE is returned. Otherwise, the
+ * shared object file is closed and if it is a internal Eina module, it
+ * is shutted down just before. In that case, #EINA_TRUE is
+ * returned. In all case, the reference counter is decreased. If @p m
+ * is @c NULL, the function returns immediately #EINA_FALSE.
+ */
+EAPI Eina_Bool eina_module_unload(Eina_Module *m)
+{
+   Eina_Module_Shutdown *shut;
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE);
+
+   DBG("m=%p, handle=%p, file=%s, refs=%d", m, m->handle, m->file, m->ref);
+
+   m->ref--;
+   if (!m->ref)
+     {
+        shut = dlsym(m->handle, EINA_MODULE_SYMBOL_SHUTDOWN);
+        if ((shut) && (*shut))
+           (*shut)();
+
+        dlclose(m->handle);
+        m->handle = NULL;
+        DBG("unloaded module %s", m->file);
+        return EINA_TRUE;
+     }
+
+   return EINA_FALSE;
+}
+
+/**
+ * @brief Retrive the data associated to a symbol.
+ *
+ * @param m The module.
+ * @param symbol The symbol.
+ * @return The data associated to the symbol, or @c NULL on failure.
+ *
+ * This function returns the data associated to @p symbol of @p m. @p
+ * m must have been loaded before with eina_module_load(). If @p m
+ * is @c NULL, or if it has not been correctly loaded before, the
+ * function returns immediately @c NULL.
+ */
+EAPI void *eina_module_symbol_get(const Eina_Module *m, const char *symbol)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m,         NULL);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m->handle, NULL);
+   return dlsym(m->handle, symbol);
+}
+
+/**
+ * @brief Return the file name associated to the module.
+ *
+ * @param m The module.
+ * @return The file name.
+ *
+ * This function returns the file name passed in eina_module_new(). If
+ * @p m is @c NULL, the function returns immediately @c NULL. The
+ * returned value must no be freed.
+ */
+EAPI const char *eina_module_file_get(const Eina_Module *m)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL);
+   return m->file;
+}
+
+/**
+ * @brief Return the path built from the location of a library and a
+ * given sub directory.
+ *
+ * @param symbol The symbol to search for.
+ * @param sub_dir The subdirectory to append.
+ * @return The built path on success, @c NULL otherwise.
+ *
+ * This function returns the path built by concatenating the path of
+ * the library containing the symbol @p symbol and @p sub_dir. @p sub_dir
+ * can be @c NULL. The returned path must be freed when not used
+ * anymore. If the symbol is not found, or dl_addr() is not supported,
+ * or allocation fails, this function returns @c NULL.
+ */
+EAPI char *eina_module_symbol_path_get(const void *symbol, const char *sub_dir)
+{
+#ifdef HAVE_DLADDR
+   Dl_info eina_dl;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(symbol, NULL);
+
+   if (dladdr(symbol, &eina_dl))
+     {
+        char *pos = strrchr(eina_dl.dli_fname, '/');
+        if (pos)
+          {
+             char *path;
+             int l0;
+             int l1;
+             int l2 = 0;
+
+             l0 = strlen(eina_dl.dli_fname);
+             l1 = strlen(pos);
+             if (sub_dir && (*sub_dir != '\0'))
+                l2 = strlen(sub_dir);
+
+             path = malloc(l0 - l1 + l2 + 1);
+             if (path)
+               {
+                  memcpy(path, eina_dl.dli_fname, l0 - l1);
+                  if (sub_dir && (*sub_dir != '\0'))
+                     memcpy(path + l0 - l1, sub_dir, l2);
+
+                  path[l0 - l1 + l2] = '\0';
+                  return path;
+               }
+          }
+     }
+
+#endif /* ! HAVE_DLADDR */
+
+   return NULL;
+}
+
+/**
+ * @brief Return the path built from the value of an environment varialbe and a
+ * given sub directory.
+ *
+ * @param env The environment variable to expand.
+ * @param sub_dir The subdirectory to append.
+ * @return The built path on success, @c NULL otherwise.
+ *
+ * This function returns the path built by concatenating the value of
+ * the environment variable named @p env and @p sub_dir. @p sub_dir
+ * can be @c NULL. The returned path must be freed when not used
+ * anymore. If the symbol is not found, or @p env does not exist, or
+ * allocation fails, this function returns @c NULL.
+ */
+EAPI char *eina_module_environment_path_get(const char *env,
+                                            const char *sub_dir)
+{
+   const char *env_dir;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(env, NULL);
+
+   env_dir = getenv(env);
+   if (env_dir)
+     {
+        char *path;
+        size_t l1;
+        size_t l2 = 0;
+
+        l1 = strlen(env_dir);
+        if (sub_dir && (*sub_dir != '\0'))
+           l2 = strlen(sub_dir);
+
+        path = (char *)malloc(l1 + l2 + 1);
+        if (path)
+          {
+                memcpy(path,      env_dir, l1);
+             if (sub_dir && (*sub_dir != '\0'))
+                memcpy(path + l1, sub_dir, l2);
+
+             path[l1 + l2] = '\0';
+
+             return path;
+          }
+     }
+
+   return NULL;
+}
+
+/**
+ * @brief Get an array of modules found on the directory path matching an arch type.
+ *
+ * @param array The array that stores the list of the modules.
+ * @param path The directory's path to search for modules.
+ * @param arch The architecture string.
+ *
+ * This function adds to @p array the module names found in @p path
+ * which match the cpu architecture @p arch. If @p path or @p arch is
+ * @c NULL, the function returns immediately @p array. @p array can be
+ * @c NULL. In that case, it is created with 4 elements.
+ */
+EAPI Eina_Array *eina_module_arch_list_get(Eina_Array *array,
+                                           const char *path,
+                                           const char *arch)
+{
+   Dir_List_Get_Cb_Data list_get_cb_data;
+
+   if ((!path) || (!arch))
+      return array;
+
+   list_get_cb_data.array = array ? array : eina_array_new(4);
+   list_get_cb_data.cb = NULL;
+   list_get_cb_data.data = (void *)arch;
+
+   eina_file_dir_list(path, 0, &_dir_arch_list_cb, &list_get_cb_data);
+
+   return list_get_cb_data.array;
+}
+
+/**
+ * @brief Get a list of modules found on the directory path.
+ *
+ * @param array The array that stores the list of the modules.
+ * @param path The directory's path to search for modules.
+ * @param recursive Iterate recursively on the path.
+ * @param cb Callback function to call on each module.
+ * @param data Data passed to the callback function.
+ *
+ * This function adds to @p array the list of modules found in
+ * @p path. If @p recursive is #EINA_TRUE, then recursive search is
+ * done. The callback @p cb is called on each module found, and @p data
+ * is passed to @p cb. If @p path is @c NULL, the function returns
+ * immediately @p array. If the returned value of @p cb is 0, the
+ * module will not be added to the list, otherwise it will be added.
+ * @p array can be @c NULL. In that case, it is created with 4
+ * elements. @p cb can be @c NULL.
+ */
+EAPI Eina_Array *eina_module_list_get(Eina_Array *array,
+                                      const char *path,
+                                      Eina_Bool recursive,
+                                      Eina_Module_Cb cb,
+                                      void *data)
+{
+   Dir_List_Get_Cb_Data list_get_cb_data;
+   Dir_List_Cb_Data list_cb_data;
+
+   if (!path)
+      return array;
+
+   list_get_cb_data.array = array ? array : eina_array_new(4);
+   list_get_cb_data.cb = cb;
+   list_get_cb_data.data = data;
+
+   list_cb_data.cb = &_dir_list_get_cb;
+   list_cb_data.data = &list_get_cb_data;
+
+   eina_file_dir_list(path, recursive, &_dir_list_cb, &list_cb_data);
+
+   return list_get_cb_data.array;
+}
+
+/**
+ * @brief Find an module in array.
+ *
+ * @param array The array to find the module.
+ * @param module The name of module to be searched.
+ *
+ * This function finds an @p module in @p array.
+ * If the element is found  the function returns the module, else
+ * @c NULL is returned.
+ */
+EAPI Eina_Module *
+eina_module_find(const Eina_Array *array, const char *module)
+{
+   unsigned int i;
+   Eina_Array_Iterator iterator;
+   Eina_Module *m;
+
+   EINA_ARRAY_ITER_NEXT(array, i, m, iterator)
+     {
+        char *file_m;
+        char *tmp;
+        ssize_t len;
+
+        /* basename() can modify its argument, so we first get a copie */
+        /* do not use strdupa, as opensolaris does not have it */
+        len = strlen(eina_module_file_get(m));
+        tmp = alloca(len + 1);
+        memcpy(tmp, eina_module_file_get(m), len + 1);
+        file_m = basename(tmp);
+        len = strlen(file_m);
+        len -= sizeof(SHARED_LIB_SUFFIX) - 1;
+        if (len <= 0)
+           continue;
+
+        if (!strncmp(module, file_m, len))
+          return m;;
+     }
+
+   return NULL;
+}
+
+/**
+ * @brief Load every module on the list of modules.
+ *
+ * @param array The array of modules to load.
+ *
+ * This function calls eina_module_load() on each element found in
+ * @p array. If @p array is @c NULL, this function does nothing.
+ */
+EAPI void eina_module_list_load(Eina_Array *array)
+{
+   Eina_Array_Iterator iterator;
+   Eina_Module *m;
+   unsigned int i;
+
+   EINA_SAFETY_ON_NULL_RETURN(array);
+   DBG("array %p, count %u", array, array->count);
+   EINA_ARRAY_ITER_NEXT(array, i, m, iterator)
+     eina_module_load(m);
+}
+
+/**
+ * @brief Unload every module on the list of modules.
+ *
+ * @param array The array of modules to unload.
+ *
+ * This function calls eina_module_unload() on each element found in
+ * @p array. If @p array is @c NULL, this function does nothing.
+ */
+EAPI void eina_module_list_unload(Eina_Array *array)
+{
+   Eina_Array_Iterator iterator;
+   Eina_Module *m;
+   unsigned int i;
+
+   EINA_SAFETY_ON_NULL_RETURN(array);
+   DBG("array %p, count %u", array, array->count);
+   EINA_ARRAY_ITER_NEXT(array, i, m, iterator)
+     eina_module_unload(m);
+}
+
+/**
+ * @p Free every module on the list of modules.
+ *
+ * @param array The array of modules to free.
+ *
+ * This function calls eina_module_free() on each element found in
+ * @p array. If @p array is @c NULL, this function does nothing.
+ */
+EAPI void eina_module_list_free(Eina_Array *array)
+{
+   Eina_Array_Iterator iterator;
+   Eina_Module *m;
+   unsigned int i;
+
+   EINA_SAFETY_ON_NULL_RETURN(array);
+   DBG("array %p, count %u", array, array->count);
+   EINA_ARRAY_ITER_NEXT(array, i, m, iterator)
+     eina_module_free(m);
+
+   eina_array_flush(array);
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_private.h b/tests/suite/ecore/src/lib/eina_private.h
new file mode 100644 (file)
index 0000000..2f8ff81
--- /dev/null
@@ -0,0 +1,135 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Carsten Haitzler, Vincent Torri, Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_PRIVATE_H_
+#define EINA_PRIVATE_H_
+
+#include <stdarg.h>
+
+#if HAVE___ATTRIBUTE__
+# define __UNUSED__ __attribute__((unused))
+#else
+# define __UNUSED__
+#endif
+
+#include "eina_magic.h"
+#include "eina_iterator.h"
+#include "eina_accessor.h"
+
+#ifndef MIN
+# define MIN(x, y) (((x) > (y)) ? (y) : (x))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef ABS
+# define ABS(x) ((x) < 0 ? -(x) : (x))
+#endif
+
+#ifndef CLAMP
+# define CLAMP(x, min, \
+               max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x)))
+#endif
+
+#define READBUFSIZ 65536
+
+#define EINA_LOG_COLOR_DEFAULT "\033[36m"
+
+/* eina magic types */
+#define EINA_MAGIC_SHARE 0x98761234
+#define EINA_MAGIC_SHARE_HEAD 0x98761235
+#define EINA_MAGIC_STRINGSHARE_NODE 0x98761254
+#define EINA_MAGIC_USTRINGSHARE_NODE 0x98761255
+#define EINA_MAGIC_BINSHARE_NODE 0x98761256
+
+#define EINA_MAGIC_LIST 0x98761237
+#define EINA_MAGIC_LIST_ITERATOR 0x98761238
+#define EINA_MAGIC_LIST_ACCESSOR 0x98761239
+#define EINA_MAGIC_LIST_ACCOUNTING 0x9876123a
+
+#define EINA_MAGIC_ARRAY 0x9876123b
+#define EINA_MAGIC_ARRAY_ITERATOR 0x9876123c
+#define EINA_MAGIC_ARRAY_ACCESSOR 0x9876123d
+
+#define EINA_MAGIC_HASH 0x9876123e
+#define EINA_MAGIC_HASH_ITERATOR 0x9876123f
+
+#define EINA_MAGIC_TILER 0x98761240
+#define EINA_MAGIC_TILER_ITERATOR 0x98761241
+
+#define EINA_MAGIC_MATRIXSPARSE 0x98761242
+#define EINA_MAGIC_MATRIXSPARSE_ROW 0x98761243
+#define EINA_MAGIC_MATRIXSPARSE_CELL 0x98761244
+#define EINA_MAGIC_MATRIXSPARSE_ITERATOR 0x98761245
+#define EINA_MAGIC_MATRIXSPARSE_ROW_ITERATOR 0x98761246
+#define EINA_MAGIC_MATRIXSPARSE_ROW_ACCESSOR 0x98761247
+#define EINA_MAGIC_MATRIXSPARSE_CELL_ITERATOR 0x98761248
+#define EINA_MAGIC_MATRIXSPARSE_CELL_ACCESSOR 0x98761249
+
+#define EINA_MAGIC_STRBUF 0x98761250
+#define EINA_MAGIC_USTRBUF 0x98761257
+
+#define EINA_MAGIC_QUADTREE 0x98761251
+#define EINA_MAGIC_QUADTREE_ROOT 0x98761252
+#define EINA_MAGIC_QUADTREE_ITEM 0x98761253
+
+/* undef the following, we want out version */
+#undef FREE
+#define FREE(ptr)                              \
+  do {                                         \
+     free(ptr);                                        \
+     ptr = NULL;                               \
+  } while(0);
+
+#undef IF_FREE
+#define IF_FREE(ptr)                           \
+  do {                                         \
+     if (ptr) {                                        \
+       free(ptr);                              \
+       ptr = NULL;                             \
+     }                                         \
+  } while(0);
+
+#undef IF_FN_DEL
+#define IF_FN_DEL(_fn, ptr)                    \
+  do {                                         \
+     if (ptr) {                                        \
+       _fn(ptr);                               \
+       ptr = NULL;                             \
+     }                                         \
+  } while(0);
+
+#define MAGIC_FREE(ptr)                                \
+  do {                                         \
+     if (ptr) {                                        \
+       EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE);   \
+       FREE(ptr);                              \
+     }                                         \
+  } while(0);
+
+#ifdef EFL_HAVE_THREADS
+void eina_share_common_threads_init(void);
+void eina_share_common_threads_shutdown(void);
+void eina_log_threads_init(void);
+void eina_log_threads_shutdown(void);
+#endif
+
+#endif /* EINA_PRIVATE_H_ */
+
diff --git a/tests/suite/ecore/src/lib/eina_quadtree.c b/tests/suite/ecore/src/lib/eina_quadtree.c
new file mode 100644 (file)
index 0000000..251bb96
--- /dev/null
@@ -0,0 +1,916 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page tutorial_quadtree_page QuadTree Tutorial
+ *
+ * to be written...
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "eina_quadtree.h"
+#include "eina_magic.h"
+#include "eina_mempool.h"
+#include "eina_list.h"
+#include "eina_inlist.h"
+#include "eina_trash.h"
+#include "eina_log.h"
+#include "eina_rectangle.h"
+
+#include "eina_private.h"
+
+typedef struct _Eina_QuadTree_Root Eina_QuadTree_Root;
+
+static const char EINA_MAGIC_QUADTREE_STR[] = "Eina QuadTree";
+static const char EINA_MAGIC_QUADTREE_ROOT_STR[] = "Eina QuadTree Root";
+static const char EINA_MAGIC_QUADTREE_ITEM_STR[] = "Eina QuadTree Item";
+
+#define EINA_MAGIC_CHECK_QUADTREE(d, ...)               \
+   do {                                                  \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE))     \
+          {                                                \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE);      \
+             return __VA_ARGS__;                           \
+          }                                                \
+     } while(0);
+
+#define EINA_MAGIC_CHECK_QUADTREE_ROOT(d, ...)                  \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ROOT))        \
+          {                                                        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ROOT);         \
+             return __VA_ARGS__;                                   \
+          }                                                        \
+     } while(0);
+
+#define EINA_MAGIC_CHECK_QUADTREE_ITEM(d, ...)                  \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_QUADTREE_ITEM))        \
+          {                                                        \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_QUADTREE_ITEM);         \
+             return __VA_ARGS__;                                   \
+          }                                                        \
+     } while(0);
+
+struct _Eina_QuadTree
+{
+   EINA_MAGIC;
+
+   Eina_QuadTree_Root *root;
+
+   Eina_List *hidden;
+
+   size_t root_count;
+   size_t items_count;
+
+   Eina_Trash *items_trash;
+   Eina_Trash *root_trash;
+
+   Eina_Inlist *change;
+   Eina_Inlist *cached;
+   Eina_Rectangle target;
+
+   size_t index;
+
+   struct
+   {
+      Eina_Quad_Callback v;
+      Eina_Quad_Callback h;
+   } func;
+
+   struct
+   {
+      size_t w;
+      size_t h;
+   } geom;
+
+   Eina_Bool resize : 1;
+   Eina_Bool lost : 1;
+};
+
+struct _Eina_QuadTree_Root
+{
+   EINA_MAGIC;
+
+   Eina_QuadTree_Root *parent;
+   Eina_QuadTree_Root *left;
+   Eina_QuadTree_Root *right;
+
+   Eina_List *both;
+
+   Eina_Bool sorted : 1;
+};
+
+struct _Eina_QuadTree_Item
+{
+   EINA_MAGIC;
+   EINA_INLIST;
+
+   Eina_QuadTree *quad;
+   Eina_QuadTree_Root *root;
+
+   const void *object;
+
+   size_t index;
+
+   Eina_Bool change : 1;
+   Eina_Bool delete_me : 1;
+   Eina_Bool visible : 1;
+   Eina_Bool hidden : 1;
+};
+
+static int _eina_log_qd_dom = -1;
+static Eina_Mempool *root_mp = NULL;
+static Eina_Mempool *items_mp = NULL;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_log_qd_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_log_qd_dom, __VA_ARGS__)
+
+
+static int
+_eina_quadtree_item_cmp(const void *a, const void *b)
+{
+   const Eina_QuadTree_Item *i = a;
+   const Eina_QuadTree_Item *j = b;
+
+   return i->index - j->index;
+}
+
+static Eina_QuadTree_Root *
+eina_quadtree_root_free(Eina_QuadTree *q, Eina_QuadTree_Root *root)
+{
+   Eina_QuadTree_Item *item;
+
+   if (!root)
+      return NULL;
+
+   EINA_MAGIC_CHECK_QUADTREE_ROOT(root, NULL);
+
+   EINA_LIST_FREE(root->both, item)
+   eina_mempool_free(items_mp, item);
+
+   root->left = eina_quadtree_root_free(q, root->left);
+   root->right = eina_quadtree_root_free(q, root->right);
+
+   EINA_MAGIC_SET(root, 0);
+   eina_mempool_free(root_mp, root);
+
+   return NULL;
+}
+
+static Eina_QuadTree_Root *
+eina_quadtree_root_rebuild_pre(Eina_QuadTree *q,
+                               Eina_Inlist **change,
+                               Eina_QuadTree_Root *root)
+{
+   Eina_QuadTree_Item *item;
+
+   if (!root)
+      return NULL;
+
+   EINA_LIST_FREE(root->both, item)
+   {
+      if (item->visible)
+         *change = eina_inlist_append(*change, EINA_INLIST_GET(item));
+      else if (!item->hidden)
+        {
+           q->hidden = eina_list_append(q->hidden, item);
+           item->hidden = EINA_TRUE;
+           item->root = NULL;
+        }
+   }
+
+   root->left = eina_quadtree_root_rebuild_pre(q, change, root->left);
+   root->right = eina_quadtree_root_rebuild_pre(q, change, root->right);
+
+   EINA_MAGIC_SET(root, 0);
+   if (q->root_count > 50)
+      eina_mempool_free(root_mp, root);
+   else
+     {
+        eina_trash_push(&q->root_trash, root);
+        q->root_count++;
+     }
+
+   return NULL;
+}
+
+static size_t
+_eina_quadtree_split(Eina_Inlist *objects,
+                     Eina_QuadTree_Root *root,
+                     Eina_Inlist **left,
+                     Eina_Inlist **right,
+                     Eina_Quad_Callback func,
+                     int border,
+                     int middle)
+{
+   Eina_QuadTree_Item *object;
+
+   middle /= 2;
+
+   if (middle <= 4)
+      while (objects)
+        {
+           object = EINA_INLIST_CONTAINER_GET(objects, Eina_QuadTree_Item);
+           objects = objects->next;
+
+           object->change = EINA_FALSE;
+           if (!object->visible)
+             {
+                if (!object->hidden)
+                  {
+                     object->hidden = EINA_TRUE;
+                     object->quad->hidden = eina_list_append(
+                           object->quad->hidden,
+                           object);
+                  }
+
+                continue;
+             }
+
+           if (object->hidden)
+             {
+                object->hidden = EINA_FALSE;
+                object->quad->hidden = eina_list_remove(object->quad->hidden,
+                                                        object);
+             }
+
+           if (!object->delete_me)
+             {
+                if (root->sorted)
+                   root->both = eina_list_sorted_insert(root->both,
+                                                        _eina_quadtree_item_cmp,
+                                                        object);
+                else
+                   root->both = eina_list_append(root->both, object);
+
+                object->root = root;
+             }
+           else
+              eina_quadtree_del(object);
+        }
+   else
+      while (objects)
+        {
+           object = EINA_INLIST_CONTAINER_GET(objects, Eina_QuadTree_Item);
+           objects = objects->next;
+
+           object->change = EINA_FALSE;
+           if (!object->visible)
+             {
+                if (!object->hidden)
+                  {
+                     object->hidden = EINA_TRUE;
+                     object->quad->hidden = eina_list_append(
+                           object->quad->hidden,
+                           object);
+                  }
+
+                continue;
+             }
+
+           if (object->hidden)
+             {
+                object->hidden = EINA_FALSE;
+                object->quad->hidden = eina_list_remove(object->quad->hidden,
+                                                        object);
+             }
+
+           if (!object->delete_me)
+             {
+                switch (func(object->object, border + middle))
+                  {
+                   case EINA_QUAD_LEFT:
+                      *left = eina_inlist_append(*left, EINA_INLIST_GET(object));
+                      break;
+
+                   case EINA_QUAD_RIGHT:
+                      *right =
+                         eina_inlist_append(*right, EINA_INLIST_GET(object));
+                      break;
+
+                   case EINA_QUAD_BOTH:
+                      root->both = eina_list_append(root->both, object);
+                      object->root = root;
+                      break;
+
+                   default:
+                      abort();
+                  }
+             }
+           else
+              eina_quadtree_del(object);
+        }
+
+   return middle;
+}
+
+
+static Eina_QuadTree_Root *
+_eina_quadtree_update(Eina_QuadTree *q, Eina_QuadTree_Root *parent,
+                      Eina_QuadTree_Root *root, Eina_Inlist *objects,
+                      Eina_Bool direction, Eina_Rectangle *size)
+{
+   Eina_Inlist *right = NULL;
+   Eina_Inlist *left = NULL;
+   size_t w2;
+   size_t h2;
+
+   if (!objects)
+      return root;
+
+   if (!root)
+     {
+        root = eina_trash_pop(&q->root_trash);
+        if (!root)
+           root = eina_mempool_malloc(root_mp, sizeof (Eina_QuadTree_Root));
+        else
+           q->root_count--;
+
+        if (!root)
+           /* FIXME: NOT GOOD TIMING, WE ARE GOING TO LEAK MORE MEMORY */
+           return NULL;
+
+        root->parent = parent;
+        root->both = NULL;
+        root->left = NULL;
+        root->right = NULL;
+        root->sorted = EINA_TRUE;
+
+        EINA_MAGIC_SET(root, EINA_MAGIC_QUADTREE_ROOT);
+     }
+
+   w2 = 0;
+   h2 = 0;
+
+   if (direction)
+      w2 = _eina_quadtree_split(objects, root,
+                                &left, &right,
+                                q->func.h, size->x, size->w);
+   else
+      h2 = _eina_quadtree_split(objects, root,
+                                &left, &right,
+                                q->func.v, size->y, size->h);
+
+   size->w -= w2; size->h -= h2;
+   root->left = _eina_quadtree_update(q, root,
+                                      root->left, left,
+                                      !direction, size);
+   size->x += w2; size->y += h2;
+   root->right = _eina_quadtree_update(q, root,
+                                       root->right, right,
+                                       !direction, size);
+   size->x -= w2; size->y -= h2;
+   size->w += w2; size->h += h2;
+
+   return root;
+}
+
+static Eina_Inlist *
+_eina_quadtree_merge(Eina_Inlist *result,
+                     Eina_List *both)
+{
+   Eina_QuadTree_Item *item;
+   Eina_QuadTree_Item *b;
+   Eina_Inlist *moving;
+
+   if (!both)
+      return result;
+
+   if (!result)
+     {
+        Eina_List *l;
+
+        EINA_LIST_FOREACH(both, l, item)
+        if (item->visible)
+           result = eina_inlist_append(result, EINA_INLIST_GET(item));
+
+        return result;
+     }
+
+   moving = result;
+
+   item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item);
+   b = eina_list_data_get(both);
+
+   while (both && moving)
+     {
+        if (!b->visible)
+          {
+             both = eina_list_next(both);
+             b = eina_list_data_get(both);
+             continue;
+          }
+
+        if (_eina_quadtree_item_cmp(item, b) < 0)
+          {
+             /* moving is still lower than item, so we can continue to the next one. */
+             moving = moving->next;
+             item = EINA_INLIST_CONTAINER_GET(moving, Eina_QuadTree_Item);
+          }
+        else
+          {
+             /* we just get above the limit of both, so insert it */
+             result = eina_inlist_prepend_relative(result,
+                                                   EINA_INLIST_GET(b),
+                                                   moving);
+             both = eina_list_next(both);
+             b = eina_list_data_get(both);
+          }
+     }
+
+   item = EINA_INLIST_CONTAINER_GET(result->last, Eina_QuadTree_Item);
+
+   while (both)
+     {
+        b = eina_list_data_get(both);
+        if (b->visible)
+          {
+             if (_eina_quadtree_item_cmp(item, b) < 0)
+                break;
+
+             result = eina_inlist_prepend_relative(result,
+                                                   EINA_INLIST_GET(b),
+                                                   result->last);
+          }
+
+        both = eina_list_next(both);
+     }
+
+   while (both)
+     {
+        b = eina_list_data_get(both);
+        if (b->visible)
+           result = eina_inlist_append(result, EINA_INLIST_GET(b));
+
+        both = eina_list_next(both);
+     }
+
+   return result;
+}
+
+static Eina_Inlist *
+_eina_quadtree_collide(Eina_Inlist *result,
+                       Eina_QuadTree_Root *root,
+                       Eina_Bool direction, Eina_Rectangle *size,
+                       Eina_Rectangle *target)
+{
+   if (!root)
+      return result;
+
+   if (!root->sorted)
+     {
+        root->both = eina_list_sort(root->both, -1, _eina_quadtree_item_cmp);
+        root->sorted = EINA_TRUE;
+     }
+
+   result = _eina_quadtree_merge(result, root->both);
+   DBG("%p: %i in both for (%i, %i - %i, %i)",
+       root, eina_list_count(root->both),
+       size->x, size->y, size->w, size->h);
+
+   if (direction)
+     {
+        int middle = size->w / 2;
+
+        size->w -= middle;
+        if (eina_spans_intersect(size->x, size->w, target->x, target->w))
+           result = _eina_quadtree_collide(result, root->left,
+                                           !direction, size,
+                                           target);
+
+        size->x += middle;
+        if (eina_spans_intersect(size->x, size->w, target->x, target->w))
+           result = _eina_quadtree_collide(result, root->right,
+                                           !direction, size,
+                                           target);
+
+        size->x -= middle;
+        size->w += middle;
+     }
+   else
+     {
+        int middle = size->h / 2;
+
+        size->h -= middle;
+        if (eina_spans_intersect(size->y, size->h, target->y, target->h))
+           result = _eina_quadtree_collide(result, root->left,
+                                           !direction, size,
+                                           target);
+
+        size->y += middle;
+        if (eina_spans_intersect(size->y, size->h, target->y, target->h))
+           result = _eina_quadtree_collide(result, root->right,
+                                           !direction, size,
+                                           target);
+
+        size->y -= middle;
+        size->h += middle;
+     }
+
+   return result;
+}
+
+static void
+_eina_quadtree_remove(Eina_QuadTree_Item *object)
+{
+   if (!object->root)
+      return;
+
+   object->root->both = eina_list_remove(object->root->both, object);
+   if (object->root->both)
+      goto end;
+
+   if (object->root->left)
+      goto end;
+
+   if (object->root->right)
+      goto end;
+
+   /* The root is not useful anymore... */
+   if (object->root->parent)
+     {
+        if (object->root->parent->left == object->root)
+           object->root->parent->left = NULL;
+        else
+           object->root->parent->right = NULL;
+
+        object->root->parent = NULL;
+     }
+   else
+      object->quad->root = NULL;
+
+   if (object->quad->root_count > 50)
+      eina_mempool_free(root_mp, object->root);
+   else
+     {
+        eina_trash_push(&object->quad->root_trash, object->root);
+        object->quad->root_count++;
+     }
+
+end:
+   object->root = NULL;
+}
+
+EAPI Eina_QuadTree *
+eina_quadtree_new(size_t w, size_t h,
+                  Eina_Quad_Callback vertical, Eina_Quad_Callback horizontal)
+{
+   Eina_QuadTree *result;
+
+   if (!vertical || !horizontal || h == 0 || w == 0)
+      return NULL;
+
+   result = calloc(1, sizeof (Eina_QuadTree));
+   if (!result)
+      return NULL;
+
+   result->func.v = vertical;
+   result->func.h = horizontal;
+
+   result->geom.w = w;
+   result->geom.h = h;
+
+   result->change = NULL;
+
+   result->lost = EINA_TRUE;
+
+   EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE);
+
+   return result;
+}
+
+EAPI void
+eina_quadtree_free(Eina_QuadTree *q)
+{
+   Eina_QuadTree_Item *item;
+
+   if (!q)
+      return;
+
+   EINA_MAGIC_CHECK_QUADTREE(q);
+
+   while (q->change)
+     {
+        item = EINA_INLIST_CONTAINER_GET(q->change, Eina_QuadTree_Item);
+        q->change = q->change->next;
+        if (!item->hidden)
+           eina_mempool_free(items_mp, item);
+     }
+
+   EINA_LIST_FREE(q->hidden, item)
+   eina_mempool_free(items_mp, item);
+
+   eina_quadtree_root_free(q, q->root);
+
+   while (q->items_trash)
+     {
+        item = eina_trash_pop(&q->items_trash);
+        eina_mempool_free(items_mp, item);
+     }
+
+   while (q->root_trash)
+     {
+        Eina_QuadTree_Root *root;
+
+        root = eina_trash_pop(&q->root_trash);
+        eina_mempool_free(root_mp, root);
+     }
+
+        EINA_MAGIC_SET(q, 0);
+   free(q);
+}
+
+EAPI Eina_QuadTree_Item *
+eina_quadtree_add(Eina_QuadTree *q, const void *object)
+{
+   Eina_QuadTree_Item *result;
+
+   EINA_MAGIC_CHECK_QUADTREE(q, NULL);
+
+   if (!object)
+      return NULL;
+
+   result = eina_trash_pop(&q->items_trash);
+   if (!result)
+      result = eina_mempool_malloc(items_mp, sizeof (Eina_QuadTree_Item));
+   else
+      q->items_count--;
+
+   if (!result)
+      return NULL;
+
+   result->quad = q;
+   result->root = NULL;
+   result->object = object;
+
+   result->index = q->index++;
+
+   result->change = EINA_TRUE;
+   result->delete_me = EINA_FALSE;
+   result->visible = EINA_TRUE;
+   result->hidden = EINA_FALSE;
+
+   EINA_MAGIC_SET(result, EINA_MAGIC_QUADTREE_ITEM);
+
+   /* Insertion is delayed until we really need to use it */
+   q->change = eina_inlist_append(q->change, EINA_INLIST_GET(result));
+
+   return result;
+}
+
+EAPI Eina_Bool
+eina_quadtree_del(Eina_QuadTree_Item *object)
+{
+   if (!object)
+      return EINA_FALSE;
+
+   EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+   _eina_quadtree_remove(object);
+
+   if (object->change)
+     {
+        /* This object is still in the update array, delaying it's removal !*/
+        object->delete_me = EINA_TRUE;
+        object->visible = EINA_TRUE;
+        return EINA_TRUE;
+     }
+
+   if (object->hidden)
+     {
+        object->quad->hidden = eina_list_remove(object->quad->hidden, object);
+        object->hidden = EINA_TRUE;
+     }
+
+   /* This object is not anymore inside the tree, we can remove it now !*/
+   EINA_MAGIC_SET(object, 0);
+   if (object->quad->items_count > 256)
+      eina_mempool_free(items_mp, object);
+   else
+     {
+        object->quad->items_count++;
+        eina_trash_push(&object->quad->items_trash, object);
+     }
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_quadtree_change(Eina_QuadTree_Item *object)
+{
+   EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+   if (object->delete_me || !object->visible)
+      return EINA_FALSE;
+
+   if (object->quad->resize)
+      return EINA_TRUE;
+
+   /* Delaying change until needed */
+   if (!object->change)
+      object->quad->change = eina_inlist_append(object->quad->change,
+                                                EINA_INLIST_GET(object));
+
+   object->change = EINA_TRUE;
+
+   _eina_quadtree_remove(object);
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_quadtree_hide(Eina_QuadTree_Item *object)
+{
+   EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+   object->visible = EINA_FALSE;
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_quadtree_show(Eina_QuadTree_Item *object)
+{
+   EINA_MAGIC_CHECK_QUADTREE_ITEM(object, EINA_FALSE);
+
+   object->quad->lost = EINA_TRUE;
+
+   if (object->visible)
+      return EINA_TRUE;
+
+   object->visible = EINA_TRUE;
+   if (!object->change)
+      return eina_quadtree_change(object);
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Inlist *
+eina_quadtree_collide(Eina_QuadTree *q, int x, int y, int w, int h)
+{
+   Eina_Rectangle canvas;
+
+   EINA_MAGIC_CHECK_QUADTREE(q, NULL);
+
+   /* Now we need the tree to be up to date, so it's time */
+   if (q->resize) /* Full rebuild needed ! */
+     {
+        DBG("resizing quadtree");
+        q->root = eina_quadtree_root_rebuild_pre(q, &q->change, q->root);
+        q->resize = EINA_FALSE;
+     }
+
+   EINA_RECTANGLE_SET(&canvas, 0, 0, q->geom.w, q->geom.h);
+
+   if (q->change)
+     {
+        DBG("updating quadtree content");
+        q->root = _eina_quadtree_update(q, NULL, q->root, q->change,
+                                        EINA_FALSE, &canvas);
+        q->change = NULL;
+        q->lost = EINA_TRUE;
+     }
+
+   if (q->target.x != x
+       || q->target.y != y
+       || q->target.w != w
+       || q->target.h != h)
+     {
+        DBG("new target");
+        EINA_RECTANGLE_SET(&q->target, x, y, w, h);
+        q->lost = EINA_TRUE;
+     }
+
+   if (q->lost)
+     {
+        DBG("computing collide");
+        q->cached = _eina_quadtree_collide(NULL, q->root,
+                                           EINA_FALSE, &canvas,
+                                           &q->target);
+        q->lost = EINA_FALSE;
+     }
+
+   return q->cached;
+}
+
+EAPI void *
+eina_quadtree_object(Eina_Inlist *item)
+{
+   Eina_QuadTree_Item *qi;
+
+   if (!item)
+      return NULL;
+
+   qi = EINA_INLIST_CONTAINER_GET(item, Eina_QuadTree_Item);
+   if (!qi)
+      return NULL;
+
+   EINA_MAGIC_CHECK_QUADTREE_ITEM(qi, NULL);
+
+   if (!qi->visible)
+      return NULL;
+
+   return (void *)qi->object;
+}
+
+EAPI void
+eina_quadtree_resize(Eina_QuadTree *q, size_t w, size_t h)
+{
+   EINA_MAGIC_CHECK_QUADTREE(q);
+
+   if (q->geom.w == w
+       && q->geom.h == h)
+      return;
+
+   q->resize = EINA_TRUE;
+   q->geom.w = w;
+   q->geom.h = h;
+}
+
+EAPI void
+eina_quadtree_cycle(Eina_QuadTree *q)
+{
+   EINA_MAGIC_CHECK_QUADTREE(q);
+
+   q->index = 0;
+}
+
+EAPI void
+eina_quadtree_increase(Eina_QuadTree_Item *object)
+{
+   size_t tmp;
+
+   tmp = object->quad->index++;
+   if (object->index == tmp)
+      return;
+
+   object->index = tmp;
+   if (object->root)
+      object->root->sorted = EINA_FALSE;
+}
+
+Eina_Bool
+eina_quadtree_init(void)
+{
+   _eina_log_qd_dom = eina_log_domain_register("eina_quadtree",
+                                               EINA_LOG_COLOR_DEFAULT);
+   if (_eina_log_qd_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_quadtree");
+        return EINA_FALSE;
+     }
+
+#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
+   EMS(EINA_MAGIC_QUADTREE);
+   EMS(EINA_MAGIC_QUADTREE_ROOT);
+   EMS(EINA_MAGIC_QUADTREE_ITEM);
+#undef EMS
+
+   items_mp = eina_mempool_add("chained_mempool", "QuadTree Item", NULL,
+                               sizeof (Eina_QuadTree_Item), 320);
+   root_mp = eina_mempool_add("chained_mempool", "QuadTree Root", NULL,
+                              sizeof (Eina_QuadTree_Root), 32);
+
+   return EINA_TRUE;
+}
+
+Eina_Bool
+eina_quadtree_shutdown(void)
+{
+   eina_log_domain_unregister(_eina_log_qd_dom);
+   _eina_log_qd_dom = -1;
+   return EINA_TRUE;
+}
+
+
+
diff --git a/tests/suite/ecore/src/lib/eina_rbtree.c b/tests/suite/ecore/src/lib/eina_rbtree.c
new file mode 100644 (file)
index 0000000..1f03308
--- /dev/null
@@ -0,0 +1,592 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_array.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_rbtree.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+#define EINA_RBTREE_ITERATOR_PREFIX_MASK  0x1
+#define EINA_RBTREE_ITERATOR_INFIX_MASK   0x2
+#define EINA_RBTREE_ITERATOR_POSTFIX_MASK 0x4
+
+typedef struct _Eina_Iterator_Rbtree Eina_Iterator_Rbtree;
+typedef struct _Eina_Iterator_Rbtree_List Eina_Iterator_Rbtree_List;
+
+struct _Eina_Iterator_Rbtree
+{
+   Eina_Iterator iterator;
+
+   Eina_Array *stack;
+
+   unsigned char mask;
+};
+
+struct _Eina_Iterator_Rbtree_List
+{
+   Eina_Rbtree *tree;
+
+   Eina_Rbtree_Direction dir : 1;
+   Eina_Bool up : 1;
+};
+
+static Eina_Iterator_Rbtree_List *
+_eina_rbtree_iterator_list_new(const Eina_Rbtree *tree)
+{
+   Eina_Iterator_Rbtree_List *new;
+
+        eina_error_set(0);
+   new = malloc(sizeof (Eina_Iterator_Rbtree_List));
+   if (!new)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   new->tree = (Eina_Rbtree *)tree;
+   new->dir = EINA_RBTREE_RIGHT;
+   new->up = EINA_FALSE;
+
+   return new;
+}
+
+static Eina_Rbtree *
+_eina_rbtree_iterator_get_content(Eina_Iterator_Rbtree *it)
+{
+   if (eina_array_count_get(it->stack) <= 0)
+      return NULL;
+
+   return eina_array_data_get(it->stack, 0);
+}
+
+static void
+_eina_rbtree_iterator_free(Eina_Iterator_Rbtree *it)
+{
+   Eina_Iterator_Rbtree_List *item;
+   Eina_Array_Iterator et;
+   unsigned int i;
+
+   EINA_ARRAY_ITER_NEXT(it->stack, i, item, et)
+     free(item);
+
+   eina_array_free(it->stack);
+                     free(it);
+}
+
+static Eina_Bool
+_eina_rbtree_iterator_next(Eina_Iterator_Rbtree *it, void **data)
+{
+   Eina_Iterator_Rbtree_List *last;
+   Eina_Iterator_Rbtree_List *new;
+   Eina_Rbtree *tree;
+
+   if (eina_array_count_get(it->stack) <= 0)
+      return EINA_FALSE;
+
+   last = eina_array_data_get(it->stack, eina_array_count_get(it->stack) - 1);
+   tree = last->tree;
+
+   if (!last->tree || last->up == EINA_TRUE)
+     {
+        last = eina_array_pop(it->stack);
+        while (last->dir == EINA_RBTREE_LEFT
+               || !last->tree)
+          {
+             if (tree)
+                if ((it->mask & EINA_RBTREE_ITERATOR_POSTFIX_MASK) ==
+                    EINA_RBTREE_ITERATOR_POSTFIX_MASK)
+                  {
+                     free(last);
+
+                     if (eina_array_count_get(it->stack) > 0)
+                       {
+                          last = eina_array_data_get(it->stack,
+                                                     eina_array_count_get(
+                                                        it->
+                                                        stack)
+                                                     - 1);
+                          last->up = EINA_TRUE;
+                       }
+
+                     goto onfix;
+                  }
+
+             free(last);
+
+             last = eina_array_pop(it->stack);
+             if (!last)
+                return EINA_FALSE;
+
+             tree = last->tree;
+          }
+
+        last->dir = EINA_RBTREE_LEFT;
+        last->up = EINA_FALSE;
+
+        eina_array_push(it->stack, last);
+
+        if ((it->mask & EINA_RBTREE_ITERATOR_INFIX_MASK) ==
+            EINA_RBTREE_ITERATOR_INFIX_MASK)
+           goto onfix;
+     }
+
+   new = _eina_rbtree_iterator_list_new(last->tree->son[last->dir]);
+   if (!new)
+      return EINA_FALSE;
+
+        eina_array_push(it->stack, new);
+
+   if (last->dir == EINA_RBTREE_RIGHT)
+      if ((it->mask & EINA_RBTREE_ITERATOR_PREFIX_MASK) ==
+          EINA_RBTREE_ITERATOR_PREFIX_MASK)
+         goto onfix;
+
+   return _eina_rbtree_iterator_next(it, data);
+
+onfix:
+   *data = tree;
+   return EINA_TRUE;
+}
+
+static Eina_Iterator *
+_eina_rbtree_iterator_build(const Eina_Rbtree *root, unsigned char mask)
+{
+   Eina_Iterator_Rbtree_List *first;
+   Eina_Iterator_Rbtree *it;
+
+   eina_error_set(0);
+   it = calloc(1, sizeof (Eina_Iterator_Rbtree));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   it->stack = eina_array_new(8);
+   if (!it->stack)
+      goto on_error2;
+
+   first = _eina_rbtree_iterator_list_new(root);
+   if (!first)
+      goto on_error;
+
+   eina_array_push(it->stack, first);
+
+   it->mask = mask;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_eina_rbtree_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _eina_rbtree_iterator_get_content);
+   it->iterator.free = FUNC_ITERATOR_FREE(_eina_rbtree_iterator_free);
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   return &it->iterator;
+
+on_error:
+   eina_array_free(it->stack);
+on_error2:
+   free(it);
+
+   return NULL;
+}
+
+/*
+ * Thanks to Julienne Walker public domain tutorial.
+ * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx
+ */
+
+static void
+_eina_rbtree_node_init(Eina_Rbtree *node)
+{
+   if (!node)
+      return;
+
+   node->son[0] = NULL;
+   node->son[1] = NULL;
+
+   node->color = EINA_RBTREE_RED;
+}
+
+static inline Eina_Bool
+_eina_rbtree_is_red(Eina_Rbtree *node)
+{
+   return !!node && node->color == EINA_RBTREE_RED;
+}
+
+static inline Eina_Rbtree *
+_eina_rbtree_inline_single_rotation(Eina_Rbtree *node,
+                                    Eina_Rbtree_Direction dir)
+{
+   Eina_Rbtree *save = node->son[!dir];
+
+   node->son[!dir] = save->son[dir];
+   save->son[dir] = node;
+
+   node->color = EINA_RBTREE_RED;
+   save->color = EINA_RBTREE_BLACK;
+
+   return save;
+}
+
+static inline Eina_Rbtree *
+_eina_rbtree_inline_double_rotation(Eina_Rbtree *node,
+                                    Eina_Rbtree_Direction dir)
+{
+   node->son[!dir] = _eina_rbtree_inline_single_rotation(node->son[!dir], !dir);
+   return _eina_rbtree_inline_single_rotation(node, dir);
+}
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Rbtree_Group Red-Black tree
+ *
+ * @brief These functions provide Red-Black trees management.
+ *
+ * @{
+ */
+
+EAPI Eina_Rbtree *
+eina_rbtree_inline_insert(Eina_Rbtree *root,
+                          Eina_Rbtree *node,
+                          Eina_Rbtree_Cmp_Node_Cb cmp,
+                          const void *data)
+{
+   Eina_Rbtree head;
+   Eina_Rbtree *g, *t; /* Grandparent & parent */
+   Eina_Rbtree *p, *q; /* Iterator & parent */
+   /* WARNING:
+      Compiler is not able to understand the underlying algorithm and don't know that
+      first top node is always black, so it will never use last before running the loop
+      one time.
+    */
+   Eina_Rbtree_Direction dir, last;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(node, root);
+   EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root);
+
+   if (!node)
+      return root;
+
+   _eina_rbtree_node_init(node);
+
+   if (!root)
+     {
+        root = node;
+        goto end_add;
+     }
+
+   memset(&head, 0, sizeof (Eina_Rbtree));
+   last = dir = EINA_RBTREE_LEFT;
+
+   /* Set up helpers */
+   t = &head;
+   g = p = NULL;
+   q = t->son[1] = root;
+
+   /* Search down the tree */
+   for (;; )
+     {
+        if (!q)
+           /* Insert new node at the bottom */
+           p->son[dir] = q = node;
+        else if (_eina_rbtree_is_red(q->son[0])
+                 && _eina_rbtree_is_red(q->son[1]))
+          {
+             /* Color flip */
+             q->color = EINA_RBTREE_RED;
+             q->son[0]->color = EINA_RBTREE_BLACK;
+             q->son[1]->color = EINA_RBTREE_BLACK;
+          }
+
+        /* Fix red violation */
+        if (_eina_rbtree_is_red(q) && _eina_rbtree_is_red(p))
+          {
+             Eina_Rbtree_Direction dir2;
+
+             dir2 = (t->son[1] == g) ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT;
+
+             if (q == p->son[last])
+                t->son[dir2] = _eina_rbtree_inline_single_rotation(g, !last);
+             else
+                t->son[dir2] = _eina_rbtree_inline_double_rotation(g, !last);
+          }
+
+        /* Stop if found */
+        if (q == node)
+           break;
+
+        last = dir;
+        dir = cmp(q, node, (void *)data);
+
+        /* Update helpers */
+        if ( g )
+           t = g;
+
+        g = p, p = q;
+        q = q->son[dir];
+     }
+
+   root = head.son[1];
+
+end_add:
+   /* Make root black */
+   root->color = EINA_RBTREE_BLACK;
+
+   return root;
+}
+
+EAPI Eina_Rbtree *
+eina_rbtree_inline_remove(Eina_Rbtree *root,
+                          Eina_Rbtree *node,
+                          Eina_Rbtree_Cmp_Node_Cb cmp,
+                          const void *data)
+{
+   Eina_Rbtree head;
+   Eina_Rbtree *q, *p;
+   Eina_Rbtree *f = NULL;
+   Eina_Rbtree_Direction dir;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(node, root);
+   EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root);
+
+   if (!root || !node)
+      return root;
+
+   memset(&head, 0, sizeof(Eina_Rbtree));
+
+   dir = EINA_RBTREE_RIGHT;
+   q = &head;
+   p = NULL;
+   q->son[EINA_RBTREE_RIGHT] = root;
+
+   /* Search and push a red down */
+   while (q->son[dir])
+     {
+        Eina_Rbtree_Direction last = dir;
+        Eina_Rbtree *g;
+
+        /* Update helpers */
+        g = p; p = q;
+        q = q->son[dir];
+        dir = cmp(q, node, (void *)data);
+
+        /* Save parent node found */
+        if (q == node)
+           f = p;
+
+        /* Push the red node down */
+        if (!_eina_rbtree_is_red(q)
+            && !_eina_rbtree_is_red(q->son[dir]))
+          {
+             if (_eina_rbtree_is_red(q->son[!dir]))
+                q = p->son[last] = _eina_rbtree_inline_single_rotation(q, dir);
+             else if (!_eina_rbtree_is_red(q->son[!dir]))
+               {
+                  Eina_Rbtree *s = p->son[!last];
+
+                  if (s)
+                    {
+                       if (!_eina_rbtree_is_red(s->son[EINA_RBTREE_LEFT])
+                           && !_eina_rbtree_is_red(s->son[EINA_RBTREE_RIGHT]))
+                         {
+/* Color flip */
+                            p->color = EINA_RBTREE_BLACK;
+                            p->son[EINA_RBTREE_LEFT]->color = EINA_RBTREE_RED;
+                            p->son[EINA_RBTREE_RIGHT]->color = EINA_RBTREE_RED;
+                         }
+                       else
+                         {
+                            Eina_Rbtree_Direction dir2;
+
+                            dir2 = g->son[1] ==
+                               p ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT;
+
+                            if (_eina_rbtree_is_red(s->son[last]))
+                              {
+                                 g->son[dir2] =
+                                    _eina_rbtree_inline_double_rotation(p, last);
+                                 if (f == g)
+                                   {
+                                      p = g->son[dir2]->son[last];
+                                      f = g->son[dir2];
+                                   }
+                              }
+                            else if (_eina_rbtree_is_red(s->son[!last]))
+                              {
+                                 g->son[dir2] =
+                                    _eina_rbtree_inline_single_rotation(p, last);
+                                 if (f == g)
+                                   {
+                                      p = g->son[dir2]->son[last];
+                                      f = g->son[dir2];
+                                   }
+                              }
+
+/* Ensure correct coloring */
+                            q->color = g->son[dir2]->color = EINA_RBTREE_RED;
+                            g->son[dir2]->son[EINA_RBTREE_LEFT]->color =
+                               EINA_RBTREE_BLACK;
+                            g->son[dir2]->son[EINA_RBTREE_RIGHT]->color =
+                               EINA_RBTREE_BLACK;
+                         }
+                    }
+               }
+          }
+     }
+
+   /* Replace and remove if found */
+   if (f)
+     {
+        /* 'q' should take the place of 'node' parent */
+        f->son[f->son[1] == node] = q;
+
+        /* Switch the link from the parent to q's son */
+        p->son[p->son[1] == q] = q->son[!q->son[0]];
+
+        /* Put q at the place of node */
+        q->son[0] = node->son[0];
+        q->son[1] = node->son[1];
+        q->color = node->color;
+
+        /* Reset node link */
+        node->son[0] = NULL;
+        node->son[1] = NULL;
+     }
+
+   root = head.son[1];
+   if (root)
+      root->color = EINA_RBTREE_BLACK;
+
+   return root;
+}
+
+/**
+ * @brief Returned a new prefix iterator associated to a rbtree.
+ *
+ * @param root The root of rbtree.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * root. It will iterate the tree using prefix walk. If @p root is @c
+ * NULL, this function still returns a valid iterator that will always
+ * return false on eina_iterator_next(), thus keeping API sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the rbtree structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove nodes this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_rbtree_iterator_prefix(const Eina_Rbtree *root)
+{
+   return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_PREFIX_MASK);
+}
+
+/**
+ * @brief Returned a new prefix iterator associated to a rbtree.
+ *
+ * @param root The root of rbtree.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * root. It will iterate the tree using infix walk. If @p root is @c
+ * NULL, this function still returns a valid iterator that will always
+ * return false on eina_iterator_next(), thus keeping API sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the rbtree structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove nodes this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_rbtree_iterator_infix(const Eina_Rbtree *root)
+{
+   return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_INFIX_MASK);
+}
+
+/**
+ * @brief Returned a new prefix iterator associated to a rbtree.
+ *
+ * @param root The root of rbtree.
+ * @return A new iterator.
+ *
+ * This function returns a newly allocated iterator associated to @p
+ * root. It will iterate the tree using postfix walk. If @p root is @c
+ * NULL, this function still returns a valid iterator that will always
+ * return false on eina_iterator_next(), thus keeping API sane.
+ *
+ * If the memory can not be allocated, NULL is returned and
+ * #EINA_ERROR_OUT_OF_MEMORY is set. Otherwise, a valid iterator is
+ * returned.
+ *
+ * @warning if the rbtree structure changes then the iterator becomes
+ *    invalid! That is, if you add or remove nodes this iterator
+ *    behavior is undefined and your program may crash!
+ */
+EAPI Eina_Iterator *
+eina_rbtree_iterator_postfix(const Eina_Rbtree *root)
+{
+   return _eina_rbtree_iterator_build(root, EINA_RBTREE_ITERATOR_POSTFIX_MASK);
+}
+
+EAPI void
+eina_rbtree_delete(Eina_Rbtree *root, Eina_Rbtree_Free_Cb func, void *data)
+{
+   if (!root)
+      return;
+
+   EINA_SAFETY_ON_NULL_RETURN(func);
+
+   eina_rbtree_delete(root->son[0], func, data);
+   eina_rbtree_delete(root->son[1], func, data);
+   func(root, data);
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_rectangle.c b/tests/suite/ecore/src/lib/eina_rectangle.c
new file mode 100644 (file)
index 0000000..237ca8b
--- /dev/null
@@ -0,0 +1,693 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Cedric BAIL, Carsten Haitzler
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_magic.h"
+#include "eina_inlist.h"
+#include "eina_mempool.h"
+#include "eina_list.h"
+#include "eina_trash.h"
+#include "eina_log.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_rectangle.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_RECTANGLE_POOL_MAGIC 0x1578FCB0
+#define EINA_RECTANGLE_ALLOC_MAGIC 0x1578FCB1
+
+#define BUCKET_THRESHOLD 110
+
+typedef struct _Eina_Rectangle_Alloc Eina_Rectangle_Alloc;
+
+struct _Eina_Rectangle_Pool
+{
+   Eina_Inlist *head;
+   Eina_List *empty;
+   void *data;
+
+   Eina_Trash *bucket;
+   unsigned int bucket_count;
+
+   unsigned int references;
+   int w;
+   int h;
+
+   Eina_Bool sorted;
+   EINA_MAGIC
+};
+
+struct _Eina_Rectangle_Alloc
+{
+   EINA_INLIST;
+   Eina_Rectangle_Pool *pool;
+   EINA_MAGIC
+};
+
+#define EINA_MAGIC_CHECK_RECTANGLE_POOL(d)                     \
+   do {                                                         \
+        if (!EINA_MAGIC_CHECK((d), EINA_RECTANGLE_POOL_MAGIC)) {    \
+             EINA_MAGIC_FAIL((d), EINA_RECTANGLE_POOL_MAGIC); }        \
+     } while (0)
+
+#define EINA_MAGIC_CHECK_RECTANGLE_ALLOC(d)                    \
+   do {                                                         \
+        if (!EINA_MAGIC_CHECK((d), EINA_RECTANGLE_ALLOC_MAGIC)) {   \
+             EINA_MAGIC_FAIL((d), EINA_RECTANGLE_ALLOC_MAGIC); }       \
+     } while (0)
+
+static Eina_Mempool *_eina_rectangle_alloc_mp = NULL;
+static Eina_Mempool *_eina_rectangle_mp = NULL;
+
+static Eina_Trash *_eina_rectangles = NULL;
+static unsigned int _eina_rectangles_count = 0;
+static int _eina_rectangle_log_dom = -1;
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_rectangle_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_rectangle_log_dom, __VA_ARGS__)
+
+static int
+_eina_rectangle_cmp(const Eina_Rectangle *r1, const Eina_Rectangle *r2)
+{
+   return (r2->w * r2->h) - (r1->w * r1->h);
+}
+
+static Eina_List *
+_eina_rectangle_merge_list(Eina_List *empty, Eina_Rectangle *r)
+{
+   Eina_Rectangle *match;
+   Eina_List *l;
+   int xw;
+   int yh;
+
+   if (r->w == 0 || r->h == 0)
+     {
+        eina_rectangle_free(r);
+        return empty;
+     }
+
+start_again:
+   xw = r->x + r->w;
+   yh = r->y + r->h;
+
+   EINA_LIST_FOREACH(empty, l, match)
+   {
+      if (match->x == r->x && match->w == r->w
+          && (match->y == yh || r->y == match->y + match->h))
+        {
+           if (match->y > r->y)
+              match->y = r->y;
+
+           match->h += r->h;
+
+           eina_rectangle_free(r);
+
+           empty = eina_list_remove_list(empty, l);
+
+           r = match;
+
+           goto start_again;
+        }
+      else if (match->y == r->y && match->h == r->h
+               && (match->x == xw || r->x == match->x + match->w))
+        {
+           if (match->x > r->x)
+              match->x = r->x;
+
+           match->w += r->w;
+
+           eina_rectangle_free(r);
+
+           empty = eina_list_remove_list(empty, l);
+
+           r = match;
+
+           goto start_again;
+        }
+   }
+
+   return eina_list_append(empty, r);
+}
+
+static Eina_List *
+_eina_rectangle_empty_space_find(Eina_List *empty, int w, int h, int *x, int *y)
+{
+   Eina_Rectangle *r;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(empty, l, r)
+   {
+      if (r->w >= w && r->h >= h)
+        {
+           /* Remove l from empty */
+           empty = eina_list_remove_list(empty, l);
+           /* Remember x and y */
+           *x = r->x;
+           *y = r->y;
+           /* Split r in 2 rectangle if needed (only the empty one) and insert them */
+           if (r->w == w)
+             {
+                r->y += h;
+                r->h -= h;
+             }
+           else if (r->h == h)
+             {
+                r->x += w;
+                r->w -= w;
+             }
+           else
+             {
+                int rx1, ry1, rw1, rh1;
+                int x2, y2, w2, h2;
+
+                rx1 = r->x + w;
+                ry1 = r->y;
+                rw1 = r->w - w;
+                /* h1 could be h or r->h */
+                x2 = r->x;
+                y2 = r->y + h;
+                /* w2 could be w or r->w */
+                h2 = r->h - h;
+
+                if (rw1 * r->h > h2 * r->w)
+                  {
+                     rh1 = r->h;
+                     w2 = w;
+                  }
+                else
+                  {
+                     rh1 = h;
+                     w2 = r->w;
+                  }
+
+                EINA_RECTANGLE_SET(r, rx1, ry1, rw1, rh1);
+                empty = _eina_rectangle_merge_list(empty, r);
+
+                r = eina_rectangle_new(x2, y2, w2, h2);
+             }
+
+           if (r)
+             {
+                empty = _eina_rectangle_merge_list(empty, r); /* Return empty */
+
+             }
+
+           return empty;
+        }
+   }
+
+   *x = -1;
+   *y = -1;
+   return empty;
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+Eina_Bool
+eina_rectangle_init(void)
+{
+   const char *choice, *tmp;
+
+   _eina_rectangle_log_dom = eina_log_domain_register("eina_rectangle",
+                                                      EINA_LOG_COLOR_DEFAULT);
+   if (_eina_rectangle_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_rectangle");
+        return EINA_FALSE;
+     }
+
+#ifdef EINA_DEFAULT_MEMPOOL
+   choice = "pass_through";
+#else
+   choice = "chained_mempool";
+#endif
+   tmp = getenv("EINA_MEMPOOL");
+   if (tmp && tmp[0])
+      choice = tmp;
+
+   _eina_rectangle_alloc_mp = eina_mempool_add
+         (choice, "rectangle-alloc", NULL,
+         sizeof(Eina_Rectangle_Alloc) + sizeof(Eina_Rectangle), 1024);
+   if (!_eina_rectangle_alloc_mp)
+     {
+        ERR("Mempool for rectangle cannot be allocated in rectangle init.");
+        goto init_error;
+     }
+
+   _eina_rectangle_mp = eina_mempool_add
+         (choice, "rectangle", NULL, sizeof(Eina_Rectangle), 256);
+   if (!_eina_rectangle_mp)
+     {
+        ERR("Mempool for rectangle cannot be allocated in rectangle init.");
+        goto init_error;
+     }
+
+   return EINA_TRUE;
+
+init_error:
+   eina_log_domain_unregister(_eina_rectangle_log_dom);
+   _eina_rectangle_log_dom = -1;
+
+   return EINA_FALSE;
+}
+
+Eina_Bool
+eina_rectangle_shutdown(void)
+{
+   Eina_Rectangle *del;
+
+   while ((del = eina_trash_pop(&_eina_rectangles)))
+      eina_mempool_free(_eina_rectangle_mp, del);
+   _eina_rectangles_count = 0;
+
+   eina_mempool_del(_eina_rectangle_alloc_mp);
+   eina_mempool_del(_eina_rectangle_mp);
+
+   eina_log_domain_unregister(_eina_rectangle_log_dom);
+   _eina_rectangle_log_dom = -1;
+
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Rectangle_Group Rectangle
+ *
+ * @brief These functions provide rectangle management.
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new rectangle.
+ *
+ * @param x The X coordinate of the top left corner of the rectangle.
+ * @param y The Y coordinate of the top left corner of the rectangle.
+ * @param w The width of the rectangle.
+ * @param h The height of the rectangle.
+ * @return The new rectangle on success, @ NULL otherwise.
+ *
+ * This function creates a rectangle which top left corner has the
+ * coordinates (@p x, @p y), with height @p w and height @p h and adds
+ * it to the rectangles pool. No check is done on @p w and @p h. This
+ * function returns a new rectangle on success, @c NULL otherwhise.
+ */
+EAPI Eina_Rectangle *
+eina_rectangle_new(int x, int y, int w, int h)
+{
+   Eina_Rectangle *rect;
+
+   if (_eina_rectangles)
+     {
+        rect = eina_trash_pop(&_eina_rectangles);
+        _eina_rectangles_count--;
+     }
+   else
+      rect = eina_mempool_malloc(_eina_rectangle_mp, sizeof (Eina_Rectangle));
+
+   if (!rect)
+      return NULL;
+
+   EINA_RECTANGLE_SET(rect, x, y, w, h);
+
+   return rect;
+}
+
+/**
+ * @brief Free the given rectangle.
+ *
+ * @param rect The rectangle to free.
+ *
+ * This function removes @p rect from the rectangles pool.
+ */
+EAPI void
+eina_rectangle_free(Eina_Rectangle *rect)
+{
+   EINA_SAFETY_ON_NULL_RETURN(rect);
+
+   if (_eina_rectangles_count > BUCKET_THRESHOLD)
+      eina_mempool_free(_eina_rectangle_mp, rect);
+   else
+     {
+        eina_trash_push(&_eina_rectangles, rect);
+        _eina_rectangles_count++;
+     }
+}
+
+/**
+ * @brief Add a rectangle in a new pool.
+ *
+ * @param w The width of the rectangle.
+ * @param h The height of the rectangle.
+ * @return A newly allocated pool on success, @c NULL otherwise.
+ *
+ * This function adds the rectangle of size (@p width, @p height) to a
+ * new pool. If the pool can not be created, @c NULL is
+ * returned. Otherwise the newly allocated pool is returned.
+ */
+EAPI Eina_Rectangle_Pool *
+eina_rectangle_pool_new(int w, int h)
+{
+   Eina_Rectangle_Pool *new;
+
+   new = malloc(sizeof (Eina_Rectangle_Pool));
+   if (!new)
+      return NULL;
+
+   new->head = NULL;
+   new->empty = eina_list_append(NULL, eina_rectangle_new(0, 0, w, h));
+   new->references = 0;
+   new->sorted = EINA_FALSE;
+   new->w = w;
+   new->h = h;
+   new->bucket = NULL;
+   new->bucket_count = 0;
+
+   EINA_MAGIC_SET(new, EINA_RECTANGLE_POOL_MAGIC);
+   DBG("pool=%p, size=(%d, %d)", new, w, h);
+
+   return new;
+}
+
+/**
+ * @brief Free the given pool.
+ *
+ * @param pool The pool to free.
+ *
+ * This function frees the allocated data of @p pool. If @p pool is
+ * @c NULL, ths function returned immediately.
+ */
+EAPI void
+eina_rectangle_pool_free(Eina_Rectangle_Pool *pool)
+{
+   Eina_Rectangle_Alloc *del;
+
+   EINA_SAFETY_ON_NULL_RETURN(pool);
+   DBG("pool=%p, size=(%d, %d), references=%u",
+       pool, pool->w, pool->h, pool->references);
+   while (pool->head)
+     {
+        del = (Eina_Rectangle_Alloc *)pool->head;
+
+        pool->head = (EINA_INLIST_GET(del))->next;
+
+        EINA_MAGIC_SET(del, EINA_MAGIC_NONE);
+        eina_mempool_free(_eina_rectangle_alloc_mp, del);
+     }
+
+   while (pool->bucket)
+     {
+        del = eina_trash_pop(&pool->bucket);
+        eina_mempool_free(_eina_rectangle_alloc_mp, del);
+     }
+
+        MAGIC_FREE(pool);
+}
+
+/**
+ * @brief Return the number of rectangles in the given pool.
+ *
+ * @param pool The pool.
+ * @return The number of rectangles in the pool.
+ *
+ * This function returns the number of rectangles in @p pool.
+ */
+EAPI int
+eina_rectangle_pool_count(Eina_Rectangle_Pool *pool)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pool, 0);
+   return pool->references;
+}
+
+/**
+ * @brief Request a rectangle of given size in the given pool.
+ *
+ * @param pool The pool.
+ * @param w The width of the rectangle to request.
+ * @param h The height of the rectangle to request.
+ * @return The requested rectangle on success, @c NULL otherwise.
+ *
+ * This function retrieve from @p pool the rectangle of width @p w and
+ * height @p h. If @p pool is @c NULL, or @p w or @p h are non-positive,
+ * the function returns @c NULL. If @p w or @p h are greater than the
+ * pool size, the function returns @c NULL. On success, the function
+ * returns the rectangle which matches the size (@p w, @p h).
+ * Otherwise it returns @c NULL.
+ */
+EAPI Eina_Rectangle *
+eina_rectangle_pool_request(Eina_Rectangle_Pool *pool, int w, int h)
+{
+   Eina_Rectangle_Alloc *new;
+   Eina_Rectangle *rect;
+   int x;
+   int y;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pool, NULL);
+
+   DBG("pool=%p, size=(%d, %d), references=%u",
+       pool, pool->w, pool->h, pool->references);
+
+   if (w <= 0 || h <= 0)
+      return NULL;
+
+   if (w > pool->w || h > pool->h)
+      return NULL;
+
+   /* Sort empty if dirty */
+   if (pool->sorted)
+     {
+        pool->empty =
+           eina_list_sort(pool->empty, 0, EINA_COMPARE_CB(_eina_rectangle_cmp));
+        pool->sorted = EINA_TRUE;
+     }
+
+   pool->empty = _eina_rectangle_empty_space_find(pool->empty, w, h, &x, &y);
+   if (x == -1)
+      return NULL;
+
+   pool->sorted = EINA_FALSE;
+
+   if (pool->bucket_count > 0)
+     {
+        new = eina_trash_pop(&pool->bucket);
+        pool->bucket_count--;
+     }
+   else
+      new = eina_mempool_malloc(_eina_rectangle_alloc_mp,
+                                sizeof (Eina_Rectangle_Alloc) +
+                                sizeof (Eina_Rectangle));
+
+   if (!new)
+      return NULL;
+
+   rect = (Eina_Rectangle *)(new + 1);
+   eina_rectangle_coords_from(rect, x, y, w, h);
+
+   pool->head = eina_inlist_prepend(pool->head, EINA_INLIST_GET(new));
+   pool->references++;
+
+   new->pool = pool;
+
+   EINA_MAGIC_SET(new, EINA_RECTANGLE_ALLOC_MAGIC);
+   DBG("rect=%p pool=%p, size=(%d, %d), references=%u",
+       rect, pool, pool->w, pool->h, pool->references);
+
+   return rect;
+}
+
+/**
+ * @brief Remove the given rectangle from the pool.
+ *
+ * @param rect The rectangle to remove from the pool.
+ *
+ * This function removes @p rect from the pool. If @p rect is
+ * @c NULL, the function returns immediately. Otherwise it remoes @p
+ * rect from the pool.
+ */
+EAPI void
+eina_rectangle_pool_release(Eina_Rectangle *rect)
+{
+   Eina_Rectangle_Alloc *era = ((Eina_Rectangle_Alloc *)rect) - 1;
+   Eina_Rectangle *r;
+
+   EINA_SAFETY_ON_NULL_RETURN(rect);
+
+   EINA_MAGIC_CHECK_RECTANGLE_ALLOC(era);
+   EINA_MAGIC_CHECK_RECTANGLE_POOL(era->pool);
+
+   DBG("rect=%p pool=%p, size=(%d, %d), references=%u",
+       rect, era->pool, era->pool->w, era->pool->h, era->pool->references);
+
+   era->pool->references--;
+   era->pool->head = eina_inlist_remove(era->pool->head, EINA_INLIST_GET(era));
+
+   r = eina_rectangle_new(rect->x, rect->y, rect->w, rect->h);
+   if (r)
+     {
+        era->pool->empty = _eina_rectangle_merge_list(era->pool->empty, r);
+        era->pool->sorted = EINA_FALSE;
+     }
+
+   if (era->pool->bucket_count < BUCKET_THRESHOLD)
+     {
+        Eina_Rectangle_Pool *pool;
+
+        pool = era->pool;
+
+        pool->bucket_count++;
+        eina_trash_push(&pool->bucket, era);
+     }
+   else
+     {
+        EINA_MAGIC_SET(era, EINA_MAGIC_NONE);
+        eina_mempool_free(_eina_rectangle_alloc_mp, era);
+     }
+}
+
+/**
+ * @brief Return the pool of the given rectangle.
+ *
+ * @param rect The rectangle.
+ * @return The pool of the given rectangle.
+ *
+ * This function returns the pool in which @p rect is. If  @p rect is
+ * @c NULL, @c NULL is returned.
+ */
+EAPI Eina_Rectangle_Pool *
+eina_rectangle_pool_get(Eina_Rectangle *rect)
+{
+   Eina_Rectangle_Alloc *era = ((Eina_Rectangle_Alloc *)rect) - 1;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(rect, NULL);
+
+   EINA_MAGIC_CHECK_RECTANGLE_ALLOC(era);
+   EINA_MAGIC_CHECK_RECTANGLE_POOL(era->pool);
+
+   return era->pool;
+}
+
+/**
+ * @brief Set the data to the given pool.
+ *
+ * @param pool The pool.
+ * @param data The data to set.
+ *
+ * This function sets @p data to @p pool. If @p pool is @c NULL, this
+ * function does nothing.
+ */
+EAPI void
+eina_rectangle_pool_data_set(Eina_Rectangle_Pool *pool, const void *data)
+{
+   EINA_MAGIC_CHECK_RECTANGLE_POOL(pool);
+   EINA_SAFETY_ON_NULL_RETURN(pool);
+
+   DBG("data=%p pool=%p, size=(%d, %d), references=%u",
+       data, pool, pool->w, pool->h, pool->references);
+
+   pool->data = (void *)data;
+}
+
+/**
+ * @brief Get the data from the given pool.
+ *
+ * @param pool The pool.
+ * @return The returned data.
+ *
+ * This function gets the data from @p pool set by
+ * eina_rectangle_pool_data_set(). If @p pool is @c NULL, this
+ * function returns @c NULL.
+ */
+EAPI void *
+eina_rectangle_pool_data_get(Eina_Rectangle_Pool *pool)
+{
+   EINA_MAGIC_CHECK_RECTANGLE_POOL(pool);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pool, NULL);
+
+   return pool->data;
+}
+
+/**
+ * @brief Return the width and height of the given pool.
+ *
+ * @param pool The pool.
+ * @param w The returned width.
+ * @param h The returned height.
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ *
+ * This function returns the width and height of @p pool and store
+ * them in respectively @p w and @p h if they are not @c NULL. If
+ * @p pool is @c NULL, #EINA_FALSE is returned. Otherwise #EINA_TRUE is
+ * returned.
+ */
+EAPI Eina_Bool
+eina_rectangle_pool_geometry_get(Eina_Rectangle_Pool *pool, int *w, int *h)
+{
+   if (!pool)
+      return EINA_FALSE;
+
+   EINA_MAGIC_CHECK_RECTANGLE_POOL(pool);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(pool, EINA_FALSE);
+
+   if (w)
+      *w = pool->w;
+
+   if (h)
+      *h = pool->h;
+
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_safety_checks.c b/tests/suite/ecore/src/lib/eina_safety_checks.c
new file mode 100644 (file)
index 0000000..09aa298
--- /dev/null
@@ -0,0 +1,114 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2008 Gustavo Sverzut Barbieri
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_private.h"
+#include "eina_error.h"
+#include "eina_log.h"
+#include "eina_safety_checks.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Shut down the safety checks module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the error module set up by
+ * eina_safety_checks_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_safety_checks_shutdown(void)
+{
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Safety_Checks_Group Safety Checks
+ *
+ * Safety checks are a set of macros to check for parameters or values
+ * that should never happen, it is similar in concept to assert(), but
+ * will log and return instead of abort() your program.
+ *
+ * Since these cases should never happen, one may wantto keep safety
+ * checks enabled during tests but disable then during deploy, not
+ * doing any checks at all. This is a common requirement for embedded
+ * systems. Whenever to check or not should be set during compile time
+ * by using @c --disable-safety-checks or @c --enable-safety-checks
+ * options to @c configure script.
+ *
+ * Whenever these macros capture an error, EINA_LOG_ERR() will be
+ * called and @c eina_error set to @c EINA_ERROR_SAFETY_FAILED and can
+ * be checked with eina_error_get() after call.
+ *
+ * @see EINA_SAFETY_ON_NULL_RETURN(), EINA_SAFETY_ON_NULL_RETURN_VAL()
+ *      and other macros.
+ *
+ * @{
+ */
+
+/**
+ * @cond LOCAL
+ */
+
+EAPI Eina_Error EINA_ERROR_SAFETY_FAILED = 0;
+
+static const char EINA_ERROR_SAFETY_FAILED_STR[] = "Safety check failed.";
+
+/**
+ * @endcond
+ */
+
+/**
+ * @internal
+ * @brief Initialize the safety checks module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the safety checks module of Eina. It is
+ * called by eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_safety_checks_init(void)
+{
+   EINA_ERROR_SAFETY_FAILED = eina_error_msg_static_register(
+         EINA_ERROR_SAFETY_FAILED_STR);
+   return EINA_TRUE;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_sched.c b/tests/suite/ecore/src/lib/eina_sched.c
new file mode 100644 (file)
index 0000000..dbb3da6
--- /dev/null
@@ -0,0 +1,95 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2010 ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef EFL_HAVE_POSIX_THREADS
+# include <pthread.h>
+# ifdef __linux__
+#  include <sched.h>
+#  include <sys/time.h>
+#  include <sys/resource.h>
+#  include <errno.h>
+# endif
+#endif
+
+#include "eina_sched.h"
+#include "eina_log.h"
+
+#define RTNICENESS 5
+#define NICENESS 5
+
+/**
+ * @brief Lower priority of current thread.
+ *
+ * It's used by worker threads so they use up background cpu and do not stall
+ * the main thread If current thread is running with real-time priority, we
+ * decrease our priority by @c RTNICENESS. This is done in a portable way.
+ *
+ * Otherwise (we are running with SCHED_OTHER policy) there's no portable way to
+ * set the nice level on current thread. In Linux, it does work and it's the
+ * only one that is implemented as of now. In this case the nice level is
+ * incremented on this thread by @c NICENESS.
+ */
+EAPI void
+eina_sched_prio_drop(void)
+{
+#ifdef EFL_HAVE_POSIX_THREADS
+   struct sched_param param;
+   int pol, prio, ret;
+   pthread_t pthread_id;
+
+   pthread_id = pthread_self();
+   ret = pthread_getschedparam(pthread_id, &pol, &param);
+   if (ret)
+     {
+        EINA_LOG_ERR("Unable to query sched parameters");
+        return;
+     }
+
+   if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO))
+     {
+        prio = sched_get_priority_max(pol);
+        param.sched_priority += RTNICENESS;
+        if (prio > 0 && param.sched_priority > prio)
+           param.sched_priority = prio;
+
+        pthread_setschedparam(pthread_id, pol, &param);
+     }
+#ifdef __linux__
+   else
+     {
+        errno = 0;
+        prio = getpriority(PRIO_PROCESS, 0);
+        if (errno == 0)
+          {
+             prio += NICENESS;
+             if (prio > 19)
+                prio = 19;
+
+             setpriority(PRIO_PROCESS, 0, prio);
+          }
+     }
+#endif
+#else
+   EINA_LOG_ERR("Eina does not have support for threads enabled"
+                "or it doesn't support setting scheduler priorities");
+#endif
+}
diff --git a/tests/suite/ecore/src/lib/eina_share_common.c b/tests/suite/ecore/src/lib/eina_share_common.c
new file mode 100644 (file)
index 0000000..2302843
--- /dev/null
@@ -0,0 +1,974 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2010
+ *                         Carsten Haitzler,
+ *                         Jorge Luis Zapata Muga,
+ *                         Cedric Bail,
+ *                         Gustavo Sverzut Barbieri
+ *                         Tom Hacohen
+ *                         Brett Nash
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2008 Peter Wehrfritz
+ *
+ *  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 and its Copyright notices. In addition publicly
+ *  documented acknowledgment must be given that this software has been used if no
+ *  source code of this software is made available publicly. This includes
+ *  acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+ *  documents or any documentation provided with any product containing this
+ *  software. This License does not apply to any software that links to the
+ *  libraries provided by this software (statically or dynamically), but only to
+ *  the software provided.
+ *
+ *  Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice
+ *  and it's intent.
+ *
+ *  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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+
+#ifdef EFL_HAVE_POSIX_THREADS
+# include <pthread.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_hash.h"
+#include "eina_rbtree.h"
+#include "eina_error.h"
+#include "eina_log.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_share_common.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_SHARE_COMMON_BUCKETS 256
+#define EINA_SHARE_COMMON_MASK 0xFF
+
+static const char EINA_MAGIC_SHARE_STR[] = "Eina Share";
+static const char EINA_MAGIC_SHARE_HEAD_STR[] = "Eina Share Head";
+
+
+#define EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(d, unlock, ...)      \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK((d), EINA_MAGIC_SHARE_HEAD))  \
+          {                                                           \
+             EINA_MAGIC_FAIL((d), EINA_MAGIC_SHARE_HEAD);    \
+             unlock;                                                 \
+             return __VA_ARGS__;                                     \
+          }                                                           \
+     } while (0)
+
+#define EINA_MAGIC_CHECK_SHARE_COMMON_NODE(d, _node_magic, unlock)              \
+   do {                                                          \
+        if (!EINA_MAGIC_CHECK((d), _node_magic))    \
+          {                                                           \
+             unlock;                                                   \
+             EINA_MAGIC_FAIL((d), _node_magic);        \
+          }                                                           \
+     } while (0)
+
+#ifdef EINA_SHARE_USAGE
+typedef struct _Eina_Share_Common_Population Eina_Share_Common_Population;
+#endif
+
+typedef struct _Eina_Share_Common Eina_Share_Common;
+typedef struct _Eina_Share_Common_Node Eina_Share_Common_Node;
+typedef struct _Eina_Share_Common_Head Eina_Share_Common_Head;
+
+int _eina_share_common_log_dom = -1;
+
+struct _Eina_Share
+{
+   Eina_Share_Common *share;
+   Eina_Magic node_magic;
+#ifdef EINA_SHARE_COMMON_USAGE
+   Eina_Share_Common_Population population;
+   int max_node_population;
+#endif
+};
+
+struct _Eina_Share_Common
+{
+   Eina_Share_Common_Head *buckets[EINA_SHARE_COMMON_BUCKETS];
+
+   EINA_MAGIC
+};
+
+struct _Eina_Share_Common_Node
+{
+   Eina_Share_Common_Node *next;
+
+   EINA_MAGIC
+
+   unsigned int length;
+   unsigned int references;
+   char str[];
+};
+
+struct _Eina_Share_Common_Head
+{
+   EINA_RBTREE;
+   EINA_MAGIC
+
+   int hash;
+
+#ifdef EINA_SHARE_COMMON_USAGE
+   int population;
+#endif
+
+   Eina_Share_Common_Node *head;
+   Eina_Share_Common_Node builtin_node;
+};
+
+#ifdef EFL_HAVE_THREADS
+Eina_Bool _share_common_threads_activated = EINA_FALSE;
+
+# ifdef EFL_HAVE_POSIX_THREADS
+static pthread_mutex_t _mutex_big = PTHREAD_MUTEX_INITIALIZER;
+#  define SHARE_COMMON_LOCK_BIG() if(_share_common_threads_activated) \
+      pthread_mutex_lock(&_mutex_big)
+#  define SHARE_COMMON_UNLOCK_BIG() if(_share_common_threads_activated) \
+      pthread_mutex_unlock(&_mutex_big)
+# else /* EFL_HAVE_WIN32_THREADS */
+static HANDLE _mutex_big = NULL;
+#  define SHARE_COMMON_LOCK_BIG() if(_share_common_threads_activated) \
+      WaitForSingleObject(_mutex_big, INFINITE)
+#  define SHARE_COMMON_UNLOCK_BIG() if(_share_common_threads_activated) \
+      ReleaseMutex(_mutex_big)
+
+# endif /* EFL_HAVE_WIN32_THREADS */
+#else /* EFL_HAVE_THREADS */
+# define SHARE_COMMON_LOCK_BIG() do {} while (0)
+# define SHARE_COMMON_UNLOCK_BIG() do {} while (0)
+#endif
+
+#ifdef EINA_SHARE_COMMON_USAGE
+struct _Eina_Share_Common_Population
+{
+   int count;
+   int max;
+};
+
+static Eina_Share_Common_Population population = { 0, 0 };
+
+static Eina_Share_Common_Population population_group[4] =
+{
+   { 0, 0 },
+   { 0, 0 },
+   { 0, 0 },
+   { 0, 0 }
+};
+
+static void
+_eina_share_common_population_init(Eina_Share *share)
+{
+   unsigned int i;
+
+   for (i = 0;
+        i < sizeof (share->population_group) /
+        sizeof (share->population_group[0]);
+        ++i)
+     {
+        share->population_group[i].count = 0;
+        share->population_group[i].max = 0;
+     }
+}
+
+static void
+_eina_share_common_population_shutdown(Eina_Share *share)
+{
+   unsigned int i;
+
+   share->max_node_population = 0;
+   share->population.count = 0;
+   share->population.max = 0;
+
+   for (i = 0;
+        i < sizeof (share->population_group) /
+        sizeof (share->population_group[0]);
+        ++i)
+     {
+        share->population_group[i].count = 0;
+        share->population_group[i].max = 0;
+     }
+}
+
+static void
+_eina_share_common_population_stats(Eina_Share *share)
+{
+   unsigned int i;
+
+      fprintf(stderr, "eina share_common statistic:\n");
+      fprintf(stderr,
+           " * maximum shared strings : %i\n",
+           share->population.max);
+      fprintf(stderr,
+           " * maximum shared strings per node : %i\n",
+           share->max_node_population);
+
+   for (i = 0;
+        i < sizeof (share->population_group) /
+        sizeof (share->population_group[0]);
+        ++i)
+      fprintf(stderr,
+              "DDD: %i strings of length %i, max strings: %i\n",
+              share->population_group[i].count,
+              i,
+              share->population_group[i].max);
+}
+
+void
+eina_share_common_population_add(Eina_Share *share, int slen)
+{
+   SHARE_COMMON_LOCK_BIG();
+
+   share->population.count++;
+   if (share->population.count > share->population.max)
+      share->population.max = share->population.count;
+
+   if (slen < 4)
+     {
+        share->population_group[slen].count++;
+        if (share->population_group[slen].count >
+            share->population_group[slen].max)
+           share->population_group[slen].max =
+              share->population_group[slen].count;
+     }
+
+   SHARE_COMMON_UNLOCK_BIG();
+}
+
+void
+eina_share_common_population_del(Eina_Share *share, int slen)
+{
+   SHARE_COMMON_LOCK_BIG();
+
+   share->population.count--;
+   if (slen < 4)
+      share->population_group[slen].count--;
+
+   SHARE_COMMON_UNLOCK_BIG();
+}
+
+static void
+_eina_share_common_population_head_init(Eina_Share *share,
+                                        Eina_Share_Common_Head *head)
+{
+   head->population = 1;
+}
+
+static void
+_eina_share_common_population_head_add(Eina_Share *share,
+                                       Eina_Share_Common_Head *head)
+{
+   head->population++;
+   if (head->population > share->max_node_population)
+      share->max_node_population = head->population;
+}
+
+static void
+_eina_share_common_population_head_del(Eina_Share *share,
+                                       Eina_Share_Common_Head *head)
+{
+   head->population--;
+}
+
+#else /* EINA_SHARE_COMMON_USAGE undefined */
+
+static void _eina_share_common_population_init(__UNUSED__ Eina_Share *share) {
+}
+static void _eina_share_common_population_shutdown(__UNUSED__ Eina_Share *share)
+{
+}
+static void _eina_share_common_population_stats(__UNUSED__ Eina_Share *share) {
+}
+void eina_share_common_population_add(__UNUSED__ Eina_Share *share,
+                                      __UNUSED__ int slen) {
+}
+void eina_share_common_population_del(__UNUSED__ Eina_Share *share,
+                                      __UNUSED__ int slen) {
+}
+static void _eina_share_common_population_head_init(
+   __UNUSED__ Eina_Share *share,
+   __UNUSED__ Eina_Share_Common_Head *head) {
+}
+static void _eina_share_common_population_head_add(
+   __UNUSED__ Eina_Share *share,
+   __UNUSED__
+   Eina_Share_Common_Head *head) {
+}
+static void _eina_share_common_population_head_del(
+   __UNUSED__ Eina_Share *share,
+   __UNUSED__
+   Eina_Share_Common_Head *head) {
+}
+#endif
+
+static int
+_eina_share_common_cmp(const Eina_Share_Common_Head *ed,
+                       const int *hash,
+                       __UNUSED__ int length,
+                       __UNUSED__ void *data)
+{
+   EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, , 0);
+
+   return ed->hash - *hash;
+}
+
+static Eina_Rbtree_Direction
+_eina_share_common_node(const Eina_Share_Common_Head *left,
+                        const Eina_Share_Common_Head *right,
+                        __UNUSED__ void *data)
+{
+   EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(left,  , 0);
+   EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(right, , 0);
+
+   if (left->hash - right->hash < 0)
+      return EINA_RBTREE_LEFT;
+
+   return EINA_RBTREE_RIGHT;
+}
+
+static void
+_eina_share_common_head_free(Eina_Share_Common_Head *ed, __UNUSED__ void *data)
+{
+   EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, );
+
+   while (ed->head)
+     {
+        Eina_Share_Common_Node *el = ed->head;
+
+        ed->head = ed->head->next;
+        if (el != &ed->builtin_node)
+           MAGIC_FREE(el);
+     }
+           MAGIC_FREE(ed);
+}
+
+static void
+_eina_share_common_node_init(Eina_Share_Common_Node *node,
+                             const char *str,
+                             int slen,
+                             unsigned int null_size,
+                             Eina_Magic node_magic)
+{
+   EINA_MAGIC_SET(node, node_magic);
+   node->references = 1;
+   node->length = slen;
+   memcpy(node->str, str, slen);
+   memset(node->str + slen, 0, null_size); /* Nullify the null */
+
+   (void) node_magic; /* When magic are disable, node_magic is unused, this remove a warning. */
+}
+
+static Eina_Share_Common_Head *
+_eina_share_common_head_alloc(int slen)
+{
+   Eina_Share_Common_Head *head;
+   const size_t head_size = offsetof(Eina_Share_Common_Head, builtin_node.str);
+
+   head = malloc(head_size + slen);
+   if (!head)
+      eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+
+   return head;
+}
+
+static const char *
+_eina_share_common_add_head(Eina_Share *share,
+                            Eina_Share_Common_Head **p_bucket,
+                            int hash,
+                            const char *str,
+                            unsigned int slen,
+                            unsigned int null_size)
+{
+   Eina_Rbtree **p_tree = (Eina_Rbtree **)p_bucket;
+   Eina_Share_Common_Head *head;
+
+   head = _eina_share_common_head_alloc(slen + null_size);
+   if (!head)
+      return NULL;
+
+   EINA_MAGIC_SET(head, EINA_MAGIC_SHARE_HEAD);
+   head->hash = hash;
+   head->head = &head->builtin_node;
+   _eina_share_common_node_init(head->head,
+                                str,
+                                slen,
+                                null_size,
+                                share->node_magic);
+   head->head->next = NULL;
+
+   _eina_share_common_population_head_init(share, head);
+
+   *p_tree = eina_rbtree_inline_insert
+         (*p_tree, EINA_RBTREE_GET(head),
+         EINA_RBTREE_CMP_NODE_CB(_eina_share_common_node), NULL);
+
+   return head->head->str;
+}
+
+static void
+_eina_share_common_del_head(Eina_Share_Common_Head **p_bucket,
+                            Eina_Share_Common_Head *head)
+{
+   Eina_Rbtree **p_tree = (Eina_Rbtree **)p_bucket;
+
+   *p_tree = eina_rbtree_inline_remove
+         (*p_tree, EINA_RBTREE_GET(head),
+         EINA_RBTREE_CMP_NODE_CB(_eina_share_common_node), NULL);
+
+         MAGIC_FREE(head);
+}
+
+
+static inline Eina_Bool
+_eina_share_common_node_eq(const Eina_Share_Common_Node *node,
+                           const char *str,
+                           unsigned int slen)
+{
+   return ((node->length == slen) &&
+           (memcmp(node->str, str, slen) == 0));
+}
+
+static Eina_Share_Common_Node *
+_eina_share_common_head_find(Eina_Share_Common_Head *head,
+                             const char *str,
+                             unsigned int slen)
+{
+   Eina_Share_Common_Node *node, *prev;
+
+   node = head->head;
+   if (_eina_share_common_node_eq(node, str, slen))
+      return node;
+
+   prev = node;
+   node = node->next;
+   for (; node; prev = node, node = node->next)
+      if (_eina_share_common_node_eq(node, str, slen))
+        {
+           /* promote node, make hot items be at the beginning */
+           prev->next = node->next;
+           node->next = head->head;
+           head->head = node;
+           return node;
+        }
+
+   return NULL;
+}
+
+static Eina_Bool
+_eina_share_common_head_remove_node(Eina_Share_Common_Head *head,
+                                    const Eina_Share_Common_Node *node)
+{
+   Eina_Share_Common_Node *cur, *prev;
+
+   if (head->head == node)
+     {
+        head->head = node->next;
+        return 1;
+     }
+
+   prev = head->head;
+   cur = head->head->next;
+   for (; cur; prev = cur, cur = cur->next)
+      if (cur == node)
+        {
+           prev->next = cur->next;
+           return 1;
+        }
+
+   return 0;
+}
+
+static Eina_Share_Common_Head *
+_eina_share_common_find_hash(Eina_Share_Common_Head *bucket, int hash)
+{
+   return (Eina_Share_Common_Head *)eina_rbtree_inline_lookup
+             (EINA_RBTREE_GET(bucket), &hash, 0,
+             EINA_RBTREE_CMP_KEY_CB(_eina_share_common_cmp), NULL);
+}
+
+static Eina_Share_Common_Node *
+_eina_share_common_node_alloc(unsigned int slen, unsigned int null_size)
+{
+   Eina_Share_Common_Node *node;
+   const size_t node_size = offsetof(Eina_Share_Common_Node, str);
+
+   node = malloc(node_size + slen + null_size);
+   if (!node)
+      eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+
+   return node;
+}
+
+static Eina_Share_Common_Node *
+_eina_share_common_node_from_str(const char *str, Eina_Magic node_magic)
+{
+   Eina_Share_Common_Node *node;
+   const size_t offset = offsetof(Eina_Share_Common_Node, str);
+
+   node = (Eina_Share_Common_Node *)(str - offset);
+   EINA_MAGIC_CHECK_SHARE_COMMON_NODE(node, node_magic, );
+   return node;
+
+   (void) node_magic; /* When magic are disable, node_magic is unused, this remove a warning. */
+}
+
+static Eina_Bool
+eina_iterator_array_check(const Eina_Rbtree *rbtree __UNUSED__,
+                          Eina_Share_Common_Head *head,
+                          struct dumpinfo *fdata)
+{
+   Eina_Share_Common_Node *node;
+
+   SHARE_COMMON_LOCK_BIG();
+
+   fdata->used += sizeof(Eina_Share_Common_Head);
+   for (node = head->head; node; node = node->next)
+     {
+        printf("DDD: %5i %5i ", node->length, node->references);
+        printf("'%.*s'\n", node->length, ((char *)node) + sizeof(Eina_Share_Common_Node));
+        fdata->used += sizeof(Eina_Share_Common_Node);
+        fdata->used += node->length;
+        fdata->saved += (node->references - 1) * node->length;
+        fdata->dups += node->references - 1;
+        fdata->unique++;
+     }
+
+   SHARE_COMMON_UNLOCK_BIG();
+
+   return EINA_TRUE;
+}
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the share_common module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_share_common_init(Eina_Share **_share,
+                       Eina_Magic node_magic,
+                       const char *node_magic_STR)
+{
+   Eina_Share *share;
+   share = *_share = calloc(sizeof(Eina_Share), 1);
+   if (!share)
+      return EINA_FALSE;
+
+   if (_eina_share_common_log_dom < 0) /*Only register if not already */
+      _eina_share_common_log_dom = eina_log_domain_register(
+            "eina_share",
+            EINA_LOG_COLOR_DEFAULT);
+
+   if (_eina_share_common_log_dom < 0)
+     {
+        EINA_LOG_ERR("Could not register log domain: eina_share_common");
+        return EINA_FALSE;
+     }
+
+   share->share = calloc(1, sizeof(Eina_Share_Common));
+   if (!share->share)
+     {
+        if (_eina_share_common_log_dom > 0)
+          {
+             eina_log_domain_unregister(_eina_share_common_log_dom);
+             _eina_share_common_log_dom = -1;
+          }
+
+        return EINA_FALSE;
+     }
+
+   share->node_magic = node_magic;
+#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
+   EMS(EINA_MAGIC_SHARE);
+   EMS(EINA_MAGIC_SHARE_HEAD);
+   EMS(node_magic);
+#undef EMS
+   EINA_MAGIC_SET(share->share, EINA_MAGIC_SHARE);
+
+   _eina_share_common_population_init(share);
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the share_common module set up by
+ * eina_share_common_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_share_common_shutdown(Eina_Share **_share)
+{
+   unsigned int i;
+   Eina_Share *share = *_share;
+
+   SHARE_COMMON_LOCK_BIG();
+
+   _eina_share_common_population_stats(share);
+
+   /* remove any string still in the table */
+   for (i = 0; i < EINA_SHARE_COMMON_BUCKETS; i++)
+     {
+        eina_rbtree_delete(EINA_RBTREE_GET(
+                              share->share->buckets[i]),
+                           EINA_RBTREE_FREE_CB(
+                              _eina_share_common_head_free), NULL);
+        share->share->buckets[i] = NULL;
+     }
+   MAGIC_FREE(share->share);
+
+   _eina_share_common_population_shutdown(share);
+   if (_eina_share_common_log_dom > 0) /* Only free if necessary */
+     {
+        eina_log_domain_unregister(_eina_share_common_log_dom);
+        _eina_share_common_log_dom = -1;
+     }
+
+   SHARE_COMMON_UNLOCK_BIG();
+
+   free(*_share);
+   *_share = NULL;
+   return EINA_TRUE;
+}
+
+#ifdef EFL_HAVE_THREADS
+
+/**
+ * @internal
+ * @brief Activate the share_common mutexes.
+ *
+ * This function activate the mutexes in the eina share_common module. It is called by
+ * eina_threads_init().
+ *
+ * @see eina_threads_init()
+ */
+void
+eina_share_common_threads_init(void)
+{
+   _share_common_threads_activated = EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the share_common mutexes.
+ *
+ * This function shuts down the mutexes in the share_common module.
+ * It is called by eina_threads_shutdown().
+ *
+ * @see eina_threads_shutdown()
+ */
+void
+eina_share_common_threads_shutdown(void)
+{
+   _share_common_threads_activated = EINA_FALSE;
+}
+
+#endif
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+const char *
+eina_share_common_add_length(Eina_Share *share,
+                             const char *str,
+                             unsigned int slen,
+                             unsigned int null_size)
+{
+   Eina_Share_Common_Head **p_bucket, *ed;
+   Eina_Share_Common_Node *el;
+   int hash_num, hash;
+
+   if (!str)
+      return NULL;
+
+   eina_share_common_population_add(share, slen);
+
+   if (slen <= 0)
+      return NULL;
+
+   hash = eina_hash_superfast(str, slen);
+   hash_num = hash & 0xFF;
+   hash = (hash >> 8) & EINA_SHARE_COMMON_MASK;
+
+   SHARE_COMMON_LOCK_BIG();
+   p_bucket = share->share->buckets + hash_num;
+
+   ed = _eina_share_common_find_hash(*p_bucket, hash);
+   if (!ed)
+     {
+        const char *s = _eina_share_common_add_head(share,
+                                                    p_bucket,
+                                                    hash,
+                                                    str,
+                                                    slen,
+                                                    null_size);
+        SHARE_COMMON_UNLOCK_BIG();
+        return s;
+     }
+
+   EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, SHARE_COMMON_UNLOCK_BIG(), NULL);
+
+   el = _eina_share_common_head_find(ed, str, slen);
+   if (el)
+     {
+        EINA_MAGIC_CHECK_SHARE_COMMON_NODE(el,
+                                           share->node_magic,
+                                           SHARE_COMMON_UNLOCK_BIG());
+        el->references++;
+                                           SHARE_COMMON_UNLOCK_BIG();
+        return el->str;
+     }
+
+   el = _eina_share_common_node_alloc(slen, null_size);
+   if (!el)
+     {
+                                           SHARE_COMMON_UNLOCK_BIG();
+        return NULL;
+     }
+
+   _eina_share_common_node_init(el, str, slen, null_size, share->node_magic);
+   el->next = ed->head;
+   ed->head = el;
+   _eina_share_common_population_head_add(share, ed);
+
+   SHARE_COMMON_UNLOCK_BIG();
+
+   return el->str;
+}
+
+const char *
+eina_share_common_ref(Eina_Share *share, const char *str)
+{
+   Eina_Share_Common_Node *node;
+
+   if (!str)
+      return NULL;
+
+   SHARE_COMMON_LOCK_BIG();
+   node = _eina_share_common_node_from_str(str, share->node_magic);
+   node->references++;
+   DBG("str=%p refs=%u", str, node->references);
+
+   SHARE_COMMON_UNLOCK_BIG();
+
+   eina_share_common_population_add(share, node->length);
+
+   return str;
+}
+
+
+void
+eina_share_common_del(Eina_Share *share, const char *str)
+{
+   unsigned int slen;
+   Eina_Share_Common_Head *ed;
+   Eina_Share_Common_Head **p_bucket;
+   Eina_Share_Common_Node *node;
+   int hash_num, hash;
+
+   if (!str)
+      return;
+
+   SHARE_COMMON_LOCK_BIG();
+
+   node = _eina_share_common_node_from_str(str, share->node_magic);
+   slen = node->length;
+   eina_share_common_population_del(share, slen);
+   if (node->references > 1)
+     {
+        node->references--;
+        DBG("str=%p refs=%u", str, node->references);
+        SHARE_COMMON_UNLOCK_BIG();
+        return;
+     }
+
+   DBG("str=%p refs=0, delete.", str);
+   node->references = 0;
+
+   hash = eina_hash_superfast(str, slen);
+   hash_num = hash & 0xFF;
+   hash = (hash >> 8) & EINA_SHARE_COMMON_MASK;
+
+   p_bucket = share->share->buckets + hash_num;
+   ed = _eina_share_common_find_hash(*p_bucket, hash);
+   if (!ed)
+      goto on_error;
+
+   EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, SHARE_COMMON_UNLOCK_BIG());
+
+   if (!_eina_share_common_head_remove_node(ed, node))
+      goto on_error;
+
+   if (node != &ed->builtin_node)
+      MAGIC_FREE(node);
+
+   if (!ed->head)
+      _eina_share_common_del_head(p_bucket, ed);
+   else
+      _eina_share_common_population_head_del(share, ed);
+
+   SHARE_COMMON_UNLOCK_BIG();
+
+   return;
+
+on_error:
+   SHARE_COMMON_UNLOCK_BIG();
+   /* possible segfault happened before here, but... */
+   CRITICAL("EEEK trying to del non-shared share_common \"%s\"", str);
+}
+
+int
+eina_share_common_length(__UNUSED__ Eina_Share *share, const char *str)
+{
+   const Eina_Share_Common_Node *node;
+
+   if (!str)
+      return -1;
+
+   node = _eina_share_common_node_from_str(str, share->node_magic);
+   return node->length;
+}
+
+void
+eina_share_common_dump(Eina_Share *share, void (*additional_dump)(
+                          struct dumpinfo *), int used)
+{
+   Eina_Iterator *it;
+   unsigned int i;
+   struct dumpinfo di;
+
+   if (!share)
+      return;
+
+   di.used = used;
+   di.saved = 0;
+   di.dups = 0;
+   di.unique = 0;
+   printf("DDD:   len   ref string\n");
+   printf("DDD:-------------------\n");
+
+   SHARE_COMMON_LOCK_BIG();
+   for (i = 0; i < EINA_SHARE_COMMON_BUCKETS; i++)
+     {
+        if (!share->share->buckets[i])
+          {
+             continue; //      printf("DDD: BUCKET # %i (HEAD=%i, NODE=%i)\n", i,
+
+          }
+
+//            sizeof(Eina_Share_Common_Head), sizeof(Eina_Share_Common_Node));
+        it = eina_rbtree_iterator_prefix(
+              (Eina_Rbtree *)share->share->buckets[i]);
+        eina_iterator_foreach(it, EINA_EACH_CB(eina_iterator_array_check), &di);
+        eina_iterator_free(it);
+     }
+   if (additional_dump)
+      additional_dump(&di);
+
+#ifdef EINA_SHARE_COMMON_USAGE
+   /* One character strings are not counted in the hash. */
+   di.saved += share->population_group[0].count * sizeof(char);
+   di.saved += share->population_group[1].count * sizeof(char) * 2;
+#endif
+   printf("DDD:-------------------\n");
+   printf("DDD: usage (bytes) = %i, saved = %i (%3.0f%%)\n",
+          di.used, di.saved, di.used ? (di.saved * 100.0 / di.used) : 0.0);
+   printf("DDD: unique: %d, duplicates: %d (%3.0f%%)\n",
+          di.unique, di.dups, di.unique ? (di.dups * 100.0 / di.unique) : 0.0);
+
+#ifdef EINA_SHARE_COMMON_USAGE
+   printf("DDD: Allocated strings: %i\n",     share->population.count);
+   printf("DDD: Max allocated strings: %i\n", share->population.max);
+
+   for (i = 0;
+        i < sizeof (share->population_group) /
+        sizeof (share->population_group[0]);
+        ++i)
+      fprintf(stderr,
+              "DDD: %i strings of length %i, max strings: %i\n",
+              share->population_group[i].count,
+              i,
+              share->population_group[i].max);
+#endif
+
+   SHARE_COMMON_UNLOCK_BIG();
+}
+
+/**
+ * @endcond
+ */
diff --git a/tests/suite/ecore/src/lib/eina_share_common.h b/tests/suite/ecore/src/lib/eina_share_common.h
new file mode 100644 (file)
index 0000000..002c652
--- /dev/null
@@ -0,0 +1,103 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler, Jorge Luis Zapata Muga, Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2008 Peter Wehrfritz
+ *
+ *  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 and its Copyright notices. In addition publicly
+ *  documented acknowledgment must be given that this software has been used if no
+ *  source code of this software is made available publicly. This includes
+ *  acknowledgments in either Copyright notices, Manuals, Publicity and Marketing
+ *  documents or any documentation provided with any product containing this
+ *  software. This License does not apply to any software that links to the
+ *  libraries provided by this software (statically or dynamically), but only to
+ *  the software provided.
+ *
+ *  Please see the OLD-COPYING.PLAIN for a plain-english explanation of this notice
+ *  and it's intent.
+ *
+ *  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.
+ */
+
+#ifndef EINA_SHARE_COMMON_H_
+#define EINA_SHARE_COMMON_H_
+
+#include "eina_types.h"
+#include "eina_magic.h"
+
+typedef struct _Eina_Share Eina_Share;
+
+struct dumpinfo
+{
+   int used, saved, dups, unique;
+};
+
+Eina_Bool   eina_share_common_init(Eina_Share **share,
+                                   Eina_Magic node_magic,
+                                   const char *node_magic_STR);
+Eina_Bool   eina_share_common_shutdown(Eina_Share **share);
+const char *eina_share_common_add_length(Eina_Share *share,
+                                         const char *str,
+                                         unsigned int slen,
+                                         unsigned int null_size)
+EINA_WARN_UNUSED_RESULT;
+const char *eina_share_common_ref(Eina_Share *share, const char *str);
+void        eina_share_common_del(Eina_Share *share, const char *str);
+int         eina_share_common_length(Eina_Share *share,
+                                     const char *str) EINA_CONST
+EINA_WARN_UNUSED_RESULT;
+void        eina_share_common_dump(Eina_Share *share, void (*additional_dump)(
+                                      struct dumpinfo *), int used);
+
+
+/* Population functions */
+void        eina_share_common_population_add(Eina_Share *share, int slen);
+void        eina_share_common_population_del(Eina_Share *share, int slen);
+
+/* Share logging */
+#ifdef CRITICAL
+#undef CRITICAL
+#endif
+#define CRITICAL(...) EINA_LOG_DOM_CRIT(_eina_share_common_log_dom, __VA_ARGS__)
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_share_common_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_share_common_log_dom, __VA_ARGS__)
+extern int _eina_share_common_log_dom;
+
+#endif /* EINA_STRINGSHARE_H_ */
diff --git a/tests/suite/ecore/src/lib/eina_str.c b/tests/suite/ecore/src/lib/eina_str.c
new file mode 100644 (file)
index 0000000..bd9badb
--- /dev/null
@@ -0,0 +1,639 @@
+/* Leave the OpenBSD version below so we can track upstream fixes */
+/*      $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $        */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+
+#ifdef HAVE_ICONV
+# include <errno.h>
+# include <iconv.h>
+#endif
+
+#include "eina_private.h"
+#include "eina_str.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+/*
+ * Internal helper function used by eina_str_has_suffix() and
+ * eina_str_has_extension()
+ */
+static inline Eina_Bool
+eina_str_has_suffix_helper(const char *str,
+                           const char *suffix,
+                           int (*cmp)(const char *, const char *))
+{
+   size_t str_len;
+   size_t suffix_len;
+
+   str_len = strlen(str);
+   suffix_len = eina_strlen_bounded(suffix, str_len);
+   if (suffix_len == (size_t)-1)
+      return EINA_FALSE;
+
+   return cmp(str + str_len - suffix_len, suffix) == 0;
+}
+
+static inline char **
+eina_str_split_full_helper(const char *str,
+                           const char *delim,
+                           int max_tokens,
+                           unsigned int *elements)
+{
+   char *s, **str_array;
+   const char *src;
+   size_t len, dlen;
+   unsigned int tokens;
+
+   dlen = strlen(delim);
+   if (dlen == 0)
+     {
+        if (elements)
+           *elements = 0;
+
+        return NULL;
+     }
+
+   tokens = 0;
+   src = str;
+   /* count tokens and check strlen(str) */
+   while (*src != '\0')
+     {
+        const char *d = delim, *d_end = d + dlen;
+        const char *tmp = src;
+        for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
+          {
+             if (EINA_LIKELY(*d != *tmp))
+                break;
+          }
+        if (EINA_UNLIKELY(d == d_end))
+          {
+             src = tmp;
+             tokens++;
+          }
+        else
+           src++;
+     }
+   len = src - str;
+
+   if ((max_tokens > 0) && (tokens > (unsigned int)max_tokens))
+      tokens = max_tokens;
+
+   str_array = malloc(sizeof(char *) * (tokens + 2));
+   if (!str_array)
+     {
+        if (elements)
+           *elements = 0;
+
+        return NULL;
+     }
+
+   s = malloc(len + 1);
+   if (!s)
+     {
+        free(str_array);
+        if (elements)
+           *elements = 0;
+
+        return NULL;
+     }
+
+   /* copy tokens and string */
+   tokens = 0;
+   str_array[0] = s;
+   src = str;
+   while (*src != '\0')
+     {
+        const char *d = delim, *d_end = d + dlen;
+        const char *tmp = src;
+        for (; (d < d_end) && (*tmp != '\0'); d++, tmp++)
+          {
+             if (EINA_LIKELY(*d != *tmp))
+                break;
+          }
+        if (EINA_UNLIKELY(d == d_end))
+          {
+             src = tmp;
+             *s = '\0';
+             s += dlen;
+             tokens++;
+             str_array[tokens] = s;
+          }
+        else
+          {
+             *s = *src;
+             s++;
+             src++;
+          }
+     }
+   *s = '\0';
+   str_array[tokens + 1] = NULL;
+   if (elements)
+      *elements = (tokens + 1);
+
+   return str_array;
+}
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_String_Group String
+ *
+ * @brief These functions provide useful C string management.
+ *
+ * @{
+ */
+
+
+/**
+ * @brief Copy a c-string to another.
+ *
+ * @param dst The destination string.
+ * @param src The source string.
+ * @param siz The size of the destination string.
+ * @return The length of the source string.
+ *
+ * This function copies up to @p siz - 1 characters from the
+ * NUL-terminated string @p src to @p dst, NUL-terminating the result
+ * (unless @p siz is equal to 0). The returned value is the length of
+ * @p src. If the returned value is greater than @p siz, truncation
+ * occurred.
+ */
+EAPI size_t
+eina_strlcpy(char *dst, const char *src, size_t siz)
+{
+#ifdef HAVE_STRLCPY
+   return strlcpy(dst, src, siz);
+#else
+   char *d = dst;
+   const char *s = src;
+   size_t n = siz;
+
+   /* Copy as many bytes as will fit */
+   if (n != 0)
+      while (--n != 0)
+        {
+           if ((*d++ = *s++) == '\0')
+              break;
+        }
+
+   /* Not enough room in dst, add NUL and traverse rest of src */
+   if (n == 0)
+     {
+        if (siz != 0)
+           *d = '\0';  /* NUL-terminate dst */
+
+        while (*s++)
+           ;
+     }
+
+   return(s - src - 1); /* count does not include NUL */
+#endif
+}
+
+/**
+ * @brief Append a c-string.
+ *
+ * @param dst The destination string.
+ * @param src The source string.
+ * @param siz The size of the destination string.
+ * @return The length of the source string plus MIN(siz, strlen(initial dst))
+ *
+ * This function appends @p src to @p dst of size @p siz (unlike
+ * strncat, @p siz is the full size of @p dst, not space left).  At
+ * most @p siz - 1 characters will be copied.  Always NUL terminates
+ * (unless @p siz <= strlen(dst)). This function returns strlen(src) +
+ * MIN(siz, strlen(initial dst)). If the returned value is greater or
+ * equal than @p siz, truncation occurred.
+ */
+EAPI size_t
+eina_strlcat(char *dst, const char *src, size_t siz)
+{
+   char *d = dst;
+   const char *s = src;
+   size_t n = siz;
+   size_t dlen;
+
+   /* Find the end of dst and adjust bytes left but don't go past end */
+   while (n-- != 0 && *d != '\0')
+      d++;
+   dlen = d - dst;
+   n = siz - dlen;
+
+   if (n == 0)
+      return(dlen + strlen(s));
+
+   while (*s != '\0') {
+        if (n != 1)
+          {
+             *d++ = *s;
+             n--;
+          }
+
+        s++;
+     }
+   *d = '\0';
+
+   return(dlen + (s - src)); /* count does not include NUL */
+}
+
+/**
+ * @brief Check if the given string has the given prefix.
+ *
+ * @param str The string to work with.
+ * @param prefix The prefix to check for.
+ * @return #EINA_TRUE if the string has the given prefix, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if @p str has the prefix
+ * @p prefix, #EINA_FALSE otherwise. If the length of @p prefix is
+ * greater than @p str, #EINA_FALSE is returned.
+ */
+EAPI Eina_Bool
+eina_str_has_prefix(const char *str, const char *prefix)
+{
+   size_t str_len;
+   size_t prefix_len;
+
+   str_len = strlen(str);
+   prefix_len = eina_strlen_bounded(prefix, str_len);
+   if (prefix_len == (size_t)-1)
+      return EINA_FALSE;
+
+   return (strncmp(str, prefix, prefix_len) == 0);
+}
+
+/**
+ * @brief Check if the given string has the given suffix.
+ *
+ * @param str The string to work with.
+ * @param suffix The suffix to check for.
+ * @return #EINA_TRUE if the string has the given suffix, #EINA_FALSE otherwise.
+ *
+ * This function returns #EINA_TRUE if @p str has the suffix
+ * @p suffix, #EINA_FALSE otherwise. If the length of @p suffix is
+ * greater than @p str, #EINA_FALSE is returned.
+ */
+/**
+ * @param str the string to work with
+ * @param suffix the suffix to check for
+ * @return true if str has the given suffix
+ * @brief checks if the string has the given suffix
+ */
+EAPI Eina_Bool
+eina_str_has_suffix(const char *str, const char *suffix)
+{
+   return eina_str_has_suffix_helper(str, suffix, strcmp);
+}
+
+/**
+ * @brief Check if the given string has the given suffix.
+ *
+ * @param str The string to work with.
+ * @param ext The  extension to check for.
+ * @return #EINA_TRUE if the string has the given extension, #EINA_FALSE otherwise.
+ *
+ * This function does the same like eina_str_has_suffix(), but with a
+ * case insensitive compare.
+ */
+EAPI Eina_Bool
+eina_str_has_extension(const char *str, const char *ext)
+{
+   return eina_str_has_suffix_helper(str, ext, strcasecmp);
+}
+
+/**
+ * @brief Split a string using a delimiter and returns number of elements.
+ *
+ * @param str The string to split.
+ * @param delim The string which specifies the places at which to split the string.
+ * @param max_tokens The maximum number of strings to split string into.
+ * @param elements Where to return the number of elements in returned
+ *        array (not counting the terminating @c NULL). May be @c NULL.
+ * @return A newly-allocated NULL-terminated array of strings or NULL if it
+ * fails to allocate the array.
+ *
+ * This functin splits @p str into a maximum of @p max_tokens pieces,
+ * using the given delimiter @p delim. @p delim is not included in any
+ * of the resulting strings, unless @p max_tokens is reached. If
+ * @p max_tokens is less than @c 1, the string is splitted completely. If
+ * @p max_tokens is reached, the last string in the returned string
+ * array contains the remainder of string. The returned value is a
+ * newly allocated NULL-terminated array of strings or NULL if it fails to
+ * allocate the array. To free it, free the first element of the array and the
+ * array itself.
+ *
+ * @see eina_str_split()
+ */
+EAPI char **
+eina_str_split_full(const char *str,
+                    const char *delim,
+                    int max_tokens,
+                    unsigned int *elements)
+{
+   return eina_str_split_full_helper(str, delim, max_tokens, elements);
+}
+
+
+/**
+ * @brief Split a string using a delimiter.
+ *
+ * @param str The string to split.
+ * @param delim The string which specifies the places at which to split the string.
+ * @param max_tokens The maximum number of strings to split string into.
+ * @return A newly-allocated NULL-terminated array of strings or NULL if it
+ * fails to allocate the array.
+ *
+ * This functin splits @p str into a maximum of @p max_tokens pieces,
+ * using the given delimiter @p delim. @p delim is not included in any
+ * of the resulting strings, unless @p max_tokens is reached. If
+ * @p max_tokens is less than @c 1, the string is splitted completely. If
+ * @p max_tokens is reached, the last string in the returned string
+ * array contains the remainder of string. The returned value is a
+ * newly allocated NULL-terminated array of strings or NULL if it fails to
+ * allocate the array. To free it, free the first element of the array and the
+ * array itself.
+ */
+EAPI char **
+eina_str_split(const char *str, const char *delim, int max_tokens)
+{
+   return eina_str_split_full_helper(str, delim, max_tokens, NULL);
+}
+
+/**
+ * @brief Join two strings of known length.
+ *
+ * @param dst The buffer to store the result.
+ * @param size Size (in byte) of the buffer.
+ * @param sep The separator character to use.
+ * @param a First string to use, before @p sep.
+ * @param a_len length of @p a.
+ * @param b Second string to use, after @p sep.
+ * @param b_len length of @p b.
+ * @return The number of characters printed.
+ *
+ * This function joins the strings @p a and @p b (in that order) and
+ * separate them with @p sep. The result is stored in the buffer
+ * @p dst and at most @p size - 1 characters will be written and the
+ * string is NULL-terminated. @p a_len is the length of @p a (not
+ * including '\\0') and @p b_len is the length of @p b (not including
+ * '\\0'). This function returns the number of characters printed (not
+ * including the trailing '\\0' used to end output to strings). Just
+ * like snprintf(), it will not write more than @p size bytes, thus a
+ * returned value of @p size or more means that the output was
+ * truncated.
+ *
+ * @see eina_str_join()
+ * @see eina_str_join_static()
+ */
+EAPI size_t
+eina_str_join_len(char *dst,
+                  size_t size,
+                  char sep,
+                  const char *a,
+                  size_t a_len,
+                  const char *b,
+                  size_t b_len)
+{
+   size_t ret = a_len + b_len + 1;
+   size_t off;
+
+   if (size < 1)
+      return ret;
+
+   if (size <= a_len)
+     {
+        memcpy(dst, a, size - 1);
+        dst[size - 1] = '\0';
+        return ret;
+     }
+
+        memcpy(dst, a, a_len);
+   off = a_len;
+
+   if (size <= off + 1)
+     {
+        dst[size - 1] = '\0';
+        return ret;
+     }
+
+   dst[off] = sep;
+   off++;
+
+   if (size <= off + b_len + 1)
+     {
+        memcpy(dst + off, b, size - off - 1);
+        dst[size - 1] = '\0';
+        return ret;
+     }
+
+        memcpy(dst + off, b, b_len);
+   dst[off + b_len] = '\0';
+   return ret;
+}
+
+/**
+ * @brief Use iconv to convert a text string from one encoding to another
+ *
+ * @param enc_from encoding to convert from
+ * @param enc_to   encoding to convert to
+ * @param text     text to convert
+ *
+ */
+#ifdef HAVE_ICONV
+EAPI char *
+eina_str_convert(const char *enc_from, const char *enc_to, const char *text)
+{
+   iconv_t ic;
+   char *new_txt, *inp, *outp;
+   size_t inb, outb, outlen, tob, outalloc;
+
+   if (!text)
+      return NULL;
+
+   ic = iconv_open(enc_to, enc_from);
+   if (ic == (iconv_t)(-1))
+      return NULL;
+
+   new_txt = malloc(64);
+   inb = strlen(text);
+   outb = 64;
+   inp = (char *)text;
+   outp = new_txt;
+   outalloc = 64;
+   outlen = 0;
+
+   for (;; )
+     {
+        size_t count;
+
+        tob = outb;
+        count = iconv(ic, &inp, &inb, &outp, &outb);
+        outlen += tob - outb;
+        if (count == (size_t)(-1))
+          {
+             if (errno == E2BIG)
+               {
+                  new_txt = realloc(new_txt, outalloc + 64);
+                  outp = new_txt + outlen;
+                  outalloc += 64;
+                  outb += 64;
+               }
+             else if (errno == EILSEQ)
+               {
+                  if (new_txt)
+                     free(new_txt);
+
+                  new_txt = NULL;
+                  break;
+               }
+             else if (errno == EINVAL)
+               {
+                  if (new_txt)
+                     free(new_txt);
+
+                  new_txt = NULL;
+                  break;
+               }
+             else
+               {
+                  if (new_txt)
+                     free(new_txt);
+
+                  new_txt = NULL;
+                  break;
+               }
+          }
+
+        if (inb == 0)
+          {
+             if (outalloc == outlen)
+                new_txt = realloc(new_txt, outalloc + 1);
+
+             new_txt[outlen] = 0;
+             break;
+          }
+     }
+   iconv_close(ic);
+   return new_txt;
+}
+#else
+EAPI char *
+eina_str_convert(const char *enc_from __UNUSED__,
+                 const char *enc_to __UNUSED__,
+                 const char *text __UNUSED__)
+{
+   return NULL;
+}
+#endif
+
+/**
+ * @brief Put a \ before and Space( ), \ or ' in a string.
+ *
+ * @param str the string to escape
+ *
+ * A newly allocated string is returned.
+ */
+EAPI char *
+eina_str_escape(const char *str)
+{
+   char *s2, *d;
+   const char *s;
+
+   s2 = malloc((strlen(str) * 2) + 1);
+   if (!s2)
+      return NULL;
+
+   for (s = str, d = s2; *s != 0; s++, d++)
+     {
+        if ((*s == ' ') || (*s == '\\') || (*s == '\''))
+          {
+             *d = '\\';
+             d++;
+          }
+
+        *d = *s;
+     }
+   *d = 0;
+   return s2;
+}
+
+/**
+ * @brief Lowercase all the characters in range [A-Z] in the given string.
+ *
+ * @param str the string to lowercase
+ *
+ * This modifies the original string, changing all characters in [A-Z] to lowercase.
+ */
+EAPI void
+eina_str_tolower(char **str)
+{
+   char *p;
+   if ((!str) || (!(*str)))
+      return;
+
+   for (p = *str; (*p); p++)
+      *p = tolower((unsigned char )(*p));
+}
+
+/**
+ * @brief Uppercase all the characters in range [a-z] in the given string.
+ *
+ * @param str the string to uppercase
+ *
+ * This modifies the original string, changing all characters in [a-z] to uppercase.
+ */
+EAPI void
+eina_str_toupper(char **str)
+{
+   char *p;
+   if ((!str) || (!(*str)))
+      return;
+
+   for (p = *str; (*p); p++)
+      *p = toupper((unsigned char)(*p));
+}
+
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_strbuf.c b/tests/suite/ecore/src/lib/eina_strbuf.c
new file mode 100644 (file)
index 0000000..3968af5
--- /dev/null
@@ -0,0 +1,180 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_private.h"
+#include "eina_str.h"
+#include "eina_strbuf_common.h"
+#include "eina_unicode.h"
+
+/*============================================================================*
+ *                                  Local                                     *
+ *============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#ifdef _STRBUF_DATA_TYPE
+# undef _STRBUF_DATA_TYPE
+#endif
+
+#ifdef _STRBUF_CSIZE
+# undef _STRBUF_CSIZE
+#endif
+
+#ifdef _STRBUF_STRUCT_NAME
+# undef _STRBUF_STRUCT_NAME
+#endif
+
+#ifdef _STRBUF_STRLEN_FUNC
+# undef _STRBUF_STRLEN_FUNC
+#endif
+
+#ifdef _STRBUF_STRESCAPE_FUNC
+# undef _STRBUF_STRESCAPE_FUNC
+#endif
+
+#ifdef _STRBUF_MAGIC
+# undef _STRBUF_MAGIC
+#endif
+
+#ifdef _STRBUF_MAGIC_STR
+# undef _STRBUF_MAGIC_STR
+#endif
+
+#ifdef _FUNC_EXPAND
+# undef _FUNC_EXPAND
+#endif
+
+
+#define _STRBUF_DATA_TYPE         char
+#define _STRBUF_CSIZE             sizeof(_STRBUF_DATA_TYPE)
+#define _STRBUF_STRUCT_NAME       Eina_Strbuf
+#define _STRBUF_STRLEN_FUNC(x)    strlen(x)
+#define _STRBUF_STRESCAPE_FUNC(x) eina_str_escape(x)
+#define _STRBUF_MAGIC             EINA_MAGIC_STRBUF
+#define _STRBUF_MAGIC_STR         __STRBUF_MAGIC_STR
+static const char __STRBUF_MAGIC_STR[] = "Eina Strbuf";
+
+#define _FUNC_EXPAND(y) eina_strbuf_ ## y
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+ *                                 Global                                     *
+ *============================================================================*/
+
+
+/*============================================================================*
+ *                                   API                                      *
+ *============================================================================*/
+
+
+/**
+ * @addtogroup Eina_String_Buffer_Group String Buffer
+ *
+ * @brief These functions provide string buffers management.
+ *
+ * The String Buffer data type is designed to be a mutable string,
+ * allowing to append, prepend or insert a string to a buffer.
+ *
+ * @{
+ */
+
+EAPI Eina_Bool
+eina_strbuf_append_printf(Eina_Strbuf *buf, const char *fmt, ...)
+{
+   va_list args;
+   char *str;
+   size_t len;
+   Eina_Bool ret;
+
+   va_start(args, fmt);
+   len = vasprintf(&str, fmt, args);
+   va_end(args);
+
+   if (len <= 0 || !str)
+      return EINA_FALSE;
+
+   ret = eina_strbuf_append_length(buf, str, len);
+   free(str);
+   return ret;
+}
+
+EAPI Eina_Bool
+eina_strbuf_append_vprintf(Eina_Strbuf *buf, const char *fmt, va_list args)
+{
+   char *str;
+   size_t len;
+   Eina_Bool ret;
+
+   len = vasprintf(&str, fmt, args);
+
+   if (len <= 0 || !str)
+      return EINA_FALSE;
+
+   ret = eina_strbuf_append_length(buf, str, len);
+   free(str);
+   return ret;
+}
+
+EAPI Eina_Bool
+eina_strbuf_insert_printf(Eina_Strbuf *buf, const char *fmt, size_t pos, ...)
+{
+   va_list args;
+   char *str;
+   size_t len;
+   Eina_Bool ret;
+
+   va_start(args, pos);
+   len = vasprintf(&str, fmt, args);
+   va_end(args);
+
+   if (len <= 0 || !str)
+      return EINA_FALSE;
+
+   ret = eina_strbuf_insert(buf, str, pos);
+   free(str);
+   return ret;
+}
+
+EAPI Eina_Bool
+eina_strbuf_insert_vprintf(Eina_Strbuf *buf,
+                           const char *fmt,
+                           size_t pos,
+                           va_list args)
+{
+   char *str;
+   size_t len;
+   Eina_Bool ret;
+
+   len = vasprintf(&str, fmt, args);
+
+   if (len <= 0 || !str)
+      return EINA_FALSE;
+
+   ret = eina_strbuf_insert(buf, str, pos);
+   free(str);
+   return ret;
+}
+
+/* Unicode */
+
+#include "eina_strbuf_template_c.x"
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_strbuf_common.c b/tests/suite/ecore/src/lib/eina_strbuf_common.c
new file mode 100644 (file)
index 0000000..c96e701
--- /dev/null
@@ -0,0 +1,862 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+# include <Evil.h>
+#endif
+
+#include "eina_private.h"
+#include "eina_str.h"
+#include "eina_magic.h"
+#include "eina_error.h"
+#include "eina_safety_checks.h"
+#include "eina_strbuf.h"
+#include "eina_strbuf_common.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#define EINA_STRBUF_INIT_SIZE 32
+#define EINA_STRBUF_INIT_STEP 32
+#define EINA_STRBUF_MAX_STEP 4096
+
+/**
+ * @endcond
+ */
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the strbuf module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the strbuf module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_strbuf_common_init(void)
+{
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * @brief Shut down the strbuf module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the strbuf module set up by
+ * eina_strbuf_common_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_strbuf_common_shutdown(void)
+{
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ *
+ * init the buffer
+ * @param buf the buffer to init
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ */
+static Eina_Bool
+_eina_strbuf_common_init(size_t csize, Eina_Strbuf *buf)
+{
+   buf->len = 0;
+   buf->size = EINA_STRBUF_INIT_SIZE;
+   buf->step = EINA_STRBUF_INIT_STEP;
+
+        eina_error_set(0);
+   buf->buf = calloc(csize, buf->size);
+   if (EINA_UNLIKELY(!buf->buf))
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ *
+ * resize the buffer
+ * @param buf the buffer to resize
+ * @param size the minimum size of the buffer
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ */
+static inline Eina_Bool
+_eina_strbuf_common_resize(size_t csize, Eina_Strbuf *buf, size_t size)
+{
+   size_t new_size, new_step, delta;
+   void *buffer;
+
+   size += 1; // Add extra space for '\0'
+
+   if (size == buf->size)
+      /* nothing to do */
+      return EINA_TRUE;
+   else if (size > buf->size)
+      delta = size - buf->size;
+   else
+      delta = buf->size - size;
+
+   /* check if should keep the same step (just used while growing) */
+   if ((delta <= buf->step) && (size > buf->size))
+      new_step = buf->step;
+   else
+     {
+        new_step = (((delta / EINA_STRBUF_INIT_STEP) + 1)
+                    * EINA_STRBUF_INIT_STEP);
+
+        if (new_step > EINA_STRBUF_MAX_STEP)
+           new_step = EINA_STRBUF_MAX_STEP;
+     }
+
+   new_size = (((size / new_step) + 1) * new_step);
+
+   /* reallocate the buffer to the new size */
+   buffer = realloc(buf->buf, new_size * csize);
+   if (EINA_UNLIKELY(!buffer))
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return EINA_FALSE;
+     }
+
+   buf->buf = buffer;
+   buf->size = new_size;
+   buf->step = new_step;
+        eina_error_set(0);
+   return EINA_TRUE;
+}
+
+/**
+ * @internal
+ *
+ * If required, enlarge the buffer to fit the new size.
+ *
+ * @param buf the buffer to resize
+ * @param size the minimum size of the buffer
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ */
+Eina_Bool
+_eina_strbuf_common_grow(size_t csize, Eina_Strbuf *buf, size_t size)
+{
+   if ((size + 1) < buf->size)
+      return EINA_TRUE;
+
+   return _eina_strbuf_common_resize(csize, buf, size);
+}
+
+/**
+ * @internal
+ *
+ * insert string of known length at random within existing strbuf limits.
+ *
+ * @param buf the buffer to resize, must be valid.
+ * @param str the string to copy, must be valid (!NULL and smaller than @a len)
+ * @param len the amount of bytes in @a str to copy, must be valid.
+ * @param pos the position inside buffer to insert, must be valid (smaller
+ *        than eina_strbuf_common_length_get())
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ */
+static inline Eina_Bool
+_eina_strbuf_common_insert_length(size_t csize,
+                                  Eina_Strbuf *buf,
+                                  const void *str,
+                                  size_t len,
+                                  size_t pos)
+{
+   if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len)))
+      return EINA_FALSE;
+
+   /* move the existing text */
+   memmove(buf->buf + ((len + pos) * csize), buf->buf + (pos * csize),
+           (buf->len - pos) * csize);
+
+   /* and now insert the given string */
+   memcpy(buf->buf + (pos * csize), str, len * csize);
+
+   buf->len += len;
+   memset(buf->buf + (buf->len * csize), 0, csize);
+   return EINA_TRUE;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @brief Create a new string buffer.
+ *
+ * @return Newly allocated string buffer instance.
+ *
+ * This function creates a new string buffer. On error, @c NULL is
+ * returned and Eina error is set to #EINA_ERROR_OUT_OF_MEMORY. To
+ * free the resources, use eina_strbuf_common_free().
+ *
+ * @see eina_strbuf_common_free()
+ * @see eina_strbuf_common_append()
+ * @see eina_strbuf_common_string_get()
+ */
+Eina_Strbuf *
+eina_strbuf_common_new(size_t csize)
+{
+   Eina_Strbuf *buf;
+
+        eina_error_set(0);
+   buf = malloc(sizeof(Eina_Strbuf));
+   if (EINA_UNLIKELY(!buf))
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   if (EINA_UNLIKELY(!_eina_strbuf_common_init(csize, buf)))
+     {
+        eina_strbuf_common_free(buf);
+        return NULL;
+     }
+
+   return buf;
+}
+
+/**
+ * @brief Free a string buffer.
+ *
+ * @param buf The string buffer to free.
+ *
+ * This function frees the memory of @p buf. @p buf must have been
+ * created by eina_strbuf_common_new().
+ */
+void
+eina_strbuf_common_free(Eina_Strbuf *buf)
+{
+   free(buf->buf);
+   free(buf);
+}
+
+/**
+ * @brief Reset a string buffer.
+ *
+ * @param buf The string buffer to reset.
+ *
+ * This function reset @p buf: the buffer len is set to 0, and the
+ * string is set to '\\0'. No memory is free'd.
+ */
+void
+eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf)
+{
+   buf->len = 0;
+   buf->step = EINA_STRBUF_INIT_STEP;
+
+   memset(buf->buf, 0, csize);
+}
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends @p str to @p buf. It computes the length of
+ * @p str, so is slightly slower than eina_strbuf_common_append_length(). If
+ * the length is known beforehand, consider using that variant. If
+ * @p buf can't append it, #EINA_FALSE is returned, otherwise
+ * #EINA_TRUE is returned.
+ *
+ * @see eina_strbuf_common_append()
+ * @see eina_strbuf_common_append_length()
+ */
+Eina_Bool
+eina_strbuf_common_append(size_t csize,
+                          Eina_Strbuf *buf,
+                          const void *str,
+                          size_t len)
+{
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
+
+   if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len)))
+      return EINA_FALSE;
+
+   memcpy(buf->buf + (buf->len * csize), str, (len + 1) * csize);
+   buf->len += len;
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Append a string to a buffer, reallocating as necessary,
+ * limited by the given length.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @param maxlen The maximum number of characters to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends at most @p maxlen characters of @p str to
+ * @p buf. It can't appends more than the length of @p str. It
+ * computes the length of @p str, so is slightly slower than
+ * eina_strbuf_common_append_length(). If the length is known beforehand,
+ * consider using that variant (@p maxlen should then be checked so
+ * that it is greater than the size of @p str). If @p str can not be
+ * appended, #EINA_FALSE is returned, otherwise, #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_strbuf_common_append()
+ * @see eina_strbuf_common_append_length()
+ */
+Eina_Bool
+eina_strbuf_common_append_n(size_t csize,
+                            Eina_Strbuf *buf,
+                            const void *str,
+                            size_t len,
+                            size_t maxlen)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
+
+   if (len > maxlen)
+      len = maxlen;
+
+   if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + len)))
+      return EINA_FALSE;
+
+   memcpy(buf->buf + (buf->len * csize), str, len * csize);
+   buf->len += len;
+   memset(buf->buf + (buf->len * csize), 0, csize);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Append a string of exact length to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param str The string to append.
+ * @param length The exact length to use.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function appends @p str to @p buf. @p str must be of size at
+ * most @p length. It is slightly faster than eina_strbuf_common_append() as
+ * it does not compute the size of @p str. It is useful when dealing
+ * with strings of known size, such as eina_strngshare. If @p buf
+ * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_stringshare_length()
+ * @see eina_strbuf_common_append()
+ * @see eina_strbuf_common_append_n()
+ */
+Eina_Bool
+eina_strbuf_common_append_length(size_t csize,
+                                 Eina_Strbuf *buf,
+                                 const void *str,
+                                 size_t length)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
+
+   if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + length)))
+      return EINA_FALSE;
+
+   memcpy(buf->buf + (buf->len * csize), str, length * csize);
+   buf->len += length;
+   memset(buf->buf + (buf->len * csize), 0, csize);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert.
+ * @param str The string to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str to @p buf at position @p pos. It
+ * computes the length of @p str, so is slightly slower than
+ * eina_strbuf_common_insert_length(). If  the length is known beforehand,
+ * consider using that variant. If @p buf can't insert it, #EINA_FALSE
+ * is returned, otherwise #EINA_TRUE is returned.
+ */
+Eina_Bool
+eina_strbuf_common_insert(size_t csize,
+                          Eina_Strbuf *buf,
+                          const void *str,
+                          size_t len,
+                          size_t pos)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
+
+   if (pos >= buf->len)
+      return eina_strbuf_common_append(csize, buf, str, len);
+
+   return _eina_strbuf_common_insert_length(csize, buf, str, len, pos);
+}
+
+/**
+ * @brief Insert a string to a buffer, reallocating as necessary. Limited by maxlen.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param maxlen The maximum number of chars to insert.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str ot @p buf at position @p pos, with at
+ * most @p maxlen bytes. The number of inserted characters can not be
+ * greater than the length of @p str. It computes the length of
+ * @p str, so is slightly slower than eina_strbuf_common_insert_length(). If the
+ * length is known beforehand, consider using that variant (@p maxlen
+ * should then be checked so that it is greater than the size of
+ * @p str). If @p str can not be inserted, #EINA_FALSE is returned,
+ * otherwise, #EINA_TRUE is returned.
+ */
+Eina_Bool
+eina_strbuf_common_insert_n(size_t csize,
+                            Eina_Strbuf *buf,
+                            const void *str,
+                            size_t len,
+                            size_t maxlen,
+                            size_t pos)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
+
+   if (pos >= buf->len)
+      return eina_strbuf_common_append_n(csize, buf, str, len, maxlen);
+
+   if (len > maxlen)
+      len = maxlen;
+
+   return _eina_strbuf_common_insert_length(csize, buf, str, len, pos);
+}
+
+/**
+ * @brief Insert a string of exact length to a buffer, reallocating as necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param str The string to insert.
+ * @param length The exact length to use.
+ * @param pos The position to insert the string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p str to @p buf. @p str must be of size at
+ * most @p length. It is slightly faster than eina_strbuf_common_insert() as
+ * it does not compute the size of @p str. It is useful when dealing
+ * with strings of known size, such as eina_strngshare. If @p buf
+ * can't insert it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ *
+ * @see eina_stringshare_length()
+ * @see eina_strbuf_common_insert()
+ * @see eina_strbuf_common_insert_n()
+ */
+Eina_Bool
+eina_strbuf_common_insert_length(size_t csize,
+                                 Eina_Strbuf *buf,
+                                 const void *str,
+                                 size_t length,
+                                 size_t pos)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(str, EINA_FALSE);
+
+   if (pos >= buf->len)
+      return eina_strbuf_common_append_length(csize, buf, str, length);
+
+   return _eina_strbuf_common_insert_length(csize, buf, str, length, pos);
+}
+
+/**
+ * @brief Append a character to a string buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to append to.
+ * @param c The char to append.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p c to @p buf. If it can not insert it,
+ * #EINA_FALSE is returned, otherwise #EINA_TRUE is returned.
+ */
+Eina_Bool
+eina_strbuf_common_append_char(size_t csize, Eina_Strbuf *buf, const void *c)
+{
+
+   if (EINA_UNLIKELY(!_eina_strbuf_common_grow(csize, buf, buf->len + 1)))
+      return EINA_FALSE;
+
+   memcpy(buf->buf + ((buf->len)++ *csize), c, csize);
+   memset(buf->buf + (buf->len * csize), 0, csize);
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Insert a character to a string buffer, reallocating as
+ * necessary.
+ *
+ * @param buf The string buffer to insert to.
+ * @param c The char to insert.
+ * @param pos The position to insert the char.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function inserts @p c to @p buf at position @p pos. If @p buf
+ * can't append it, #EINA_FALSE is returned, otherwise #EINA_TRUE is
+ * returned.
+ */
+Eina_Bool
+eina_strbuf_common_insert_char(size_t csize,
+                               Eina_Strbuf *buf,
+                               const void *c,
+                               size_t pos)
+{
+
+   if (pos >= buf->len)
+      return eina_strbuf_common_append_char(csize, buf, c);
+
+   return _eina_strbuf_common_insert_length(csize, buf, c, 1, pos);
+}
+
+/**
+ * @brief Remove a slice of the given string buffer.
+ *
+ * @param buf The string buffer to remove a slice.
+ * @param start The initial (inclusive) slice position to start
+ *        removing, in bytes.
+ * @param end The final (non-inclusive) slice position to finish
+ *        removing, in bytes.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function removes a slice of @p buf, starting at @p start
+ * (inclusive) and ending at @p end (non-inclusive). Both values are
+ * in bytes. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise.
+ */
+Eina_Bool
+eina_strbuf_common_remove(size_t csize,
+                          Eina_Strbuf *buf,
+                          size_t start,
+                          size_t end)
+{
+   size_t remove_len, tail_len;
+
+   if (end >= buf->len)
+      end = buf->len;
+
+   if (end <= start)
+      return EINA_TRUE;
+
+   remove_len = end - start;
+   if (remove_len == buf->len)
+     {
+        free(buf->buf);
+        return _eina_strbuf_common_init(csize, buf);
+     }
+
+   tail_len = buf->len - end + 1; /* includes '\0' */
+   memmove(buf->buf + (start * csize),
+           buf->buf + (end * csize),
+           tail_len * csize);
+   buf->len -= remove_len;
+   return _eina_strbuf_common_resize(csize, buf, buf->len);
+}
+
+/**
+ * @brief Retrieve a pointer to the contents of a string buffer
+ *
+ * @param buf The string buffer.
+ * @return The current string in the string buffer.
+ *
+ * This function returns the string contained in @p buf. The returned
+ * value must not be modified and will no longer be valid if @p buf is
+ * modified. In other words, any eina_strbuf_common_append() or similar will
+ * make that pointer invalid.
+ *
+ * @see eina_strbuf_common_string_steal()
+ */
+const void *
+eina_strbuf_common_string_get(const Eina_Strbuf *buf)
+{
+   return buf->buf;
+}
+
+/**
+ * @brief Steal the contents of a string buffer.
+ *
+ * @param buf The string buffer to steal.
+ * @return The current string in the string buffer.
+ *
+ * This function returns the string contained in @p buf. @p buf is
+ * then initialized and does not own the returned string anymore. The
+ * caller must release the memory of the returned string by calling
+ * free().
+ *
+ * @see eina_strbuf_common_string_get()
+ */
+void *
+eina_strbuf_common_string_steal(size_t csize, Eina_Strbuf *buf)
+{
+   void *ret;
+
+   ret = buf->buf;
+   // TODO: Check return value and do something clever
+   _eina_strbuf_common_init(csize, buf);
+   return ret;
+}
+
+/**
+ * @brief Free the contents of a string buffer but not the buffer.
+ *
+ * @param buf The string buffer to free the string of.
+ *
+ * This function frees the string contained in @p buf without freeing
+ * @p buf.
+ */
+void
+eina_strbuf_common_string_free(size_t csize, Eina_Strbuf *buf)
+{
+   free(buf->buf);
+   _eina_strbuf_common_init(csize, buf);
+}
+
+/**
+ * @brief Retrieve the length of the string buffer content.
+ *
+ * @param buf The string buffer.
+ * @return The current length of the string, in bytes.
+ *
+ * This function returns the length of @p buf.
+ */
+size_t
+eina_strbuf_common_length_get(const Eina_Strbuf *buf)
+{
+   return buf->len;
+}
+
+/**
+ * @addtogroup Eina_String_Buffer_Group String Buffer
+ *
+ * @brief These functions provide string buffers management.
+ *
+ * The String Buffer data type is designed to be a mutable string,
+ * allowing to append, prepend or insert a string to a buffer.
+ *
+ * @{
+ */
+
+
+/**
+ * @cond LOCAL
+ */
+
+/*FIXME: Implementing them here is a hack! */
+
+#ifdef _STRBUF_CSIZE
+# undef _STRBUF_CSIZE
+#endif
+
+#ifdef _STRBUF_MAGIC
+# undef _STRBUF_MAGIC
+#endif
+
+#ifdef _STRBUF_MAGIC_STR
+# undef _STRBUF_MAGIC_STR
+#endif
+
+#define _STRBUF_CSIZE 1
+#define _STRBUF_MAGIC             EINA_MAGIC_STRBUF
+#define _STRBUF_MAGIC_STR         __STRBUF_STR_MAGIC_STR
+static const char __STRBUF_STR_MAGIC_STR[] = "Eina Strbuf";
+
+
+/**
+ * @endcond
+ */
+
+
+/**
+ * @brief Replace the n-th string with an other string.
+ *
+ * @param buf The string buffer to work with.
+ * @param str The string to replace.
+ * @param with The replaceing string.
+ * @param n The number of the fitting string.
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function replaces the n-th occurrence of @p str in @p buf with
+ * @p with. It returns #EINA_FALSE on failure, #EINA_TRUE otherwise.
+ */
+EAPI Eina_Bool
+eina_strbuf_replace(Eina_Strbuf *buf,
+                    const char *str,
+                    const char *with,
+                    unsigned int n)
+{
+   size_t len1, len2;
+   char *spos;
+   size_t pos;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL( str, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(with, EINA_FALSE);
+   EINA_MAGIC_CHECK_STRBUF(buf, 0);
+
+   if (n == 0)
+      return EINA_FALSE;
+
+   spos = buf->buf;
+   while (n--)
+     {
+        spos = strstr(spos, str);
+        if (!spos || *spos == '\0')
+           return EINA_FALSE;
+
+        if (n)
+           spos++;
+     }
+
+   pos = spos - (const char *)buf->buf;
+   len1 = strlen(str);
+   len2 = strlen(with);
+   if (len1 != len2)
+     {
+        /* resize the buffer if necessary */
+        if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf,
+                                                    buf->len - len1 + len2)))
+          {
+             return EINA_FALSE; /* move the existing text */
+
+          }
+
+        memmove(buf->buf + pos + len2, buf->buf + pos + len1,
+                buf->len - pos - len1);
+     }
+
+   /* and now insert the given string */
+   memcpy(buf->buf + pos, with, len2);
+   buf->len += len2 - len1;
+   memset((char *)buf->buf + buf->len, 0, 1);
+
+   return EINA_TRUE;
+}
+
+/**
+ * @brief Replace all strings with an other string.
+
+ * @param buf the string buffer to work with.
+ * @param str The string to replace.
+ * @param with The replaceing string.
+ * @return How often the string was replaced.
+ *
+ * This function replaces all the occurrences of @p str in @ buf with
+ * the string @p with. This function returns the number of times @p str
+ * has been replaced. On failure, it returns 0.
+ */
+EAPI int
+eina_strbuf_replace_all(Eina_Strbuf *buf, const char *str, const char *with)
+{
+   size_t len1, len2, len;
+   char *tmp_buf = NULL;
+   char *spos;
+   size_t pos, start;
+   size_t pos_tmp, start_tmp;
+   int n = 0;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL( str, 0);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(with, 0);
+   EINA_MAGIC_CHECK_STRBUF(buf, 0);
+
+   spos = strstr(buf->buf, str);
+   if (!spos || *spos == '\0')
+      return 0;
+
+   len1 = strlen(str);
+   len2 = strlen(with);
+
+   /* if the size of the two string is equal, it is fairly easy to replace them
+    * we don't need to resize the buffer or doing other calculations */
+   if (len1 == len2)
+     {
+        while (spos)
+          {
+             memcpy(spos, with, len2);
+             spos = strstr(spos + len2, str);
+             n++;
+          }
+        return n;
+     }
+
+   pos = pos_tmp = spos - (const char *)buf->buf;
+   tmp_buf = buf->buf;
+   buf->buf = malloc(buf->size);
+   if (EINA_UNLIKELY(!buf->buf))
+     {
+        buf->buf = tmp_buf;
+        return 0;
+     }
+
+   start = start_tmp = 0;
+   len = buf->len;
+
+   while (spos)
+     {
+        n++;
+        len = (len + len2) - len1;
+        /* resize the buffer if necessary */
+        if (EINA_UNLIKELY(!_eina_strbuf_common_grow(_STRBUF_CSIZE, buf, len)))
+          {
+             /* we have to stop replacing here, because we haven't enough
+              * memory to go on */
+             len = (len + len1) - len2;
+             break;
+          }
+
+        /* copy the untouched text */
+             memcpy(buf->buf + start, tmp_buf + start_tmp, pos - start);
+        /* copy the new string */
+             memcpy(buf->buf + pos,   with,                len2);
+
+        /* calculate the next positions */
+        start_tmp = pos_tmp + len1;
+        start = pos + len2;
+        spos = strstr(tmp_buf + start_tmp, str);
+        /* this calculations don't make sense if spos == NULL, but the
+         * calculated values won't be used, because the loop will stop
+         * then */
+        pos_tmp = spos - tmp_buf;
+        pos = start + pos_tmp - start_tmp;
+     }
+   /* and now copy the rest of the text */
+             memcpy(buf->buf + start, tmp_buf + start_tmp, len - start);
+   buf->len = len;
+             memset((char *)buf->buf + buf->len, 0, 1);
+
+   free(tmp_buf);
+
+   return n;
+}
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_strbuf_common.h b/tests/suite/ecore/src/lib/eina_strbuf_common.h
new file mode 100644 (file)
index 0000000..3713f2c
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef EINA_STRBUF_COMMON_H
+#define EINA_STRBUF_COMMON_H
+
+#include <stdlib.h>
+
+#include "eina_private.h"
+#include "eina_magic.h"
+#include "eina_strbuf.h"
+
+struct _Eina_Strbuf
+{
+   void *buf;
+   size_t len;
+   size_t size;
+   size_t step;
+
+   EINA_MAGIC
+};
+
+#define EINA_MAGIC_CHECK_STRBUF(d, ...)                         \
+   do {                                                         \
+        if (!EINA_MAGIC_CHECK((d), _STRBUF_MAGIC))              \
+          {                                                     \
+             EINA_MAGIC_FAIL((d), _STRBUF_MAGIC);               \
+             return __VA_ARGS__;                                \
+          }                                                     \
+     } while (0)
+
+Eina_Bool
+eina_strbuf_common_init(void);
+
+Eina_Bool
+eina_strbuf_common_shutdown(void);
+Eina_Strbuf *
+eina_strbuf_common_new(size_t csize);
+void
+eina_strbuf_common_free(Eina_Strbuf *buf);
+void
+eina_strbuf_common_reset(size_t csize, Eina_Strbuf *buf);
+Eina_Bool
+eina_strbuf_common_append(size_t csize,
+                          Eina_Strbuf *buf,
+                          const void *str,
+                          size_t len);
+Eina_Bool
+eina_strbuf_common_append_escaped(size_t csize,
+                                  Eina_Strbuf *buf,
+                                  const void *str);
+Eina_Bool
+eina_strbuf_common_append_n(size_t csize,
+                            Eina_Strbuf *buf,
+                            const void *str,
+                            size_t len,
+                            size_t maxlen);
+Eina_Bool
+eina_strbuf_common_append_length(size_t csize,
+                                 Eina_Strbuf *buf,
+                                 const void *str,
+                                 size_t length);
+Eina_Bool
+eina_strbuf_common_insert(size_t csize,
+                          Eina_Strbuf *buf,
+                          const void *str,
+                          size_t len,
+                          size_t pos);
+Eina_Bool
+eina_strbuf_common_insert_escaped(size_t csize,
+                                  Eina_Strbuf *buf,
+                                  const void *str,
+                                  size_t len,
+                                  size_t pos);
+Eina_Bool
+eina_strbuf_common_insert_n(size_t csize,
+                            Eina_Strbuf *buf,
+                            const void *str,
+                            size_t len,
+                            size_t maxlen,
+                            size_t pos);
+Eina_Bool
+eina_strbuf_common_insert_length(size_t csize,
+                                 Eina_Strbuf *buf,
+                                 const void *str,
+                                 size_t length,
+                                 size_t pos);
+Eina_Bool
+eina_strbuf_common_append_char(size_t csize, Eina_Strbuf *buf, const void *c);
+Eina_Bool
+eina_strbuf_common_insert_char(size_t csize,
+                               Eina_Strbuf *buf,
+                               const void *c,
+                               size_t pos);
+Eina_Bool
+eina_strbuf_common_remove(size_t csize,
+                          Eina_Strbuf *buf,
+                          size_t start,
+                          size_t end);
+const void *
+eina_strbuf_common_string_get(const Eina_Strbuf *buf);
+void *
+eina_strbuf_common_string_steal(size_t csize, Eina_Strbuf *buf);
+void
+eina_strbuf_common_string_free(size_t csize, Eina_Strbuf *buf);
+size_t
+eina_strbuf_common_length_get(const Eina_Strbuf *buf);
+
+Eina_Bool
+_eina_strbuf_common_grow(size_t csize, Eina_Strbuf *buf, size_t size);
+/**
+ * @}
+ */
+
+#endif
diff --git a/tests/suite/ecore/src/lib/eina_strbuf_template_c.x b/tests/suite/ecore/src/lib/eina_strbuf_template_c.x
new file mode 100644 (file)
index 0000000..2f3aeae
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
+ */
+
+/* This file should be included from files implementing strbuf. The including file
+ * should define the following macros:
+ * _STRBUF_DATA_TYPE
+ * _STRBUF_CSIZE
+ * _STRBUF_STRUCT_NAME
+ * _STRBUF_STRLEN_FUNC(x)
+ * _STRBUF_STRESCAPE_FUNC(x)
+ * _STRBUF_STRSTR_FUNC(x, y)
+ * _STRBUF_MAGIC
+ * _STRBUF_MAGIC_STR
+ * See how it's done in eina_ustrbuf.c and eina_strbuf.c. This just makes things
+ * a lot easier since those are essentially the same just with different sizes.
+ */
+
+/*============================================================================*
+ *                                  Local                                     *
+ *============================================================================*/
+
+
+/*============================================================================*
+ *                                 Global                                     *
+ *============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the strbuf module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the strbuf module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+_FUNC_EXPAND(init)(void)
+{
+   eina_magic_string_static_set(_STRBUF_MAGIC, _STRBUF_MAGIC_STR);
+   return eina_strbuf_common_init();
+}
+
+/**
+ * @internal
+ * @brief Shut down the strbuf module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the strbuf module set up by
+ * eina_ustrbuf_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+_FUNC_EXPAND(shutdown)(void)
+{
+   return eina_strbuf_common_shutdown();
+}
+
+/*============================================================================*
+ *                                   API                                      *
+ *============================================================================*/
+
+EAPI _STRBUF_STRUCT_NAME *
+_FUNC_EXPAND(new)(void)
+{
+   _STRBUF_STRUCT_NAME *buf = eina_strbuf_common_new(_STRBUF_CSIZE);
+   EINA_MAGIC_SET(buf, _STRBUF_MAGIC);
+   return buf;
+}
+
+EAPI void
+_FUNC_EXPAND(free)(_STRBUF_STRUCT_NAME *buf)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf);
+   EINA_MAGIC_SET(buf, EINA_MAGIC_NONE);
+   eina_strbuf_common_free(buf);
+}
+
+EAPI void
+_FUNC_EXPAND(reset)(_STRBUF_STRUCT_NAME *buf)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf);
+   eina_strbuf_common_reset(_STRBUF_CSIZE, buf);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(append)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_append(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str));
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(append_escaped)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str)
+{
+   _STRBUF_DATA_TYPE *esc;
+   Eina_Bool ret;
+
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   esc = _STRBUF_STRESCAPE_FUNC(str);
+   if (!esc) {
+      return _FUNC_EXPAND(append)(buf, str);
+   }
+   ret = _FUNC_EXPAND(append)(buf, esc);
+   if (esc)
+      free(esc);
+
+   return ret;
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(append_n)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t maxlen)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_append_n(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str), maxlen);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(append_length)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t length)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_append_length(_STRBUF_CSIZE, buf, (const void *) str, length);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(insert)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t pos)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_insert(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str), pos);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(insert_escaped)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t pos)
+{
+   _STRBUF_DATA_TYPE *esc;
+   Eina_Bool ret;
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+
+   esc = _STRBUF_STRESCAPE_FUNC(str);
+   if (!esc) {
+      return _FUNC_EXPAND(insert)(buf, str, pos);
+   }
+   ret = _FUNC_EXPAND(insert)(buf, esc, pos);
+   if (esc)
+      free(esc);
+
+   return ret;
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(insert_n)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t maxlen, size_t pos)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_insert_n(_STRBUF_CSIZE, buf, (const void *) str, _STRBUF_STRLEN_FUNC(str), maxlen, pos);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(insert_length)(_STRBUF_STRUCT_NAME *buf, const _STRBUF_DATA_TYPE *str, size_t length, size_t pos)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_insert_length(_STRBUF_CSIZE, buf, (const void *) str, length, pos);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(append_char)(_STRBUF_STRUCT_NAME *buf, _STRBUF_DATA_TYPE c)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_append_char(_STRBUF_CSIZE, buf, (const void *) &c);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(insert_char)(_STRBUF_STRUCT_NAME *buf, _STRBUF_DATA_TYPE c, size_t pos)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_insert_char(_STRBUF_CSIZE, buf, (const void *) &c, pos);
+}
+
+EAPI Eina_Bool
+_FUNC_EXPAND(remove)(_STRBUF_STRUCT_NAME *buf, size_t start, size_t end)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, EINA_FALSE);
+   return eina_strbuf_common_remove(_STRBUF_CSIZE, buf, start, end);
+}
+
+EAPI const _STRBUF_DATA_TYPE *
+_FUNC_EXPAND(string_get)(const _STRBUF_STRUCT_NAME *buf)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, NULL);
+   return (const _STRBUF_DATA_TYPE *) eina_strbuf_common_string_get(buf);
+}
+
+EAPI _STRBUF_DATA_TYPE *
+_FUNC_EXPAND(string_steal)(_STRBUF_STRUCT_NAME *buf)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, NULL);
+   return (_STRBUF_DATA_TYPE *) eina_strbuf_common_string_steal(_STRBUF_CSIZE, buf);
+}
+
+
+EAPI void
+_FUNC_EXPAND(string_free)(_STRBUF_STRUCT_NAME *buf)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf);
+   eina_strbuf_common_string_free(_STRBUF_CSIZE, buf);
+}
+
+EAPI size_t
+_FUNC_EXPAND(length_get)(const _STRBUF_STRUCT_NAME *buf)
+{
+   EINA_MAGIC_CHECK_STRBUF(buf, 0);
+   return eina_strbuf_common_length_get(buf);
+}
diff --git a/tests/suite/ecore/src/lib/eina_stringshare.c b/tests/suite/ecore/src/lib/eina_stringshare.c
new file mode 100644 (file)
index 0000000..86b4617
--- /dev/null
@@ -0,0 +1,956 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2010
+ *                        Carsten Haitzler,
+ *                         Jorge Luis Zapata Muga,
+ *                         Cedric Bail,
+ *                         Gustavo Sverzut Barbieri
+ *                         Tom Hacohen
+ *                         Brett Nash
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page tutorial_stringshare_page Stringshare Tutorial
+ *
+ * to be written...
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# ifdef  __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef EFL_HAVE_POSIX_THREADS
+# include <pthread.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_hash.h"
+#include "eina_rbtree.h"
+#include "eina_error.h"
+#include "eina_log.h"
+#include "eina_stringshare.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+#include "eina_share_common.h"
+
+/* The actual share */
+static Eina_Share *stringshare_share;
+static const char EINA_MAGIC_STRINGSHARE_NODE_STR[] = "Eina Stringshare Node";
+
+#ifdef EFL_HAVE_THREADS
+extern Eina_Bool _share_common_threads_activated;
+
+# ifdef EFL_HAVE_POSIX_THREADS
+static pthread_mutex_t _mutex_small = PTHREAD_MUTEX_INITIALIZER;
+#  define STRINGSHARE_LOCK_SMALL() if(_share_common_threads_activated) \
+      pthread_mutex_lock(&_mutex_small)
+#  define STRINGSHARE_UNLOCK_SMALL() if(_share_common_threads_activated) \
+      pthread_mutex_unlock(&_mutex_small)
+# else /* EFL_HAVE_WIN32_THREADS */
+static HANDLE _mutex_small = NULL;
+#  define STRINGSHARE_LOCK_SMALL() if(_share_common_threads_activated) \
+      WaitForSingleObject(_mutex_small, INFINITE)
+#  define STRINGSHARE_UNLOCK_SMALL() if(_share_common_threads_activated) \
+      ReleaseMutex(_mutex_small)
+
+# endif /* EFL_HAVE_WIN32_THREADS */
+#else /* EFL_HAVE_THREADS */
+# define STRINGSHARE_LOCK_SMALL() do {} while (0)
+# define STRINGSHARE_UNLOCK_SMALL() do {} while (0)
+#endif
+
+/* Stringshare optimizations */
+static const unsigned char _eina_stringshare_single[512] = {
+   0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,
+   16,0,17,0,18,0,19,0,20,0,21,0,22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,
+   31,0,32,0,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0,
+   46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,57,0,58,0,59,0,60,0,
+   61,0,62,0,63,0,64,0,65,0,66,0,67,0,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0,
+   76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,
+   91,0,92,0,93,0,94,0,95,0,96,0,97,0,98,0,99,0,100,0,101,0,102,0,103,0,104,0,
+   105,0,
+   106,0,107,0,108,0,109,0,110,0,111,0,112,0,113,0,114,0,115,0,116,0,117,0,118,
+   0,119,0,120,0,
+   121,0,122,0,123,0,124,0,125,0,126,0,127,0,128,0,129,0,130,0,131,0,132,0,133,
+   0,134,0,135,0,
+   136,0,137,0,138,0,139,0,140,0,141,0,142,0,143,0,144,0,145,0,146,0,147,0,148,
+   0,149,0,150,0,
+   151,0,152,0,153,0,154,0,155,0,156,0,157,0,158,0,159,0,160,0,161,0,162,0,163,
+   0,164,0,165,0,
+   166,0,167,0,168,0,169,0,170,0,171,0,172,0,173,0,174,0,175,0,176,0,177,0,178,
+   0,179,0,180,0,
+   181,0,182,0,183,0,184,0,185,0,186,0,187,0,188,0,189,0,190,0,191,0,192,0,193,
+   0,194,0,195,0,
+   196,0,197,0,198,0,199,0,200,0,201,0,202,0,203,0,204,0,205,0,206,0,207,0,208,
+   0,209,0,210,0,
+   211,0,212,0,213,0,214,0,215,0,216,0,217,0,218,0,219,0,220,0,221,0,222,0,223,
+   0,224,0,225,0,
+   226,0,227,0,228,0,229,0,230,0,231,0,232,0,233,0,234,0,235,0,236,0,237,0,238,
+   0,239,0,240,0,
+   241,0,242,0,243,0,244,0,245,0,246,0,247,0,248,0,249,0,250,0,251,0,252,0,253,
+   0,254,0,255,0
+};
+
+typedef struct _Eina_Stringshare_Small Eina_Stringshare_Small;
+typedef struct _Eina_Stringshare_Small_Bucket Eina_Stringshare_Small_Bucket;
+
+struct _Eina_Stringshare_Small_Bucket
+{
+   /* separate arrays for faster lookups */
+   const char **strings;
+   unsigned char *lengths;
+   unsigned short *references;
+   int count;
+   int size;
+};
+
+struct _Eina_Stringshare_Small
+{
+   Eina_Stringshare_Small_Bucket *buckets[256];
+};
+
+#define EINA_STRINGSHARE_SMALL_BUCKET_STEP 8
+static Eina_Stringshare_Small _eina_small_share;
+
+static inline int
+_eina_stringshare_small_cmp(const Eina_Stringshare_Small_Bucket *bucket,
+                            int i,
+                            const char *pstr,
+                            unsigned char plength)
+{
+   /* pstr and plength are from second char and on, since the first is
+    * always the same.
+    *
+    * First string being always the same, size being between 2 and 3
+    * characters (there is a check for special case length==1 and then
+    * small stringshare is applied to strings < 4), we just need to
+    * compare 2 characters of both strings.
+    */
+   const unsigned char cur_plength = bucket->lengths[i] - 1;
+   const char *cur_pstr;
+
+   if (cur_plength > plength)
+      return 1;
+   else if (cur_plength < plength)
+      return -1;
+
+   cur_pstr = bucket->strings[i] + 1;
+
+   if (cur_pstr[0] > pstr[0])
+      return 1;
+   else if (cur_pstr[0] < pstr[0])
+      return -1;
+
+   if (plength == 1)
+      return 0;
+
+   if (cur_pstr[1] > pstr[1])
+      return 1;
+   else if (cur_pstr[1] < pstr[1])
+      return -1;
+
+   return 0;
+}
+
+static const char *
+_eina_stringshare_small_bucket_find(const Eina_Stringshare_Small_Bucket *bucket,
+                                    const char *str,
+                                    unsigned char length,
+                                    int *idx)
+{
+   const char *pstr = str + 1; /* skip first letter, it's always the same */
+   unsigned char plength = length - 1;
+   int i, low, high;
+
+   if (bucket->count == 0)
+     {
+        *idx = 0;
+        return NULL;
+     }
+
+   low = 0;
+   high = bucket->count;
+
+   while (low < high)
+     {
+        int r;
+
+        i = (low + high - 1) / 2;
+
+        r = _eina_stringshare_small_cmp(bucket, i, pstr, plength);
+        if (r > 0)
+           high = i;
+        else if (r < 0)
+           low = i + 1;
+        else
+          {
+             *idx = i;
+             return bucket->strings[i];
+          }
+     }
+
+   *idx = low;
+   return NULL;
+}
+
+static Eina_Bool
+_eina_stringshare_small_bucket_resize(Eina_Stringshare_Small_Bucket *bucket,
+                                      int size)
+{
+   void *tmp;
+
+   tmp = realloc((void *)bucket->strings, size * sizeof(bucket->strings[0]));
+   if (!tmp)
+     {
+             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return 0;
+     }
+
+   bucket->strings = tmp;
+
+   tmp = realloc(bucket->lengths, size * sizeof(bucket->lengths[0]));
+   if (!tmp)
+     {
+             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return 0;
+     }
+
+   bucket->lengths = tmp;
+
+   tmp = realloc(bucket->references, size * sizeof(bucket->references[0]));
+   if (!tmp)
+     {
+             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return 0;
+     }
+
+   bucket->references = tmp;
+
+   bucket->size = size;
+   return 1;
+}
+
+static const char *
+_eina_stringshare_small_bucket_insert_at(
+   Eina_Stringshare_Small_Bucket **p_bucket,
+   const char *str,
+   unsigned char length,
+   int idx)
+{
+   Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
+   int todo, off;
+   char *snew;
+
+   if (!bucket)
+     {
+        *p_bucket = bucket = calloc(1, sizeof(*bucket));
+        if (!bucket)
+          {
+             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+             return NULL;
+          }
+     }
+
+   if (bucket->count + 1 >= bucket->size)
+     {
+        int size = bucket->size + EINA_STRINGSHARE_SMALL_BUCKET_STEP;
+        if (!_eina_stringshare_small_bucket_resize(bucket, size))
+           return NULL;
+     }
+
+   snew = malloc(length + 1);
+   if (!snew)
+     {
+             eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   memcpy(snew, str, length);
+   snew[length] = '\0';
+
+   off = idx + 1;
+   todo = bucket->count - idx;
+   if (todo > 0)
+     {
+        memmove((void *)(bucket->strings + off), bucket->strings + idx,
+                todo * sizeof(bucket->strings[0]));
+        memmove(bucket->lengths + off,           bucket->lengths + idx,
+                todo * sizeof(bucket->lengths[0]));
+        memmove(bucket->references + off,        bucket->references + idx,
+                todo * sizeof(bucket->references[0]));
+     }
+
+   bucket->strings[idx] = snew;
+   bucket->lengths[idx] = length;
+   bucket->references[idx] = 1;
+   bucket->count++;
+
+   return snew;
+}
+
+static void
+_eina_stringshare_small_bucket_remove_at(
+   Eina_Stringshare_Small_Bucket **p_bucket,
+   int idx)
+{
+   Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
+   int todo, off;
+
+   if (bucket->references[idx] > 1)
+     {
+        bucket->references[idx]--;
+        return;
+     }
+
+        free((char *)bucket->strings[idx]);
+
+   if (bucket->count == 1)
+     {
+        free((void *)bucket->strings);
+        free(bucket->lengths);
+        free(bucket->references);
+        free(bucket);
+        *p_bucket = NULL;
+        return;
+     }
+
+   bucket->count--;
+   if (idx == bucket->count)
+      goto end;
+
+   off = idx + 1;
+   todo = bucket->count - idx;
+
+   memmove((void *)(bucket->strings + idx), bucket->strings + off,
+           todo * sizeof(bucket->strings[0]));
+   memmove(bucket->lengths + idx,           bucket->lengths + off,
+           todo * sizeof(bucket->lengths[0]));
+   memmove(bucket->references + idx,        bucket->references + off,
+           todo * sizeof(bucket->references[0]));
+
+end:
+   if (bucket->count + EINA_STRINGSHARE_SMALL_BUCKET_STEP < bucket->size)
+     {
+        int size = bucket->size - EINA_STRINGSHARE_SMALL_BUCKET_STEP;
+        _eina_stringshare_small_bucket_resize(bucket, size);
+     }
+}
+
+static const char *
+_eina_stringshare_small_add(const char *str, unsigned char length)
+{
+   Eina_Stringshare_Small_Bucket **bucket;
+   int i;
+
+   bucket = _eina_small_share.buckets + (unsigned char)str[0];
+   if (!*bucket)
+      i = 0;
+   else
+     {
+        const char *ret;
+        ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i);
+        if (ret)
+          {
+             (*bucket)->references[i]++;
+             return ret;
+          }
+     }
+
+   return _eina_stringshare_small_bucket_insert_at(bucket, str, length, i);
+}
+
+static void
+_eina_stringshare_small_del(const char *str, unsigned char length)
+{
+   Eina_Stringshare_Small_Bucket **bucket;
+   const char *ret;
+   int i;
+
+   bucket = _eina_small_share.buckets + (unsigned char)str[0];
+   if (!*bucket)
+      goto error;
+
+   ret = _eina_stringshare_small_bucket_find(*bucket, str, length, &i);
+   if (!ret)
+      goto error;
+
+   _eina_stringshare_small_bucket_remove_at(bucket, i);
+   return;
+
+error:
+   CRITICAL("EEEK trying to del non-shared stringshare \"%s\"", str);
+}
+
+static void
+_eina_stringshare_small_init(void)
+{
+   memset(&_eina_small_share, 0, sizeof(_eina_small_share));
+}
+
+static void
+_eina_stringshare_small_shutdown(void)
+{
+   Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end;
+
+   p_bucket = _eina_small_share.buckets;
+   p_bucket_end = p_bucket + 256;
+
+   for (; p_bucket < p_bucket_end; p_bucket++)
+     {
+        Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
+        char **s, **s_end;
+
+        if (!bucket)
+           continue;
+
+        s = (char **)bucket->strings;
+        s_end = s + bucket->count;
+        for (; s < s_end; s++)
+           free(*s);
+
+           free((void *)bucket->strings);
+           free(bucket->lengths);
+           free(bucket->references);
+           free(bucket);
+        *p_bucket = NULL;
+     }
+}
+
+static void
+_eina_stringshare_small_bucket_dump(Eina_Stringshare_Small_Bucket *bucket,
+                                    struct dumpinfo *di)
+{
+   const char **s = bucket->strings;
+   unsigned char *l = bucket->lengths;
+   unsigned short *r = bucket->references;
+   int i;
+
+   di->used += sizeof(*bucket);
+   di->used += bucket->count * sizeof(*s);
+   di->used += bucket->count * sizeof(*l);
+   di->used += bucket->count * sizeof(*r);
+   di->unique += bucket->count;
+
+   for (i = 0; i < bucket->count; i++, s++, l++, r++)
+     {
+        int dups;
+#ifdef _WIN32
+        printf("DDD: %5hu %5hu '%s'\n",  *l, *r, *s);
+#else
+        printf("DDD: %5hhu %5hu '%s'\n", *l, *r, *s);
+#endif
+
+        dups = (*r - 1);
+
+        di->used += *l;
+        di->saved += *l * dups;
+        di->dups += dups;
+     }
+}
+
+static void
+_eina_stringshare_small_dump(struct dumpinfo *di)
+{
+   Eina_Stringshare_Small_Bucket **p_bucket, **p_bucket_end;
+
+   p_bucket = _eina_small_share.buckets;
+   p_bucket_end = p_bucket + 256;
+
+   for (; p_bucket < p_bucket_end; p_bucket++)
+     {
+        Eina_Stringshare_Small_Bucket *bucket = *p_bucket;
+
+        if (!bucket)
+           continue;
+
+        _eina_stringshare_small_bucket_dump(bucket, di);
+     }
+}
+
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the share_common module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_stringshare_init(void)
+{
+   Eina_Bool ret;
+   ret = eina_share_common_init(&stringshare_share,
+                                EINA_MAGIC_STRINGSHARE_NODE,
+                                EINA_MAGIC_STRINGSHARE_NODE_STR);
+   if (ret)
+      _eina_stringshare_small_init();
+
+   return ret;
+}
+
+/**
+ * @internal
+ * @brief Shut down the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the share_common module set up by
+ * eina_share_common_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_stringshare_shutdown(void)
+{
+   Eina_Bool ret;
+   _eina_stringshare_small_shutdown();
+   ret = eina_share_common_shutdown(&stringshare_share);
+   return ret;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+/**
+ * @addtogroup Eina_Stringshare_Group Stringshare
+ *
+ * These functions allow you to store one copy of a string, and use it
+ * throughout your program.
+ *
+ * This is a method to reduce the number of duplicated strings kept in
+ * memory. It's pretty common for the same strings to be dynamically
+ * allocated repeatedly between applications and libraries, especially in
+ * circumstances where you could have multiple copies of a structure that
+ * allocates the string. So rather than duplicating and freeing these
+ * strings, you request a read-only pointer to an existing string and
+ * only incur the overhead of a hash lookup.
+ *
+ * It sounds like micro-optimizing, but profiling has shown this can have
+ * a significant impact as you scale the number of copies up. It improves
+ * string creation/destruction speed, reduces memory use and decreases
+ * memory fragmentation, so a win all-around.
+ *
+ * For more information, you can look at the @ref tutorial_stringshare_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Note that the given string has lost an instance.
+ *
+ * @param str string The given string.
+ *
+ * This function decreases the reference counter associated to @p str
+ * if it exists. If that counter reaches 0, the memory associated to
+ * @p str is freed. If @p str is NULL, the function returns
+ * immediately.
+ *
+ * Note that if the given pointer is not shared or NULL, bad things
+ * will happen, likely a segmentation fault.
+ */
+EAPI void
+eina_stringshare_del(const char *str)
+{
+   int slen;
+   DBG("str=%p (%s)", str, str ? str : "");
+   if (!str)
+      return;
+
+   /* special cases */
+   if      (str[0] == '\0')
+      slen = 0;
+   else if (str[1] == '\0')
+      slen = 1;
+   else if (str[2] == '\0')
+      slen = 2;
+   else if (str[3] == '\0')
+      slen = 3;
+   else
+      slen = 4;  /* handled later */
+
+   if (slen < 2)
+      return;
+   else if (slen < 4)
+     {
+        eina_share_common_population_del(stringshare_share, slen);
+        STRINGSHARE_LOCK_SMALL();
+        _eina_stringshare_small_del(str, slen);
+        STRINGSHARE_UNLOCK_SMALL();
+        return;
+     }
+
+   eina_share_common_del(stringshare_share, str);
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program.
+ *
+ * @param   str The string to retrieve an instance of.
+ * @param   slen The string size (<= strlen(str)).
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p str. If @p str is
+ * @c NULL, then @c NULL is returned. If @p str is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * of @p str is returned.
+ *
+ * This function does not check string size, but uses the
+ * exact given size. This can be used to share_common part of a larger
+ * buffer or substring.
+ *
+ * @see eina_share_common_add()
+ */
+EAPI const char *
+eina_stringshare_add_length(const char *str, unsigned int slen)
+{
+   DBG("str=%p (%.*s), slen=%u", str, slen, str ? str : "", slen);
+
+   if (slen <= 0)
+      return "";
+   else if (slen == 1)
+      return (const char *)_eina_stringshare_single + ((*str) << 1);
+   else if (slen < 4)
+     {
+        const char *s;
+
+        STRINGSHARE_LOCK_SMALL();
+        s = _eina_stringshare_small_add(str, slen);
+        STRINGSHARE_UNLOCK_SMALL();
+        return s;
+     }
+
+   return eina_share_common_add_length(stringshare_share, str, slen *
+                                       sizeof(char), sizeof(char));
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program.
+ *
+ * @param   str The NULL terminated string to retrieve an instance of.
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p str. If @p str is
+ * @c NULL, then @c NULL is returned. If @p str is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * of @p str is returned.
+ *
+ * The string @p str must be NULL terminated ('@\0') and its full
+ * length will be used. To use part of the string or non-null
+ * terminated, use eina_stringshare_add_length() instead.
+ *
+ * @see eina_stringshare_add_length()
+ */
+EAPI const char *
+eina_stringshare_add(const char *str)
+{
+   int slen;
+   if (!str)
+      return NULL;
+
+   if      (str[0] == '\0')
+      slen = 0;
+   else if (str[1] == '\0')
+      slen = 1;
+   else if (str[2] == '\0')
+      slen = 2;
+   else if (str[3] == '\0')
+      slen = 3;
+   else
+      slen = 3 + (int)strlen(str + 3);
+
+   return eina_stringshare_add_length(str, slen);
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program
+ * from a format string.
+ *
+ * @param   fmt The NULL terminated format string to retrieve an instance of.
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p fmt. If @p fmt is
+ * @c NULL, then @c NULL is returned. If @p fmt is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * is returned.
+ *
+ * The format string @p fmt must be NULL terminated ('@\0') and its full
+ * length will be used. To use part of the format string or non-null
+ * terminated, use eina_stringshare_nprintf() instead.
+ *
+ * @see eina_stringshare_nprintf()
+ */
+EAPI const char *
+eina_stringshare_printf(const char *fmt, ...)
+{
+   va_list args;
+   char *tmp;
+   const char *ret;
+   int len;
+
+   if (!fmt)
+      return NULL;
+
+   va_start(args, fmt);
+   len = vasprintf(&tmp, fmt, args);
+   va_end(args);
+
+   if (len < 1)
+      return NULL;
+
+   ret = eina_stringshare_add_length(tmp, len);
+   free(tmp);
+
+   return ret;
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program
+ * from a format string.
+ *
+ * @param   fmt The NULL terminated format string to retrieve an instance of.
+ * @param   args The va_args for @p fmt
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p fmt with @p args. If @p fmt is
+ * @c NULL, then @c NULL is returned. If @p fmt with @p args is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * is returned.
+ *
+ * The format string @p fmt must be NULL terminated ('@\0') and its full
+ * length will be used. To use part of the format string or non-null
+ * terminated, use eina_stringshare_nprintf() instead.
+ *
+ * @see eina_stringshare_nprintf()
+ */
+EAPI const char *
+eina_stringshare_vprintf(const char *fmt, va_list args)
+{
+   char *tmp;
+   const char *ret;
+   int len;
+
+   if (!fmt)
+      return NULL;
+
+   len = vasprintf(&tmp, fmt, args);
+
+   if (len < 1)
+      return NULL;
+
+   ret = eina_stringshare_add_length(tmp, len);
+   free(tmp);
+
+   return ret;
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program
+ * from a format string with size limitation.
+ * @param   len The length of the format string to use
+ * @param   fmt The format string to retrieve an instance of.
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p fmt limited by @p len. If @p fmt is
+ * @c NULL or @p len is < 1, then @c NULL is returned. If the resulting string
+ * is already stored, it is returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * is returned.
+ *
+ * @p len length of the format string will be used. To use the
+ * entire format string, use eina_stringshare_printf() instead.
+ *
+ * @see eina_stringshare_printf()
+ */
+EAPI const char *
+eina_stringshare_nprintf(unsigned int len, const char *fmt, ...)
+{
+   va_list args;
+   char *tmp;
+   int size;
+
+   if (!fmt)
+      return NULL;
+
+   if (len < 1)
+      return NULL;
+
+   tmp = alloca(sizeof(char) * len + 1);
+
+   va_start(args, fmt);
+   size = vsnprintf(tmp, len, fmt, args);
+   va_end(args);
+
+   if (size < 1)
+      return NULL;
+
+   return eina_stringshare_add_length(tmp, len);
+}
+
+/**
+ * Increment references of the given shared string.
+ *
+ * @param str The shared string.
+ * @return    A pointer to an instance of the string on success.
+ *            @c NULL on failure.
+ *
+ * This is similar to eina_share_common_add(), but it's faster since it will
+ * avoid lookups if possible, but on the down side it requires the parameter
+ * to be shared before, in other words, it must be the return of a previous
+ * eina_share_common_add().
+ *
+ * There is no unref since this is the work of eina_share_common_del().
+ */
+EAPI const char *
+eina_stringshare_ref(const char *str)
+{
+   int slen;
+   DBG("str=%p (%s)", str, str ? str : "");
+
+   if (!str)
+      return eina_share_common_ref(stringshare_share, str);
+
+   /* special cases */
+   if      (str[0] == '\0')
+      slen = 0;
+   else if (str[1] == '\0')
+      slen = 1;
+   else if (str[2] == '\0')
+      slen = 2;
+   else if (str[3] == '\0')
+      slen = 3;
+   else
+      slen = 3 + (int)strlen(str + 3);
+
+   if (slen < 2)
+     {
+        eina_share_common_population_add(stringshare_share, slen);
+
+        return str;
+     }
+   else if (slen < 4)
+     {
+        const char *s;
+        eina_share_common_population_add(stringshare_share, slen);
+
+        STRINGSHARE_LOCK_SMALL();
+        s = _eina_stringshare_small_add(str, slen);
+        STRINGSHARE_UNLOCK_SMALL();
+
+        return s;
+     }
+
+   return eina_share_common_ref(stringshare_share, str);
+}
+
+/**
+ * @brief Note that the given string @b must be shared.
+ *
+ * @param str the shared string to know the length. It is safe to
+ *        give NULL, in that case -1 is returned.
+ *
+ * This function is a cheap way to known the length of a shared
+ * string. Note that if the given pointer is not shared, bad
+ * things will happen, likely a segmentation fault. If in doubt, try
+ * strlen().
+ */
+EAPI int
+eina_stringshare_strlen(const char *str)
+{
+   int len;
+   /* special cases */
+   if (str[0] == '\0')
+      return 0;
+
+   if (str[1] == '\0')
+      return 1;
+
+   if (str[2] == '\0')
+      return 2;
+
+   if (str[3] == '\0')
+      return 3;
+
+   len = eina_share_common_length(stringshare_share, (const char *)str);
+   len = (len > 0) ? len / (int)sizeof(char) : -1;
+   return len;
+}
+
+/**
+ * @brief Dump the contents of the share_common.
+ *
+ * This function dumps all strings in the share_common to stdout with a
+ * DDD: prefix per line and a memory usage summary.
+ */
+EAPI void
+eina_stringshare_dump(void)
+{
+   eina_share_common_dump(stringshare_share,
+                          _eina_stringshare_small_dump,
+                          sizeof(_eina_stringshare_single));
+}
+
+/**
+ * @}
+ */
+
diff --git a/tests/suite/ecore/src/lib/eina_tiler.c b/tests/suite/ecore/src/lib/eina_tiler.c
new file mode 100644 (file)
index 0000000..2d263b7
--- /dev/null
@@ -0,0 +1,1295 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2007-2008 Gustavo Sverzut Barbieri, Jorge Luis Zapata Muga
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* TODO
+ * it is possible to have more than one tiler algorithm, but for now the
+ * version Gustavo did is hardcoded here
+ * http://blog.gustavobarbieri.com.br/2007/06/03/evas-now-using-rectangle-split-and-merge/
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "eina_config.h"
+#include "eina_private.h"
+#include "eina_tiler.h"
+#include "eina_error.h"
+
+/*============================================================================*
+*                                  Local                                     *
+*============================================================================*/
+
+/* The splitter data types */
+typedef struct list_node list_node_t;
+typedef struct list list_t;
+typedef struct rect rect_t;
+typedef struct rect_node rect_node_t;
+
+struct list_node
+{
+   struct list_node *next;
+};
+
+struct list
+{
+   struct list_node *head;
+   struct list_node *tail;
+};
+
+struct rect
+{
+   short right;
+   short bottom;
+   short left;
+   short top;
+   short width;
+   short height;
+   int area;
+};
+
+struct rect_node
+{
+   struct list_node _lst;
+   struct rect rect;
+};
+
+typedef struct splitter
+{
+   Eina_Bool need_merge;
+   list_t rects;
+} splitter_t;
+
+typedef struct list_node_pool
+{
+   list_node_t *node;
+   int len;
+   int max;
+} list_node_pool_t;
+
+
+static const list_node_t list_node_zeroed = { NULL };
+static const list_t list_zeroed = { NULL, NULL };
+static list_node_pool_t list_node_pool = { NULL, 0, 1024 };
+
+
+typedef struct _Eina_Iterator_Tiler
+{
+   Eina_Iterator iterator;
+   const Eina_Tiler *tiler;
+   list_node_t *curr;
+   EINA_MAGIC
+} Eina_Iterator_Tiler;
+
+struct _Eina_Tiler
+{
+   struct
+   {
+      int w, h;
+   } tile;
+   Eina_Rectangle area;
+   EINA_MAGIC
+   splitter_t splitter;
+};
+
+#define EINA_MAGIC_CHECK_TILER(d, ...)                                  \
+   do {                                                            \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER))             \
+          {                                                       \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER);           \
+             return __VA_ARGS__;                             \
+          }                                                       \
+     } while(0)
+
+
+#define EINA_MAGIC_CHECK_TILER_ITERATOR(d, ...)                         \
+   do {                                                            \
+        if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_TILER_ITERATOR))    \
+          {                                                       \
+             EINA_MAGIC_FAIL(d, EINA_MAGIC_TILER_ITERATOR);  \
+             return __VA_ARGS__;                             \
+          }                                                       \
+     } while(0)
+
+/* The Splitter algorithm */
+static inline void rect_init(rect_t *r, int x, int y, int w, int h)
+{
+   r->area = w * h;
+
+   r->left = x;
+   r->top = y;
+
+   r->right = x + w;
+   r->bottom = y + h;
+
+   r->width = w;
+   r->height = h;
+}
+
+static inline list_node_t *
+rect_list_node_pool_get(void)
+{
+   if (list_node_pool.node)
+     {
+        list_node_t *node;
+
+        node = list_node_pool.node;
+        list_node_pool.node = node->next;
+        list_node_pool.len--;
+
+        return node;
+     }
+   else
+      return malloc(sizeof(rect_node_t));
+}
+
+
+static inline void rect_list_concat(list_t *rects, list_t *other)
+{
+   if (!other->head)
+      return;
+
+   if (rects->tail)
+     {
+        rects->tail->next = other->head;
+        rects->tail = other->tail;
+     }
+   else
+     {
+        rects->head = other->head;
+        rects->tail = other->tail;
+     }
+
+   *other = list_zeroed;
+}
+
+static inline void rect_list_append_node(list_t *rects, list_node_t *node)
+{
+   if (rects->tail)
+     {
+        rects->tail->next = node;
+        rects->tail = node;
+     }
+   else
+     {
+        rects->head = node;
+        rects->tail = node;
+     }
+}
+
+static inline void rect_list_append(list_t *rects, const rect_t r)
+{
+   rect_node_t *rect_node;
+
+   rect_node = (rect_node_t *)rect_list_node_pool_get();
+   rect_node->rect = r;
+   rect_node->_lst = list_node_zeroed;
+
+   rect_list_append_node(rects, (list_node_t *)rect_node);
+}
+
+static inline void rect_list_append_xywh(list_t *rects,
+                                         int x,
+                                         int y,
+                                         int w,
+                                         int h)
+{
+   rect_t r;
+
+   rect_init(&r, x, y, w, h);
+   rect_list_append(rects, r);
+}
+
+static inline void _calc_intra_rect_area(const rect_t a, const rect_t b,
+                                         int *width, int *height)
+{
+   int max_left, min_right, max_top, min_bottom;
+
+   if (a.left < b.left)
+      max_left = b.left;
+   else
+      max_left = a.left;
+
+   if (a.right < b.right)
+      min_right = a.right;
+   else
+      min_right = b.right;
+
+   *width = min_right - max_left;
+
+   if (a.top < b.top)
+      max_top = b.top;
+   else
+      max_top = a.top;
+
+   if (a.bottom < b.bottom)
+      min_bottom = a.bottom;
+   else
+      min_bottom = b.bottom;
+
+   *height = min_bottom - max_top;
+}
+
+static inline void _split_strict(list_t *dirty, const rect_t current, rect_t r)
+{
+   int h_1, h_2, w_1, w_2;
+
+   h_1 = current.top - r.top;
+   h_2 = r.bottom - current.bottom;
+   w_1 = current.left - r.left;
+   w_2 = r.right - current.right;
+
+   if (h_1 > 0)
+     {
+        /*    .--.r (b)                .---.r2
+         *    |  |                     |   |
+         *  .-------.cur (a) .---.r    '---'
+         *  | |  |  |     -> |   |   +
+         *  | `--'  |        `---'
+         *  `-------'
+         */
+        rect_list_append_xywh(dirty, r.left, r.top, r.width, h_1);
+        r.height -= h_1;
+        r.top = current.top;
+     }
+
+   if (h_2 > 0)
+     {
+        /*  .-------.cur (a)
+         *  | .---. |        .---.r
+         *  | |   | |    ->  |   |
+         *  `-------'        `---'   +  .---.r2
+         *    |   |                     |   |
+         *    `---'r (b)                `---'
+         */
+        rect_list_append_xywh(dirty, r.left, current.bottom, r.width,
+                              h_2);
+        r.height -= h_2;
+     }
+
+   if (w_1 > 0)
+      /* (b) r  .----.cur (a)
+       *     .--|-.  |      .--.r2   .-.r
+       *     |  | |  |  ->  |  |   + | |
+       *     `--|-'  |      `--'     `-'
+       *        `----'
+       */
+        rect_list_append_xywh(dirty, r.left, r.top, w_1, r.height);  /* not necessary to keep these, r (b) will be destroyed */
+
+   /* r.width -= w_1; */
+   /* r.left = current.left; */
+
+   if (w_2 > 0)
+      /*  .----.cur (a)
+       *  |    |
+       *  |  .-|--.r (b)  .-.r   .--.r2
+       *  |  | |  |    -> | |  + |  |
+       *  |  `-|--'       `-'    `--'
+       *  `----'
+       */
+        rect_list_append_xywh(dirty, current.right, r.top, w_2,
+                            r.height);  /* not necessary to keep this, r (b) will be destroyed */
+
+   /* r.width -= w_2; */
+}
+
+static inline void _calc_intra_outer_rect_area(const rect_t a, const rect_t b,
+                                               rect_t *intra, rect_t *outer)
+{
+   int min_left, max_left, min_right, max_right;
+   int min_top, max_top, min_bottom, max_bottom;
+
+   if (a.left < b.left)
+     {
+        max_left = b.left;
+        min_left = a.left;
+     }
+   else
+     {
+        max_left = a.left;
+        min_left = b.left;
+     }
+
+   if (a.right < b.right)
+     {
+        min_right = a.right;
+        max_right = b.right;
+     }
+   else
+     {
+        min_right = b.right;
+        max_right = a.right;
+     }
+
+   intra->left = max_left;
+   intra->right = min_right;
+   intra->width = min_right - max_left;
+
+   outer->left = min_left;
+   outer->right = max_right;
+   outer->width = max_right - min_left;
+
+   if (a.top < b.top)
+     {
+        max_top = b.top;
+        min_top = a.top;
+     }
+   else
+     {
+        max_top = a.top;
+        min_top = b.top;
+     }
+
+   if (a.bottom < b.bottom)
+     {
+        min_bottom = a.bottom;
+        max_bottom = b.bottom;
+     }
+   else
+     {
+        min_bottom = b.bottom;
+        max_bottom = a.bottom;
+     }
+
+   intra->top = max_top;
+   intra->bottom = min_bottom;
+   intra->height = min_bottom - max_top;
+   if ((intra->width > 0) && (intra->height > 0))
+      intra->area = intra->width * intra->height;
+   else
+      intra->area = 0;
+
+   outer->top = min_top;
+   outer->bottom = max_bottom;
+   outer->height = max_bottom - min_top;
+   outer->area = outer->width * outer->height;
+}
+
+enum
+{
+   SPLIT_FUZZY_ACTION_NONE,
+   SPLIT_FUZZY_ACTION_SPLIT,
+   SPLIT_FUZZY_ACTION_MERGE
+};
+
+static inline int _split_fuzzy(list_t *dirty, const rect_t a, rect_t *b)
+{
+   int h_1, h_2, w_1, w_2, action;
+
+   h_1 = a.top - b->top;
+   h_2 = b->bottom - a.bottom;
+   w_1 = a.left - b->left;
+   w_2 = b->right - a.right;
+
+   action = SPLIT_FUZZY_ACTION_NONE;
+
+   if (h_1 > 0)
+     {
+        /*    .--.r (b)                .---.r2
+         *    |  |                     |   |
+         *  .-------.cur (a) .---.r    '---'
+         *  | |  |  |     -> |   |   +
+         *  | `--'  |        `---'
+         *  `-------'
+         */
+        rect_list_append_xywh(dirty, b->left, b->top, b->width, h_1);
+        b->height -= h_1;
+        b->top = a.top;
+        action = SPLIT_FUZZY_ACTION_SPLIT;
+     }
+
+   if (h_2 > 0)
+     {
+        /*  .-------.cur (a)
+         *  | .---. |        .---.r
+         *  | |   | |    ->  |   |
+         *  `-------'        `---'   +  .---.r2
+         *    |   |                     |   |
+         *    `---'r (b)                `---'
+         */
+        rect_list_append_xywh(dirty, b->left, a.bottom, b->width, h_2);
+        b->height -= h_2;
+        action = SPLIT_FUZZY_ACTION_SPLIT;
+     }
+
+   if (((w_1 > 0) || (w_2 > 0)) && (a.height == b->height))
+      return SPLIT_FUZZY_ACTION_MERGE;
+
+   if (w_1 > 0)
+     {
+        /* (b)  r  .----.cur (a)
+         *      .--|-.  |      .--.r2   .-.r
+         *      |  | |  |  ->  |  |   + | |
+         *      `--|-'  |      `--'     `-'
+         *         `----'
+         */
+        rect_list_append_xywh(dirty, b->left, b->top, w_1, b->height);
+        /* not necessary to keep these, r (b) will be destroyed */
+        /* b->width -= w_1; */
+        /* b->left = a.left; */
+        action = SPLIT_FUZZY_ACTION_SPLIT;
+     }
+
+   if (w_2 > 0)
+     {
+        /* .----.cur (a)
+         * |    |
+         * |  .-|--.r (b)  .-.r   .--.r2
+         * |  | |  |    -> | |  + |  |
+         * |  `-|--'       `-'    `--'
+         * `----'
+         */
+        rect_list_append_xywh(dirty, a.right, b->top, w_2, b->height);
+        /* not necessary to keep these, r (b) will be destroyed */
+        /* b->width -= w_2; */
+        action = SPLIT_FUZZY_ACTION_SPLIT;
+     }
+
+   return action;
+}
+
+#if 0
+static void rect_list_node_pool_set_max(int max)
+{
+   int diff;
+
+   diff = list_node_pool.len - max;
+   for (; diff > 0 && list_node_pool.node != NULL; diff--)
+     {
+        list_node_t *node;
+
+        node = list_node_pool.node;
+        list_node_pool.node = node->next;
+        list_node_pool.len--;
+
+        free(node);
+     }
+
+   list_node_pool.max = max;
+}
+#endif
+
+static void rect_list_node_pool_flush(void)
+{
+   while (list_node_pool.node)
+     {
+        list_node_t *node;
+
+        node = list_node_pool.node;
+        list_node_pool.node = node->next;
+        list_node_pool.len--;
+
+        free(node);
+     }
+}
+
+
+
+static inline void rect_list_node_pool_put(list_node_t *node)
+{
+   if (list_node_pool.len < list_node_pool.max)
+     {
+        node->next = list_node_pool.node;
+        list_node_pool.node = node;
+        list_node_pool.len++;
+     }
+   else
+        free(node);
+}
+
+#if 0
+static void rect_print(const rect_t r)
+{
+        printf("<rect(%d, %d, %d, %d)>", r.left, r.top, r.width, r.height);
+}
+
+static void rect_list_print(const list_t rects)
+{
+   list_node_t *node;
+   int len;
+
+   len = 0;
+   for (node = rects.head; node != NULL; node = node->next)
+      len++;
+
+        printf("[");
+   for (node = rects.head; node != NULL; node = node->next)
+     {
+        rect_print(((rect_node_t *)node)->rect);
+        if (node->next)
+          {
+                  putchar(',');
+             if (len < 4)
+                  putchar(' ');
+             else
+               {
+                  putchar('\n');
+                  putchar(' ');
+               }
+          }
+     }
+   printf("]\n");
+}
+#endif
+
+static inline list_node_t *
+rect_list_unlink_next(list_t *rects, list_node_t *parent_node)
+{
+   list_node_t *node;
+
+   if (parent_node)
+     {
+        node = parent_node->next;
+        parent_node->next = node->next;
+     }
+   else
+     {
+        node = rects->head;
+        rects->head = node->next;
+     }
+
+   if (rects->tail == node)
+      rects->tail = parent_node;
+
+   *node = list_node_zeroed;
+   return node;
+}
+
+static inline void rect_list_del_next(list_t *rects, list_node_t *parent_node)
+{
+   list_node_t *node;
+
+   node = rect_list_unlink_next(rects, parent_node);
+        rect_list_node_pool_put(node);
+}
+
+static void rect_list_clear(list_t *rects)
+{
+   list_node_t *node;
+
+   node = rects->head;
+   while (node)
+     {
+        list_node_t *aux;
+
+        aux = node->next;
+        rect_list_node_pool_put(node);
+        node = aux;
+     }
+   *rects = list_zeroed;
+}
+
+static void rect_list_del_split_strict(list_t *rects, const rect_t del_r)
+{
+   list_t modified = list_zeroed;
+   list_node_t *cur_node, *prev_node;
+
+   prev_node = NULL;
+   cur_node = rects->head;
+   while (cur_node)
+     {
+        int intra_width, intra_height;
+        rect_t current;
+
+        current = ((rect_node_t *)cur_node)->rect;
+
+        _calc_intra_rect_area(del_r, current, &intra_width,
+                              &intra_height);
+        if ((intra_width <= 0) || (intra_height <= 0))
+          {
+             /*  .---.current      .---.del_r
+              *  |   |             |   |
+              *  `---+---.del_r    `---+---.current
+              *      |   |             |   |
+              *      `---'             `---'
+              * no intersection, nothing to do
+              */
+             prev_node = cur_node;
+             cur_node = cur_node->next;
+          }
+        else if ((intra_width == current.width) && (intra_height
+                                                    == current.height))
+          {
+             /*  .-------.del_r
+              *  | .---. |
+              *  | |   | |
+              *  | `---'current
+              *  `-------'
+              * current is contained, remove from rects
+              */
+             cur_node = cur_node->next;
+             rect_list_del_next(rects, prev_node);
+          }
+        else
+          {
+             _split_strict(&modified, del_r, current);
+             cur_node = cur_node->next;
+             rect_list_del_next(rects, prev_node);
+          }
+     }
+
+   rect_list_concat(rects, &modified);
+}
+
+#if 0
+static void rect_list_add_split_strict(list_t *rects, list_node_t *node)
+{
+   list_t dirty = list_zeroed;
+   list_t new_dirty = list_zeroed;
+   list_node_t *cur_node;
+
+   if (!rects->head)
+     {
+        rect_list_append_node(rects, node);
+        return;
+     }
+
+        rect_list_append_node(&dirty, node);
+
+   cur_node = rects->head;
+   while (dirty.head)
+     {
+        rect_t current;
+
+        if (!cur_node)
+          {
+             rect_list_concat(rects, &dirty);
+             break;
+          }
+
+        current = ((rect_node_t *)cur_node)->rect;
+
+        while (dirty.head)
+          {
+             int intra_width, intra_height;
+             rect_t r;
+
+             r = ((rect_node_t *)dirty.head)->rect;
+             _calc_intra_rect_area(r, current, &intra_width,
+                                   &intra_height);
+             if ((intra_width == r.width) && (intra_height
+                                              == r.height))
+                /*  .-------.cur
+                 *  | .---.r|
+                 *  | |   | |
+                 *  | `---' |
+                 *  `-------'
+                 */
+                rect_list_del_next(&dirty, NULL);
+             else if ((intra_width <= 0) || (intra_height <= 0))
+               {
+                  /*  .---.cur     .---.r
+                   *  |   |        |   |
+                   *  `---+---.r   `---+---.cur
+                   *      |   |        |   |
+                   *      `---'        `---'
+                   */
+                  list_node_t *tmp;
+                  tmp = rect_list_unlink_next(&dirty, NULL);
+                  rect_list_append_node(&new_dirty, tmp);
+               }
+             else
+               {
+                  _split_strict(&new_dirty, current, r);
+                  rect_list_del_next(&dirty, NULL);
+               }
+          }
+        dirty = new_dirty;
+        new_dirty = list_zeroed;
+
+        cur_node = cur_node->next;
+     }
+}
+#endif
+
+static list_node_t *
+rect_list_add_split_fuzzy(list_t *rects, list_node_t *node, int accepted_error)
+{
+   list_t dirty = list_zeroed;
+   list_node_t *old_last;
+
+   old_last = rects->tail;
+
+   if (!rects->head)
+     {
+        rect_list_append_node(rects, node);
+        return old_last;
+     }
+
+        rect_list_append_node(&dirty, node);
+   while (dirty.head)
+     {
+        list_node_t *d_node, *cur_node, *prev_cur_node;
+        int keep_dirty;
+        rect_t r;
+
+        d_node = rect_list_unlink_next(&dirty, NULL);
+        r = ((rect_node_t *)d_node)->rect;
+
+        prev_cur_node = NULL;
+        cur_node = rects->head;
+        keep_dirty = 1;
+        while (cur_node)
+          {
+             int area, action;
+             rect_t current, intra, outer;
+
+             current = ((rect_node_t *)cur_node)->rect;
+
+             _calc_intra_outer_rect_area(r, current, &intra, &outer);
+             area = current.area + r.area - intra.area;
+
+             if ((intra.width == r.width) && (intra.height
+                                              == r.height))
+               {
+                  /*  .-------.cur
+                   *  | .---.r|
+                   *  | |   | |
+                   *  | `---' |
+                   *  `-------'
+                   */
+                  keep_dirty = 0;
+                  break;
+               }
+             else if ((intra.width == current.width)
+                      && (intra.height == current.height))
+               {
+                  /* .-------.r
+                   * | .---.cur
+                   * | |   | |
+                   * | `---' |
+                   * `-------'
+                   */
+                  if (old_last == cur_node)
+                     old_last = prev_cur_node;
+
+                  cur_node = cur_node->next;
+                  rect_list_del_next(rects, prev_cur_node);
+               }
+             else if ((outer.area - area) <= accepted_error)
+               {
+                  /* .-----------. bounding box (outer)
+                   * |.---. .---.|
+                   * ||cur| |r  ||
+                   * ||   | |   ||
+                   * |`---' `---'|
+                   * `-----------'
+                   * merge them, remove both and add merged
+                   */
+                  rect_node_t *n;
+
+                  if (old_last == cur_node)
+                     old_last = prev_cur_node;
+
+                  n = (rect_node_t *)rect_list_unlink_next(
+                        rects, prev_cur_node);
+                  n->rect = outer;
+                       rect_list_append_node(&dirty, (list_node_t *)n);
+
+                  keep_dirty = 0;
+                  break;
+               }
+             else if (intra.area <= accepted_error)
+               {
+                  /*  .---.cur     .---.r
+                   *  |   |        |   |
+                   *  `---+---.r   `---+---.cur
+                   *      |   |        |   |
+                   *      `---'        `---'
+                   *  no split, no merge
+                   */
+                  prev_cur_node = cur_node;
+                  cur_node = cur_node->next;
+               }
+             else
+               {
+                  /* split is required */
+                  action = _split_fuzzy(&dirty, current, &r);
+                  if (action == SPLIT_FUZZY_ACTION_MERGE)
+                    {
+/* horizontal merge is possible: remove both, add merged */
+                       rect_node_t *n;
+
+                       if (old_last == cur_node)
+                          old_last = prev_cur_node;
+
+                       n
+                          = (rect_node_t *)rect_list_unlink_next(
+                                rects,
+                                prev_cur_node);
+
+                       n->rect.left = outer.left;
+                       n->rect.width = outer.width;
+                       n->rect.right = outer.right;
+                       n->rect.area = outer.width * r.height;
+                       rect_list_append_node(&dirty,
+                                             (list_node_t *)n);
+                    }
+                  else if (action == SPLIT_FUZZY_ACTION_NONE)
+                    {
+/*
+ * this rect check was totally useless,
+ * should never happen
+ */
+/* prev_cur_node = cur_node; */
+/* cur_node = cur_node->next; */
+                       printf("Should not get here!\n");
+                       abort();
+                    }
+
+                  keep_dirty = 0;
+                  break;
+               }
+          }
+        if (EINA_UNLIKELY(keep_dirty))
+           rect_list_append_node(rects, d_node);
+        else
+           rect_list_node_pool_put(d_node);
+     }
+
+   return old_last;
+}
+
+static inline void _calc_outer_rect_area(const rect_t a, const rect_t b,
+                                         rect_t *outer)
+{
+   int min_left, max_right;
+   int min_top, max_bottom;
+
+   if (a.left < b.left)
+      min_left = a.left;
+   else
+      min_left = b.left;
+
+   if (a.right < b.right)
+      max_right = b.right;
+   else
+      max_right = a.right;
+
+   outer->left = min_left;
+   outer->right = max_right;
+   outer->width = max_right - min_left;
+
+   if (a.top < b.top)
+      min_top = a.top;
+   else
+      min_top = b.top;
+
+   if (a.bottom < b.bottom)
+      max_bottom = b.bottom;
+   else
+      max_bottom = a.bottom;
+
+   outer->top = min_top;
+   outer->bottom = max_bottom;
+   outer->height = max_bottom - min_top;
+
+   outer->area = outer->width * outer->height;
+}
+
+static void rect_list_merge_rects(list_t *rects,
+                                  list_t *to_merge,
+                                  int accepted_error)
+{
+   while (to_merge->head)
+     {
+        list_node_t *node, *parent_node;
+        rect_t r1;
+        int merged;
+
+        r1 = ((rect_node_t *)to_merge->head)->rect;
+
+        merged = 0;
+        parent_node = NULL;
+        node = rects->head;
+        while (node)
+          {
+             rect_t r2, outer;
+             int area;
+
+             r2 = ((rect_node_t *)node)->rect;
+
+             _calc_outer_rect_area(r1, r2, &outer);
+             area = r1.area + r2.area; /* intra area is taken as 0 */
+             if (outer.area - area <= accepted_error)
+               {
+                  /*
+                   * remove both r1 and r2, create r3
+                   * actually r3 uses r2 instance, saves memory
+                   */
+                  rect_node_t *n;
+
+                  n = (rect_node_t *)rect_list_unlink_next(
+                        rects, parent_node);
+                  n->rect = outer;
+                  rect_list_append_node(to_merge,
+                                        (list_node_t *)n);
+                  merged = 1;
+                  break;
+               }
+
+             parent_node = node;
+             node = node->next;
+          }
+
+        if (!merged)
+          {
+             list_node_t *n;
+             n = rect_list_unlink_next(to_merge, NULL);
+                  rect_list_append_node(rects, n);
+          }
+        else
+                  rect_list_del_next(to_merge, NULL);
+     }
+}
+
+static void rect_list_add_split_fuzzy_and_merge(list_t *rects,
+                                                list_node_t *node,
+                                                int split_accepted_error,
+                                                int merge_accepted_error)
+{
+   list_node_t *n;
+
+   n = rect_list_add_split_fuzzy(rects, node, split_accepted_error);
+   if (n && n->next)
+     {
+        list_t to_merge;
+
+        /* split list into 2 segments, already merged and to merge */
+        to_merge.head = n->next;
+        to_merge.tail = rects->tail;
+        rects->tail = n;
+        n->next = NULL;
+
+        rect_list_merge_rects(rects, &to_merge, merge_accepted_error);
+     }
+}
+
+static inline void _splitter_new(Eina_Tiler *t)
+{
+   t->splitter.rects = list_zeroed;
+   t->splitter.need_merge = EINA_FALSE;
+}
+
+static inline void _splitter_del(Eina_Tiler *t)
+{
+   rect_list_clear(&t->splitter.rects);
+   rect_list_node_pool_flush();
+}
+
+static inline void _splitter_tile_size_set(Eina_Tiler *t,
+                                           int w __UNUSED__,
+                                           int h __UNUSED__)
+{
+   /* TODO are w and h used for something? */
+   t->splitter.rects = list_zeroed;
+}
+
+static inline Eina_Bool _splitter_rect_add(Eina_Tiler *t, Eina_Rectangle *rect)
+{
+   rect_node_t *rn;
+
+   //printf("ACCOUNTING[1]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h);
+   rect->x >>= 1;
+   rect->y >>= 1;
+   rect->w += 2;
+   rect->w >>= 1;
+   rect->h += 2;
+   rect->h >>= 1;
+
+   rn = (rect_node_t *)rect_list_node_pool_get();
+   rn->_lst = list_node_zeroed;
+   rect_init(&rn->rect, rect->x, rect->y, rect->w, rect->h);
+   //printf("ACCOUNTING[2]: add_redraw: %4d,%4d %3dx%3d\n", x, y, w, h);
+   //testing on my core2 duo desktop - fuzz of 32 or 48 is best.
+#define FUZZ 32
+   rect_list_add_split_fuzzy_and_merge(&t->splitter.rects,
+                                       (list_node_t *)rn,
+                                       FUZZ * FUZZ,
+                                       FUZZ * FUZZ);
+   return EINA_TRUE;
+}
+
+static inline void _splitter_rect_del(Eina_Tiler *t, Eina_Rectangle *rect)
+{
+   rect_t r;
+
+   if (!t->splitter.rects.head)
+      return;
+
+   rect->x += 1;
+   rect->y += 1;
+   rect->x >>= 1;
+   rect->y >>= 1;
+   rect->w -= 1;
+   rect->w >>= 1;
+   rect->h -= 1;
+   rect->h >>= 1;
+
+   if ((rect->w <= 0) || (rect->h <= 0))
+      return;
+
+   rect_init(&r, rect->x, rect->y, rect->w, rect->h);
+   //fprintf(stderr, "ACCOUNTING: del_redraw: %4d,%4d %3dx%3d\n", x, y, w, h);
+
+   rect_list_del_split_strict(&t->splitter.rects, r);
+   t->splitter.need_merge = EINA_TRUE;
+   return;
+}
+
+static inline void _splitter_clear(Eina_Tiler *t)
+{
+   rect_list_clear(&t->splitter.rects);
+   t->splitter.need_merge = EINA_FALSE;
+}
+/* end of splitter algorithm */
+
+static Eina_Bool _iterator_next(Eina_Iterator_Tiler *it, void **data)
+{
+   Eina_Rectangle *rect = (Eina_Rectangle *)data;
+   list_node_t *n;
+
+   for (n = it->curr; n; n = n->next)
+     {
+        rect_t cur;
+
+        cur = ((rect_node_t *)n)->rect;
+
+        rect->x = cur.left << 1;
+        rect->y = cur.top << 1;
+        rect->w = cur.width << 1;
+        rect->h = cur.height << 1;
+
+        if (eina_rectangle_intersection(rect, &it->tiler->area) == EINA_FALSE)
+           continue;
+
+        if ((rect->w <= 0) || (rect->h <= 0))
+           continue;
+
+        it->curr = n->next;
+        return EINA_TRUE;
+     }
+   return EINA_FALSE;
+}
+
+static void *_iterator_get_container(Eina_Iterator_Tiler *it)
+{
+   EINA_MAGIC_CHECK_TILER_ITERATOR(it, NULL);
+   return (void *)it->tiler;
+}
+
+static void _iterator_free(Eina_Iterator_Tiler *it)
+{
+   EINA_MAGIC_CHECK_TILER_ITERATOR(it);
+   free(it);
+}
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+EAPI Eina_Tiler *eina_tiler_new(int w, int h)
+{
+   Eina_Tiler *t;
+
+   t = calloc(1, sizeof(Eina_Tiler));
+   t->area.w = w;
+   t->area.h = h;
+   t->tile.w = w;
+   t->tile.h = h;
+   EINA_MAGIC_SET(t, EINA_MAGIC_TILER);
+   _splitter_new(t);
+   return t;
+}
+
+EAPI void eina_tiler_free(Eina_Tiler *t)
+{
+   EINA_MAGIC_CHECK_TILER(t);
+   _splitter_del(t);
+   free(t);
+}
+
+EAPI void eina_tiler_tile_size_set(Eina_Tiler *t, int w, int h)
+{
+   EINA_MAGIC_CHECK_TILER(t);
+   if ((w <= 0) || (h <= 0))
+      return;
+
+   t->tile.w = w;
+   t->tile.h = h;
+   _splitter_tile_size_set(t, w, h);
+}
+
+EAPI Eina_Bool eina_tiler_rect_add(Eina_Tiler *t, const Eina_Rectangle *r)
+{
+   Eina_Rectangle tmp;
+
+   EINA_MAGIC_CHECK_TILER(t, EINA_FALSE);
+   if ((r->w <= 0) || (r->h <= 0))
+      return EINA_FALSE;
+
+   tmp = *r;
+   if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE)
+      return EINA_FALSE;
+
+   if ((tmp.w <= 0) || (tmp.h <= 0))
+      return EINA_FALSE;
+
+   return _splitter_rect_add(t, &tmp);
+}
+
+EAPI void eina_tiler_rect_del(Eina_Tiler *t, const Eina_Rectangle *r)
+{
+   Eina_Rectangle tmp;
+
+   EINA_MAGIC_CHECK_TILER(t);
+   if ((r->w <= 0) || (r->h <= 0))
+      return;
+
+   tmp = *r;
+   if (eina_rectangle_intersection(&tmp, &t->area) == EINA_FALSE)
+      return;
+
+   if ((tmp.w <= 0) || (tmp.h <= 0))
+      return;
+
+   _splitter_rect_del(t, &tmp);
+}
+
+EAPI void eina_tiler_clear(Eina_Tiler *t)
+{
+   EINA_MAGIC_CHECK_TILER(t);
+   _splitter_clear(t);
+}
+
+
+EAPI Eina_Iterator *eina_tiler_iterator_new(const Eina_Tiler *t)
+{
+   Eina_Iterator_Tiler *it;
+
+   EINA_MAGIC_CHECK_TILER(t, NULL);
+
+   it = calloc(1, sizeof (Eina_Iterator_Tiler));
+   if (!it)
+      return NULL;
+
+   it->tiler = t;
+
+   if (t->splitter.need_merge == EINA_TRUE)
+     {
+        list_t to_merge;
+        splitter_t *sp;
+
+        sp = (splitter_t *)&(t->splitter);
+        to_merge = t->splitter.rects;
+        sp->rects = list_zeroed;
+        rect_list_merge_rects(&sp->rects, &to_merge, FUZZ * FUZZ);
+        sp->need_merge = 0;
+     }
+
+   it->curr = it->tiler->splitter.rects.head;
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(_iterator_next);
+   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+         _iterator_get_container);
+   it->iterator.free = FUNC_ITERATOR_FREE(_iterator_free);
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+   EINA_MAGIC_SET(it,            EINA_MAGIC_TILER_ITERATOR);
+
+   return &it->iterator;
+}
+
+struct _Eina_Tile_Grid_Slicer_Iterator
+{
+   Eina_Iterator iterator;
+   Eina_Tile_Grid_Slicer priv;
+};
+
+typedef struct _Eina_Tile_Grid_Slicer_Iterator Eina_Tile_Grid_Slicer_Iterator;
+
+static void
+eina_tile_grid_slicer_iterator_free(Eina_Tile_Grid_Slicer_Iterator *it)
+{
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_NONE);
+   free(it);
+}
+
+static Eina_Bool
+eina_tile_grid_slicer_iterator_next(Eina_Tile_Grid_Slicer_Iterator *it,
+                                    void **data)
+{
+   return eina_tile_grid_slicer_next
+             (&it->priv, (const Eina_Tile_Grid_Info **)data);
+}
+
+/**
+ * @brief Creates a new Eina_Iterator that slices over a list of tiles.
+ *
+ * @param   x X axis coordinate.
+ * @param   y Y axis coordinate.
+ * @param   w width.
+ * @param   h height.
+ * @param   tile_w tile width.
+ * @param   tile_h tile height.
+ * @return  A pointer to the Eina_Iterator.
+ *          @c NULL on failure.
+ *
+ * The tile grid is defined by @a tile_w and @a tile_h while the region is
+ * defined by @a x, @a y, @a w, @a h. The output is given as
+ * @c Eina_Tile_Grid_Info where tile index is given in @c col col and
+ * @c row row with tile-relative
+ *    coordinates in @c x, @c y, @c w, @c h. If tile was fully filled by
+ *    region, then @c full flag
+ *     is set.
+ */
+EAPI Eina_Iterator *
+eina_tile_grid_slicer_iterator_new(int x,
+                                   int y,
+                                   int w,
+                                   int h,
+                                   int tile_w,
+                                   int tile_h)
+{
+   Eina_Tile_Grid_Slicer_Iterator *it;
+
+   it = calloc(1, sizeof(*it));
+   if (!it)
+     {
+        eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+        return NULL;
+     }
+
+   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+
+   it->iterator.version = EINA_ITERATOR_VERSION;
+   it->iterator.next = FUNC_ITERATOR_NEXT(eina_tile_grid_slicer_iterator_next);
+   it->iterator.free = FUNC_ITERATOR_FREE(eina_tile_grid_slicer_iterator_free);
+
+   eina_tile_grid_slicer_setup(&it->priv, x, y, w, h, tile_w, tile_h);
+
+   return &it->iterator;
+}
diff --git a/tests/suite/ecore/src/lib/eina_unicode.c b/tests/suite/ecore/src/lib/eina_unicode.c
new file mode 100644 (file)
index 0000000..cef07fb
--- /dev/null
@@ -0,0 +1,176 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2010 Tom Hacohen,
+ *             Brett Nash
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+
+ */
+
+#include <Eina.h>
+#include "eina_unicode.h"
+
+/* FIXME: check if sizeof(wchar_t) == sizeof(Eina_Unicode) if so,
+ * probably better to use the standard functions */
+
+/* Maybe I'm too tired, but this is the only thing that actually worked. */
+const Eina_Unicode _EINA_UNICODE_EMPTY_STRING[1] = {0};
+EAPI const Eina_Unicode *EINA_UNICODE_EMPTY_STRING = _EINA_UNICODE_EMPTY_STRING;
+/**
+ * @brief Same as the standard strcmp just with Eina_Unicode instead of char.
+ */
+EAPI int
+eina_unicode_strcmp(const Eina_Unicode *a, const Eina_Unicode *b)
+{
+   for (; *a && *a == *b; a++, b++)
+      ;
+   if (*a == *b)
+      return 0;
+   else if (*a < *b)
+      return -1;
+   else
+      return 1;
+}
+
+/**
+ * @brief Same as the standard strcpy just with Eina_Unicode instead of char.
+ */
+EAPI Eina_Unicode *
+eina_unicode_strcpy(Eina_Unicode *dest, const Eina_Unicode *source)
+{
+   Eina_Unicode *ret = dest;
+
+   while (*source)
+      *dest++ = *source++;
+   *dest = 0;
+   return ret;
+}
+
+/**
+ * @brief Same as the standard strncpy just with Eina_Unicode instead of char.
+ */
+EAPI Eina_Unicode *
+eina_unicode_strncpy(Eina_Unicode *dest, const Eina_Unicode *source, size_t n)
+{
+   Eina_Unicode *ret = dest;
+
+   for ( ; n && *source ; n--)
+      *dest++ = *source++;
+   for (; n; n--)
+      *dest++ = 0;
+   return ret;
+}
+
+/**
+ * @brief Same as the standard strlen just with Eina_Unicode instead of char.
+ */
+EAPI size_t
+eina_unicode_strlen(const Eina_Unicode *ustr)
+{
+   const Eina_Unicode *end;
+   for (end = ustr; *end; end++)
+      ;
+   return end - ustr;
+}
+
+/**
+ * @brief Returns the length of a Eina_Unicode string, up to a limit.
+ *
+ * This function returns the number of characters in string, up to a maximum
+ * of n.  If the terminating character is not found in the string, it returns
+ * n.
+ *
+ * @param ustr String to search
+ * @param n Max length to search
+ * @return Number of characters or n.
+ */
+EAPI size_t
+eina_unicode_strnlen(const Eina_Unicode *ustr, int n)
+{
+   const Eina_Unicode *end;
+   const Eina_Unicode *last = ustr + n; /* technically not portable ;-) */
+   for (end = ustr; end < last && *end; end++)
+      ;
+   return end - ustr;
+}
+
+
+
+
+/**
+ * @brief Same as the standard strdup just with Eina_Unicode instead of char.
+ */
+EAPI Eina_Unicode *
+eina_unicode_strdup(const Eina_Unicode *text)
+{
+   Eina_Unicode *ustr;
+   int len;
+
+   len = eina_unicode_strlen(text);
+   ustr = (Eina_Unicode *)calloc(len + 1, sizeof(Eina_Unicode));
+   memcpy(ustr, text, len * sizeof(Eina_Unicode));
+
+   return ustr;
+}
+
+/**
+ * @brief Same as the standard strdup just with Eina_Unicode instead of char.
+ */
+EAPI Eina_Unicode *
+eina_unicode_strstr(const Eina_Unicode *haystack, const Eina_Unicode *needle)
+{
+   const Eina_Unicode *i, *j;
+
+   for (i = haystack; *i; i++)
+     {
+        haystack = i; /* set this location as the base position */
+        for (j = needle; *j && *i && *j == *i; j++, i++)
+           ;
+
+        if (!*j) /*if we got to the end of j this means we got a full match */
+          {
+             return (Eina_Unicode *)haystack; /* return the new base position */
+          }
+     }
+
+   return NULL;
+}
+
+/**
+ * @see eina_str_escape()
+ */
+EAPI Eina_Unicode *
+eina_unicode_escape(const Eina_Unicode *str)
+{
+   Eina_Unicode *s2, *d;
+   const Eina_Unicode *s;
+
+   s2 = malloc((eina_unicode_strlen(str) * 2) + 1);
+   if (!s2)
+      return NULL;
+
+   for (s = str, d = s2; *s != 0; s++, d++)
+     {
+        if ((*s == ' ') || (*s == '\\') || (*s == '\''))
+          {
+             *d = '\\';
+             d++;
+          }
+
+        *d = *s;
+     }
+   *d = 0;
+   return s2;
+}
+
diff --git a/tests/suite/ecore/src/lib/eina_ustrbuf.c b/tests/suite/ecore/src/lib/eina_ustrbuf.c
new file mode 100644 (file)
index 0000000..7df5b16
--- /dev/null
@@ -0,0 +1,89 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_strbuf_common.h"
+#include "eina_unicode.h"
+#include "eina_ustrbuf.h"
+
+/*============================================================================*
+ *                                  Local                                     *
+ *============================================================================*/
+
+/**
+ * @cond LOCAL
+ */
+
+#ifdef _STRBUF_DATA_TYPE
+# undef _STRBUF_DATA_TYPE
+#endif
+
+#ifdef _STRBUF_CSIZE
+# undef _STRBUF_CSIZE
+#endif
+
+#ifdef _STRBUF_STRUCT_NAME
+# undef _STRBUF_STRUCT_NAME
+#endif
+
+#ifdef _STRBUF_STRLEN_FUNC
+# undef _STRBUF_STRLEN_FUNC
+#endif
+
+#ifdef _STRBUF_STRESCAPE_FUNC
+# undef _STRBUF_STRESCAPE_FUNC
+#endif
+
+#ifdef _STRBUF_MAGIC
+# undef _STRBUF_MAGIC
+#endif
+
+#ifdef _STRBUF_MAGIC_STR
+# undef _STRBUF_MAGIC_STR
+#endif
+
+#ifdef _FUNC_EXPAND
+# undef _FUNC_EXPAND
+#endif
+
+#define _STRBUF_DATA_TYPE         Eina_Unicode
+#define _STRBUF_CSIZE             sizeof(_STRBUF_DATA_TYPE)
+#define _STRBUF_STRUCT_NAME       Eina_UStrbuf
+#define _STRBUF_STRLEN_FUNC(x)    eina_unicode_strlen(x)
+#define _STRBUF_STRESCAPE_FUNC(x) eina_unicode_escape(x)
+#define _STRBUF_MAGIC             EINA_MAGIC_USTRBUF
+#define _STRBUF_MAGIC_STR         __USTRBUF_MAGIC_STR
+static const char __USTRBUF_MAGIC_STR[] = "Eina UStrbuf";
+
+#define _FUNC_EXPAND(y) eina_ustrbuf_ ## y
+
+/**
+ * @endcond
+ */
+
+
+/*============================================================================*
+ *                                 Global                                     *
+ *============================================================================*/
+
+
+/*============================================================================*
+ *                                   API                                      *
+ *============================================================================*/
+
+/**
+ * @addtogroup Eina_Unicode_String_Buffer_Group Unicode String Buffer
+ *
+ * @brief These functions provide unicode string buffers management.
+ *
+ * The Unicode String Buffer data type is designed to be a mutable string,
+ * allowing to append, prepend or insert a string to a buffer.
+ *
+ * @{
+ */
+
+#include "eina_strbuf_template_c.x"
+
+/**
+ * @}
+ */
diff --git a/tests/suite/ecore/src/lib/eina_ustringshare.c b/tests/suite/ecore/src/lib/eina_ustringshare.c
new file mode 100644 (file)
index 0000000..8fe18c8
--- /dev/null
@@ -0,0 +1,243 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2002-2008 Carsten Haitzler,
+ *                         Jorge Luis Zapata Muga,
+ *                         Cedric Bail,
+ *                         Gustavo Sverzut Barbieri
+ *                         Tom Hacohen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+
+ */
+/**
+ * @page tutorial_ustringshare_page UStringshare Tutorial
+ *
+ * to be written...
+ *
+ */
+
+#include "eina_share_common.h"
+#include "eina_unicode.h"
+#include "eina_private.h"
+#include "eina_ustringshare.h"
+
+/* The actual share */
+static Eina_Share *ustringshare_share;
+static const char EINA_MAGIC_USTRINGSHARE_NODE_STR[] = "Eina UStringshare Node";
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/**
+ * @internal
+ * @brief Initialize the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function sets up the share_common module of Eina. It is called by
+ * eina_init().
+ *
+ * @see eina_init()
+ */
+Eina_Bool
+eina_ustringshare_init(void)
+{
+   return eina_share_common_init(&ustringshare_share,
+                                 EINA_MAGIC_USTRINGSHARE_NODE,
+                                 EINA_MAGIC_USTRINGSHARE_NODE_STR);
+}
+
+/**
+ * @internal
+ * @brief Shut down the share_common module.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on failure.
+ *
+ * This function shuts down the share_common module set up by
+ * eina_share_common_init(). It is called by eina_shutdown().
+ *
+ * @see eina_shutdown()
+ */
+Eina_Bool
+eina_ustringshare_shutdown(void)
+{
+   Eina_Bool ret;
+   ret = eina_share_common_shutdown(&ustringshare_share);
+   return ret;
+}
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+/**
+ * @addtogroup Eina_UStringshare_Group Unicode Stringshare
+ *
+ * These functions allow you to store one copy of a string, and use it
+ * throughout your program.
+ *
+ * This is a method to reduce the number of duplicated strings kept in
+ * memory. It's pretty common for the same strings to be dynamically
+ * allocated repeatedly between applications and libraries, especially in
+ * circumstances where you could have multiple copies of a structure that
+ * allocates the string. So rather than duplicating and freeing these
+ * strings, you request a read-only pointer to an existing string and
+ * only incur the overhead of a hash lookup.
+ *
+ * It sounds like micro-optimizing, but profiling has shown this can have
+ * a significant impact as you scale the number of copies up. It improves
+ * string creation/destruction speed, reduces memory use and decreases
+ * memory fragmentation, so a win all-around.
+ *
+ * For more information, you can look at the @ref tutorial_ustringshare_page.
+ *
+ * @{
+ */
+
+/**
+ * @brief Note that the given string has lost an instance.
+ *
+ * @param str string The given string.
+ *
+ * This function decreases the reference counter associated to @p str
+ * if it exists. If that counter reaches 0, the memory associated to
+ * @p str is freed. If @p str is NULL, the function returns
+ * immediately.
+ *
+ * Note that if the given pointer is not shared or NULL, bad things
+ * will happen, likely a segmentation fault.
+ */
+EAPI void
+eina_ustringshare_del(const Eina_Unicode *str)
+{
+   if (!str)
+      return;
+
+   eina_share_common_del(ustringshare_share,(const char *)str);
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program.
+ *
+ * @param   str The string to retrieve an instance of.
+ * @param   slen The string size (<= strlen(str)).
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p str. If @p str is
+ * @c NULL, then @c NULL is returned. If @p str is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * of @p str is returned.
+ *
+ * This function does not check string size, but uses the
+ * exact given size. This can be used to share_common part of a larger
+ * buffer or substring.
+ *
+ * @see eina_ustringshare_add()
+ */
+EAPI const Eina_Unicode *
+eina_ustringshare_add_length(const Eina_Unicode *str, unsigned int slen)
+{
+   return (const Eina_Unicode *)eina_share_common_add_length(ustringshare_share,
+                                                             (const char *)str,
+                                                             slen *
+                                                             sizeof(
+                                                                Eina_Unicode),
+                                                             sizeof(
+                                                                Eina_Unicode));
+}
+
+/**
+ * @brief Retrieve an instance of a string for use in a program.
+ *
+ * @param   str The NULL terminated string to retrieve an instance of.
+ * @return  A pointer to an instance of the string on success.
+ *          @c NULL on failure.
+ *
+ * This function retrieves an instance of @p str. If @p str is
+ * @c NULL, then @c NULL is returned. If @p str is already stored, it
+ * is just returned and its reference counter is increased. Otherwise
+ * it is added to the strings to be searched and a duplicated string
+ * of @p str is returned.
+ *
+ * The string @p str must be NULL terminated ('@\0') and its full
+ * length will be used. To use part of the string or non-null
+ * terminated, use eina_stringshare_add_length() instead.
+ *
+ * @see eina_ustringshare_add_length()
+ */
+EAPI const Eina_Unicode *
+eina_ustringshare_add(const Eina_Unicode *str)
+{
+   int slen = (str) ? (int)eina_unicode_strlen(str) : -1;
+   return eina_ustringshare_add_length(str, slen);
+}
+
+/**
+ * Increment references of the given shared string.
+ *
+ * @param str The shared string.
+ * @return    A pointer to an instance of the string on success.
+ *            @c NULL on failure.
+ *
+ * This is similar to eina_share_common_add(), but it's faster since it will
+ * avoid lookups if possible, but on the down side it requires the parameter
+ * to be shared before, in other words, it must be the return of a previous
+ * eina_ustringshare_add().
+ *
+ * There is no unref since this is the work of eina_ustringshare_del().
+ */
+EAPI const Eina_Unicode *
+eina_ustringshare_ref(const Eina_Unicode *str)
+{
+   return (const Eina_Unicode *)eina_share_common_ref(ustringshare_share,
+                                                      (const char *)str);
+}
+
+/**
+ * @brief Note that the given string @b must be shared.
+ *
+ * @param str the shared string to know the length. It is safe to
+ *        give NULL, in that case -1 is returned.
+ *
+ * This function is a cheap way to known the length of a shared
+ * string. Note that if the given pointer is not shared, bad
+ * things will happen, likely a segmentation fault. If in doubt, try
+ * strlen().
+ */
+EAPI int
+eina_ustringshare_strlen(const Eina_Unicode *str)
+{
+   int len = eina_share_common_length(ustringshare_share, (const char *)str);
+   len = (len > 0) ? len / (int)sizeof(Eina_Unicode) : -1;
+   return len;
+}
+
+/**
+ * @brief Dump the contents of the share_common.
+ *
+ * This function dumps all strings in the share_common to stdout with a
+ * DDD: prefix per line and a memory usage summary.
+ */
+EAPI void
+eina_ustringshare_dump(void)
+{
+   eina_share_common_dump(ustringshare_share, NULL, 0);
+}
+
+/**
+ * @}
+ */
+
diff --git a/tests/suite/ecore/src/lib/eina_value.c b/tests/suite/ecore/src/lib/eina_value.c
new file mode 100644 (file)
index 0000000..554f907
--- /dev/null
@@ -0,0 +1,47 @@
+/* eina_value.c
+
+   Copyright (C) 2001 Christopher Rosendahl    <smugg@fatelabs.com>
+                   Nathan Ingersoll         <ningerso@d.umn.edu>
+
+   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 and its documentation and acknowledgment shall be
+   given in the documentation 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.
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_config.h"
+#include "eina_private.h"
+
+/*============================================================================*
+*                                 Global                                     *
+*============================================================================*/
+
+/*============================================================================*
+*                                   API                                      *
+*============================================================================*/
+
+EAPI const unsigned int eina_prime_table[] =
+{
+   17, 31, 61, 127, 257, 509, 1021,
+   2053, 4093, 8191, 16381, 32771, 65537, 131071, 262147, 524287, 1048573,
+   2097143, 4194301, 8388617, 16777213
+};
diff --git a/tests/suite/mini-eagain2.c b/tests/suite/mini-eagain2.c
new file mode 100644 (file)
index 0000000..284fdc9
--- /dev/null
@@ -0,0 +1,205 @@
+#include <Ecore.h>
+#include <fcntl.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+
+/* Ecore_Fd_Handler example
+ * 2010 Mike Blumenkrantz
+ * compile with gcc $(pkgconfig --cflags --libs gnutls ecore)
+ */
+
+
+#define print(...) fprintf(stderr, "line %i: ", __LINE__); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n")
+
+static int done = 0;
+
+static void
+tls_log_func (int level, const char *str)
+{
+  fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+static const char*
+SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_description_t status)
+{
+   switch (status)
+     {
+      case GNUTLS_HANDSHAKE_HELLO_REQUEST:
+        return "Hello request";
+      case GNUTLS_HANDSHAKE_CLIENT_HELLO:
+        return "Client hello";
+      case GNUTLS_HANDSHAKE_SERVER_HELLO:
+        return "Server hello";
+      case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:
+        return "Certificate packet";
+      case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE:
+        return "Server key exchange";
+      case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
+        return "Certificate request";
+      case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE:
+        return "Server hello done";
+      case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
+        return "Certificate verify";
+      case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
+        return "Client key exchange";
+      case GNUTLS_HANDSHAKE_FINISHED:
+        return "Finished";
+      case GNUTLS_HANDSHAKE_SUPPLEMENTAL:
+        return "Supplemental";
+      default:
+        return NULL;
+     }
+   return NULL;
+}
+
+/* Connects to the peer and returns a socket
+ * descriptor.
+ */
+static int
+tcp_connect (void)
+{
+  const char *PORT = "4445";
+  const char *SERVER = "127.0.0.1"; //verisign.com
+  int err, sd;
+  int flag = 1, curstate = 0;
+  struct sockaddr_in sa;
+
+  /* sets some fd options such as nonblock */
+  sd = socket (AF_INET, SOCK_STREAM, 0);
+  fcntl(sd, F_SETFL, O_NONBLOCK);
+  fcntl(sd, F_SETFD, FD_CLOEXEC);
+  setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, sizeof(curstate));
+
+  setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
+
+  memset (&sa, '\0', sizeof (sa));
+  sa.sin_family = AF_INET;
+  sa.sin_port = htons (atoi (PORT));
+  inet_pton (AF_INET, SERVER, &sa.sin_addr);
+
+  /* connects to server
+   */
+  err = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
+  if ((err < 0) && (errno != EINPROGRESS))
+    {
+      print("Connect error\n");
+      exit (1);
+    }
+
+  return sd;
+}
+
+/* closes the given socket descriptor.
+ */
+static void
+tcp_close (int sd)
+{
+  shutdown (sd, SHUT_RDWR);    /* no more receptions */
+  close (sd);
+}
+
+static Eina_Bool
+_process_data(gnutls_session_t client, Ecore_Fd_Handler *fd_handler)
+{
+   static int ret, lastret;
+   static unsigned int count = 0;
+   
+   if (!done)
+     {
+        lastret = ret;
+        ret = gnutls_handshake (client);
+        count++;
+        if (gnutls_record_get_direction(client))
+          ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_WRITE);
+        else
+          ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_READ);
+        /* avoid printing messages infinity times */
+        if (lastret != ret && ret != 0 && ret != GNUTLS_E_AGAIN)
+          {
+             print("gnutls returned with: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret));
+             if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED))
+               print("Also received alert: %s", gnutls_alert_get_name(gnutls_alert_get(client)));
+             print("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_out(client)));
+             print("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_in(client)));
+          }
+
+        if (gnutls_error_is_fatal(ret))
+          {
+             print("yarrr this be an error!");
+             exit(1);
+          }
+        
+     }
+  if (ret == GNUTLS_E_SUCCESS)
+    {
+       done = 1;
+       //print("Handshake successful in %u handshake calls!", count);
+       ecore_main_loop_quit();
+    }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+int
+main (void)
+{
+  /* credentials */
+  gnutls_anon_client_credentials_t c_anoncred;
+  gnutls_certificate_credentials_t c_certcred;
+  
+  gnutls_session_t client;
+  int sd, i;
+
+  /* General init. */
+  gnutls_global_init ();
+  ecore_init();
+//  gnutls_global_set_log_function (tls_log_func);
+//  gnutls_global_set_log_level (2);
+
+  /* Init client */
+  gnutls_anon_allocate_client_credentials (&c_anoncred);
+  gnutls_certificate_allocate_credentials (&c_certcred);
+
+
+  for (i=0;i<5;i++) 
+    {
+
+      gnutls_init (&client, GNUTLS_CLIENT);
+      /* set very specific priorities */
+      gnutls_priority_set_direct(client, "NORMAL:+ANON-DH", NULL);
+      gnutls_credentials_set (client, GNUTLS_CRD_ANON, c_anoncred);
+      gnutls_credentials_set (client, GNUTLS_CRD_CERTIFICATE, c_certcred);
+      gnutls_server_name_set(client, GNUTLS_NAME_DNS, "localhost", strlen("localhost"));
+
+      /* connect to the peer
+       */
+      sd = tcp_connect ();
+
+      /* associate gnutls with socket */
+      gnutls_transport_set_ptr (client, (gnutls_transport_ptr_t) sd);
+      /* add a callback for data being available for send/receive on socket */
+      if (!ecore_main_fd_handler_add(sd, ECORE_FD_READ | ECORE_FD_WRITE, (Ecore_Fd_Cb)_process_data, client, NULL, NULL))
+        {
+           print("could not create fd handler!");
+           exit(1);
+        }
+      /* begin main loop */
+      ecore_main_loop_begin();
+
+      gnutls_bye (client, GNUTLS_SHUT_RDWR);
+
+      gnutls_deinit (client);
+
+      tcp_close (sd);
+    }
+  
+  return 0;
+}