Git init
authorKibum Kim <kb0929.kim@samsung.com>
Fri, 6 Jan 2012 15:45:39 +0000 (00:45 +0900)
committerKibum Kim <kb0929.kim@samsung.com>
Fri, 6 Jan 2012 15:45:39 +0000 (00:45 +0900)
370 files changed:
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
benchmarks/Makefile.am [new file with mode: 0644]
configure.ac [new file with mode: 0644]
debian/changelog [new file with mode: 0755]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0755]
debian/copyright [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/libelektra-dev.install.in [new file with mode: 0644]
debian/libelektra.install.in [new file with mode: 0644]
debian/rules [new file with mode: 0755]
doc/Makefile.am [new file with mode: 0644]
doc/UPLOAD [new file with mode: 0644]
doc/api.xml [new file with mode: 0644]
doc/apiexample.xml [new file with mode: 0644]
doc/author.xml [new file with mode: 0644]
doc/bestpract.xml [new file with mode: 0644]
doc/compared.xml [new file with mode: 0644]
doc/docbook.css [new file with mode: 0644]
doc/doxygen.css [new file with mode: 0644]
doc/elektra-api/html/doxygen.css [new file with mode: 0644]
doc/elektra-api/html/doxygen.png [new file with mode: 0644]
doc/elektra-api/html/err.html [new file with mode: 0644]
doc/elektra-api/html/graph_legend.dot [new file with mode: 0644]
doc/elektra-api/html/graph_legend.html [new file with mode: 0644]
doc/elektra-api/html/group__backend.html [new file with mode: 0644]
doc/elektra-api/html/group__backendhandle.html [new file with mode: 0644]
doc/elektra-api/html/group__backendhelper.html [new file with mode: 0644]
doc/elektra-api/html/group__internal.html [new file with mode: 0644]
doc/elektra-api/html/group__kdb.html [new file with mode: 0644]
doc/elektra-api/html/group__kdbhighlevel.html [new file with mode: 0644]
doc/elektra-api/html/group__key.html [new file with mode: 0644]
doc/elektra-api/html/group__keymeta.html [new file with mode: 0644]
doc/elektra-api/html/group__keyname.html [new file with mode: 0644]
doc/elektra-api/html/group__keyset.html [new file with mode: 0644]
doc/elektra-api/html/group__keytest.html [new file with mode: 0644]
doc/elektra-api/html/group__keyvalue.html [new file with mode: 0644]
doc/elektra-api/html/group__stream.html [new file with mode: 0644]
doc/elektra-api/html/index.html [new file with mode: 0644]
doc/elektra-api/html/modules.html [new file with mode: 0644]
doc/elektra-api/html/pages.html [new file with mode: 0644]
doc/elektra-api/html/tab_b.gif [new file with mode: 0644]
doc/elektra-api/html/tab_l.gif [new file with mode: 0644]
doc/elektra-api/html/tab_r.gif [new file with mode: 0644]
doc/elektra-api/html/tabs.css [new file with mode: 0644]
doc/elektra-api/latex/FreeSans.ttf [new file with mode: 0644]
doc/elektra-api/latex/doxygen.sty [new file with mode: 0644]
doc/elektra-api/latex/err.tex [new file with mode: 0644]
doc/elektra-api/latex/group__backend.tex [new file with mode: 0644]
doc/elektra-api/latex/group__backendhandle.tex [new file with mode: 0644]
doc/elektra-api/latex/group__backendhelper.tex [new file with mode: 0644]
doc/elektra-api/latex/group__internal.tex [new file with mode: 0644]
doc/elektra-api/latex/group__kdb.tex [new file with mode: 0644]
doc/elektra-api/latex/group__kdbhighlevel.tex [new file with mode: 0644]
doc/elektra-api/latex/group__key.tex [new file with mode: 0644]
doc/elektra-api/latex/group__keymeta.tex [new file with mode: 0644]
doc/elektra-api/latex/group__keyname.tex [new file with mode: 0644]
doc/elektra-api/latex/group__keyset.tex [new file with mode: 0644]
doc/elektra-api/latex/group__keytest.tex [new file with mode: 0644]
doc/elektra-api/latex/group__keyvalue.tex [new file with mode: 0644]
doc/elektra-api/latex/group__stream.tex [new file with mode: 0644]
doc/elektra-api/latex/modules.tex [new file with mode: 0644]
doc/elektra-api/latex/refman.tex [new file with mode: 0644]
doc/elektra-api/man/man3/backend.3 [new file with mode: 0644]
doc/elektra-api/man/man3/backendhandle.3 [new file with mode: 0644]
doc/elektra-api/man/man3/backendhelper.3 [new file with mode: 0644]
doc/elektra-api/man/man3/err.3 [new file with mode: 0644]
doc/elektra-api/man/man3/internal.3 [new file with mode: 0644]
doc/elektra-api/man/man3/kdb.3 [new file with mode: 0644]
doc/elektra-api/man/man3/kdbhighlevel.3 [new file with mode: 0644]
doc/elektra-api/man/man3/key.3 [new file with mode: 0644]
doc/elektra-api/man/man3/keymeta.3 [new file with mode: 0644]
doc/elektra-api/man/man3/keyname.3 [new file with mode: 0644]
doc/elektra-api/man/man3/keyset.3 [new file with mode: 0644]
doc/elektra-api/man/man3/keytest.3 [new file with mode: 0644]
doc/elektra-api/man/man3/keyvalue.3 [new file with mode: 0644]
doc/elektra-api/man/man3/stream.3 [new file with mode: 0644]
doc/elektra.5 [new file with mode: 0644]
doc/elektra.5.xml [new file with mode: 0644]
doc/elektra.7 [new file with mode: 0644]
doc/elektra.7.xml [new file with mode: 0644]
doc/garbage.xml [new file with mode: 0644]
doc/html-params.xsl [new file with mode: 0644]
doc/html-titlepage-layout.tpl [new file with mode: 0644]
doc/html-titlepage-layout.xsl [new file with mode: 0644]
doc/images/Makefile.am [new file with mode: 0644]
doc/images/classes.png [new file with mode: 0644]
doc/images/img_arrow.png [new file with mode: 0644]
doc/images/important.gif [new file with mode: 0644]
doc/images/link_button_1.gif [new file with mode: 0644]
doc/images/note.gif [new file with mode: 0644]
doc/images/type-binary.png [new file with mode: 0644]
doc/images/type-extendedbinary.png [new file with mode: 0644]
doc/images/type-extendedstring.png [new file with mode: 0644]
doc/images/type-folder.png [new file with mode: 0644]
doc/images/type-link.png [new file with mode: 0644]
doc/images/type-string.png [new file with mode: 0644]
doc/kdb.1 [new file with mode: 0644]
doc/kdb.1.xml [new file with mode: 0644]
doc/overview.xml [new file with mode: 0644]
doc/rgcmd.xml [new file with mode: 0644]
doc/rgexample.xml [new file with mode: 0644]
doc/society.xml [new file with mode: 0644]
doc/standards/Makefile.am [new file with mode: 0644]
doc/standards/env.xml [new file with mode: 0644]
doc/standards/filesystems.xml [new file with mode: 0644]
doc/standards/network.xml [new file with mode: 0644]
doc/standards/signature.xml [new file with mode: 0644]
doc/standards/sysinit.xml [new file with mode: 0644]
doc/standards/userdb.xml [new file with mode: 0644]
doc/storage.xml [new file with mode: 0644]
elektra.mandriva.spec [new file with mode: 0644]
elektra.pc.in [new file with mode: 0644]
elektra.spec.in [new file with mode: 0644]
elektra.xml [new file with mode: 0644]
elektracpp.pc.in [new file with mode: 0644]
elektratools.pc.in [new file with mode: 0644]
examples/.deps/application.Po [new file with mode: 0644]
examples/.deps/binary.Po [new file with mode: 0644]
examples/.deps/cascading.Po [new file with mode: 0644]
examples/.deps/functional.Po [new file with mode: 0644]
examples/.deps/getset.Po [new file with mode: 0644]
examples/.deps/hello.Po [new file with mode: 0644]
examples/.deps/hellokdb.Po [new file with mode: 0644]
examples/.deps/iterate.Po [new file with mode: 0644]
examples/.deps/keyNewExample.Po [new file with mode: 0644]
examples/.deps/keyset.Po [new file with mode: 0644]
examples/.deps/lookup.Po [new file with mode: 0644]
examples/.deps/reference.Po [new file with mode: 0644]
examples/.deps/small.Po [new file with mode: 0644]
examples/.deps/sort.Po [new file with mode: 0644]
examples/Makefile.am [new file with mode: 0644]
examples/application.c [new file with mode: 0644]
examples/binary.c [new file with mode: 0644]
examples/cascading.c [new file with mode: 0644]
examples/functional.c [new file with mode: 0644]
examples/getset.c [new file with mode: 0644]
examples/hello.c [new file with mode: 0644]
examples/hellokdb.c [new file with mode: 0644]
examples/hierarchy.xml [new file with mode: 0644]
examples/iterate.c [new file with mode: 0644]
examples/keyNewExample.c [new file with mode: 0644]
examples/keyset.c [new file with mode: 0644]
examples/keyset.xml [new file with mode: 0644]
examples/lookup.c [new file with mode: 0644]
examples/reference.c [new file with mode: 0644]
examples/small.c [new file with mode: 0644]
examples/sort.c [new file with mode: 0644]
m4/extensions.m4 [new file with mode: 0644]
packaging/elektra.spec [new file with mode: 0644]
scripts/Makefile.am [new file with mode: 0644]
scripts/benchmark-createtree [new file with mode: 0755]
scripts/convert-fstab [new file with mode: 0755]
scripts/convert-hosts [new file with mode: 0755]
scripts/convert-hwconfKudzu [new file with mode: 0755]
scripts/convert-inittab [new file with mode: 0755]
scripts/convert-network [new file with mode: 0755]
scripts/convert-users [new file with mode: 0755]
scripts/convert-xfree [new file with mode: 0755]
scripts/elektraenv.sh [new file with mode: 0755]
scripts/kdbd [new file with mode: 0755]
scripts/update-backend [new file with mode: 0755]
src/Doxyfile [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/backends/Makefile.am [new file with mode: 0644]
src/backends/berkeleydb/Makefile.am [new file with mode: 0644]
src/backends/berkeleydb/README [new file with mode: 0644]
src/backends/berkeleydb/berkeleydb.c [new file with mode: 0644]
src/backends/daemon/Makefile.am [new file with mode: 0644]
src/backends/daemon/daemon.c [new file with mode: 0644]
src/backends/daemon/datatype.h [new file with mode: 0644]
src/backends/daemon/ipc.c [new file with mode: 0644]
src/backends/daemon/ipc.h [new file with mode: 0644]
src/backends/daemon/kdb_wrapper.c [new file with mode: 0644]
src/backends/daemon/kdb_wrapper.h [new file with mode: 0644]
src/backends/daemon/kdbd.c [new file with mode: 0644]
src/backends/daemon/kdbd.h [new file with mode: 0644]
src/backends/daemon/main.c [new file with mode: 0644]
src/backends/daemon/message.c [new file with mode: 0644]
src/backends/daemon/message.h [new file with mode: 0644]
src/backends/daemon/protocol.c [new file with mode: 0644]
src/backends/daemon/protocol.h [new file with mode: 0644]
src/backends/daemon/serial.c [new file with mode: 0644]
src/backends/daemon/serial.h [new file with mode: 0644]
src/backends/daemon/serial_int.c [new file with mode: 0644]
src/backends/daemon/serial_int.h [new file with mode: 0644]
src/backends/daemon/serial_key.c [new file with mode: 0644]
src/backends/daemon/serial_key.h [new file with mode: 0644]
src/backends/daemon/serial_keyset.c [new file with mode: 0644]
src/backends/daemon/serial_keyset.h [new file with mode: 0644]
src/backends/daemon/serial_string.c [new file with mode: 0644]
src/backends/daemon/serial_string.h [new file with mode: 0644]
src/backends/daemon/sig.c [new file with mode: 0644]
src/backends/daemon/sig.h [new file with mode: 0644]
src/backends/daemon/thread.c [new file with mode: 0644]
src/backends/daemon/thread.h [new file with mode: 0644]
src/backends/doc/backend.c [new file with mode: 0644]
src/backends/filesys/Makefile.am [new file with mode: 0644]
src/backends/filesys/filesys.c [new file with mode: 0755]
src/backends/filesys/filesys.h [new file with mode: 0644]
src/backends/fstab/Makefile.am [new file with mode: 0644]
src/backends/fstab/README [new file with mode: 0644]
src/backends/fstab/fstab.c [new file with mode: 0644]
src/backends/gconf/Makefile.am [new file with mode: 0644]
src/backends/gconf/README [new file with mode: 0644]
src/backends/hosts/Makefile.am [new file with mode: 0644]
src/backends/hosts/hosts.c [new file with mode: 0644]
src/backends/hosts/hosts.h [new file with mode: 0644]
src/backends/ini/Makefile.am [new file with mode: 0644]
src/backends/ini/helpers.c [new file with mode: 0644]
src/backends/ini/ini.c [new file with mode: 0644]
src/backends/ini/ini.h [new file with mode: 0644]
src/backends/ini/parser.c [new file with mode: 0644]
src/backends/passwd/Makefile.am [new file with mode: 0644]
src/backends/passwd/passwd.c [new file with mode: 0644]
src/backends/passwd/passwd.h [new file with mode: 0644]
src/backends/template/Makefile.am [new file with mode: 0644]
src/backends/template/template.c [new file with mode: 0644]
src/backends/template/template.h [new file with mode: 0644]
src/backends/winregistry/winregistry.cpp [new file with mode: 0644]
src/backends/winregistry/winregistry.h [new file with mode: 0644]
src/bindings/Makefile.am [new file with mode: 0644]
src/bindings/cpp/.deps/libelektra_cpp_a-kdb.Po [new file with mode: 0644]
src/bindings/cpp/.deps/libelektra_cpp_a-key.Po [new file with mode: 0644]
src/bindings/cpp/.deps/libelektra_cpp_a-keyset.Po [new file with mode: 0644]
src/bindings/cpp/.deps/libelektra_cpp_la-kdb.Plo [new file with mode: 0644]
src/bindings/cpp/.deps/libelektra_cpp_la-key.Plo [new file with mode: 0644]
src/bindings/cpp/.deps/libelektra_cpp_la-keyset.Plo [new file with mode: 0644]
src/bindings/cpp/Makefile.am [new file with mode: 0644]
src/bindings/cpp/include/kdb [new file with mode: 0644]
src/bindings/cpp/include/key [new file with mode: 0644]
src/bindings/cpp/include/keyset [new file with mode: 0644]
src/bindings/cpp/kdb.cpp [new file with mode: 0644]
src/bindings/cpp/key.cpp [new file with mode: 0644]
src/bindings/cpp/keyset.cpp [new file with mode: 0644]
src/bindings/cpp/tests/.deps/test_kdb.Po [new file with mode: 0644]
src/bindings/cpp/tests/.deps/test_key.Po [new file with mode: 0644]
src/bindings/cpp/tests/.deps/test_ks.Po [new file with mode: 0644]
src/bindings/cpp/tests/.deps/tests.Po [new file with mode: 0644]
src/bindings/cpp/tests/Makefile.am [new file with mode: 0644]
src/bindings/cpp/tests/test_kdb.cpp [new file with mode: 0644]
src/bindings/cpp/tests/test_key.cpp [new file with mode: 0644]
src/bindings/cpp/tests/test_ks.cpp [new file with mode: 0644]
src/bindings/cpp/tests/tests.cpp [new file with mode: 0644]
src/bindings/cpp/tests/tests.h [new file with mode: 0644]
src/bindings/python/.deps/libelektra_la-libpyelektra.Plo [new file with mode: 0644]
src/bindings/python/ChangeLog [new file with mode: 0644]
src/bindings/python/Makefile.am [new file with mode: 0644]
src/bindings/python/application.py [new file with mode: 0644]
src/bindings/python/binary.py [new file with mode: 0644]
src/bindings/python/cascading.py [new file with mode: 0644]
src/bindings/python/elektra.py [new file with mode: 0644]
src/bindings/python/examples/elektra/getset.py [new file with mode: 0644]
src/bindings/python/examples/elektra/hello.py [new file with mode: 0644]
src/bindings/python/examples/elektra/remove.py [new file with mode: 0644]
src/bindings/python/examples/libelektra/getset.py [new file with mode: 0644]
src/bindings/python/examples/libelektra/hello.py [new file with mode: 0644]
src/bindings/python/examples/libelektra/remove.py [new file with mode: 0644]
src/bindings/python/libpyelektra.c [new file with mode: 0644]
src/bindings/python/test.py [new file with mode: 0644]
src/include/Makefile.am [new file with mode: 0644]
src/include/kdb.h.in [new file with mode: 0755]
src/include/kdbbackend.h [new file with mode: 0644]
src/include/kdbloader.h [new file with mode: 0644]
src/include/kdbos.h [new file with mode: 0644]
src/include/kdbprivate.h.in [new file with mode: 0755]
src/include/kdbtools.h [new file with mode: 0644]
src/include/ltdl.h [new file with mode: 0644]
src/kdb/.deps/kdb-BSDgetopt.Po [new file with mode: 0644]
src/kdb/.deps/kdb-help.Po [new file with mode: 0644]
src/kdb/.deps/kdb-kdb-tool.Po [new file with mode: 0644]
src/kdb/.deps/kdb_static-BSDgetopt.Po [new file with mode: 0644]
src/kdb/.deps/kdb_static-help.Po [new file with mode: 0644]
src/kdb/.deps/kdb_static-kdb-tool.Po [new file with mode: 0644]
src/kdb/BSDgetopt.c [new file with mode: 0644]
src/kdb/BSDgetopt.h [new file with mode: 0644]
src/kdb/Makefile.am [new file with mode: 0644]
src/kdb/help.c [new file with mode: 0755]
src/kdb/kdb-tool.c [new file with mode: 0755]
src/kdb/kdb-tool.h [new file with mode: 0644]
src/libelektra/Makefile.am [new file with mode: 0644]
src/libelektra/exportobjects.sh.in [new file with mode: 0644]
src/libelektra/exportsymbols.sh [new file with mode: 0644]
src/libelektra/kdb.c [new file with mode: 0755]
src/libelektra/kdbcapability.c [new file with mode: 0644]
src/libelektra/kdbhandle.c [new file with mode: 0644]
src/libelektra/kdbhighlevel.c [new file with mode: 0755]
src/libelektra/key.c [new file with mode: 0644]
src/libelektra/keyhelpers.c [new file with mode: 0755]
src/libelektra/keymeta.c [new file with mode: 0644]
src/libelektra/keyname.c [new file with mode: 0755]
src/libelektra/keyset.c [new file with mode: 0755]
src/libelektra/keytest.c [new file with mode: 0644]
src/libelektra/keyvalue.c [new file with mode: 0755]
src/libelektra/serialize.c [new file with mode: 0644]
src/libelektra/split.c [new file with mode: 0644]
src/libelektra/trie.c [new file with mode: 0644]
src/libelektratools/.deps/libelektratools_a-kdbtools.Po [new file with mode: 0644]
src/libelektratools/.deps/libelektratools_a-stream.Po [new file with mode: 0644]
src/libelektratools/.deps/libelektratools_la-kdbtools.Plo [new file with mode: 0644]
src/libelektratools/.deps/libelektratools_la-stream.Plo [new file with mode: 0644]
src/libelektratools/Makefile.am [new file with mode: 0644]
src/libelektratools/README [new file with mode: 0644]
src/libelektratools/kdbtools.c [new file with mode: 0644]
src/libelektratools/stream.c [new file with mode: 0644]
src/libhelper/Makefile.am [new file with mode: 0644]
src/libhelper/helper.c [new file with mode: 0755]
src/libhelper/helper.h [new file with mode: 0644]
src/libhelper/internal.c [new file with mode: 0644]
src/libloader/Makefile.am [new file with mode: 0644]
src/libloader/kdbLibLoader.c [new file with mode: 0644]
src/preload/.deps/preload-preload.Po [new file with mode: 0644]
src/preload/.deps/preload_static-preload.Po [new file with mode: 0644]
src/preload/Makefile.am [new file with mode: 0644]
src/preload/preload.c [new file with mode: 0644]
tests/.deps/print_info.Po [new file with mode: 0644]
tests/.deps/test_backendhelpers.Po [new file with mode: 0644]
tests/.deps/test_cap.Po [new file with mode: 0644]
tests/.deps/test_getset.Po [new file with mode: 0644]
tests/.deps/test_internals.Po [new file with mode: 0644]
tests/.deps/test_kdb.Po [new file with mode: 0644]
tests/.deps/test_key.Po [new file with mode: 0644]
tests/.deps/test_ks.Po [new file with mode: 0644]
tests/.deps/test_mount.Po [new file with mode: 0644]
tests/.deps/test_serialize.Po [new file with mode: 0644]
tests/.deps/test_split.Po [new file with mode: 0644]
tests/.deps/test_stream.Po [new file with mode: 0644]
tests/.deps/test_trie.Po [new file with mode: 0644]
tests/.deps/test_type.Po [new file with mode: 0644]
tests/.deps/test_xml.Po [new file with mode: 0644]
tests/.deps/tests.Po [new file with mode: 0644]
tests/Makefile.am [new file with mode: 0644]
tests/dbelow.c [new file with mode: 0644]
tests/filesys.xml [new file with mode: 0644]
tests/fstab-cmp.xml [new file with mode: 0644]
tests/fstab.xml [new file with mode: 0644]
tests/hosts.xml [new file with mode: 0644]
tests/key-cmp.xml [new file with mode: 0644]
tests/key.xml [new file with mode: 0644]
tests/keyset.c [new file with mode: 0644]
tests/keyset.xml [new file with mode: 0644]
tests/others.c [new file with mode: 0644]
tests/passwd.xml [new file with mode: 0644]
tests/print_info.c [new file with mode: 0644]
tests/test_backendhelpers.c [new file with mode: 0644]
tests/test_cap.c [new file with mode: 0644]
tests/test_getset.c [new file with mode: 0644]
tests/test_internals.c [new file with mode: 0644]
tests/test_kdb.c [new file with mode: 0644]
tests/test_key.c [new file with mode: 0644]
tests/test_ks.c [new file with mode: 0644]
tests/test_mount.c [new file with mode: 0644]
tests/test_script.sh [new file with mode: 0755]
tests/test_serialize.c [new file with mode: 0644]
tests/test_split.c [new file with mode: 0644]
tests/test_stream.c [new file with mode: 0644]
tests/test_trie.c [new file with mode: 0644]
tests/test_type.c [new file with mode: 0644]
tests/test_validate.sh [new file with mode: 0755]
tests/test_xml.c [new file with mode: 0644]
tests/tests.c [new file with mode: 0644]
tests/tests.h [new file with mode: 0644]
xmlschema/Makefile.am [new file with mode: 0644]
xmlschema/elektra.xsd [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..b64793e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,42 @@
+Avi Alkalay <avi@unix.sh>
+Concept, architecture, development, manuals, previous website
+Backends: berkleydb, filesys, fstab
+Devel/Test on: redhat.ia32, fedora.ia32, redhat.ppc, redhat.s390, aix.ppc
+
+
+Markus Raab <elektra@markus-raab.org>
+unstable development, previous website, documentation, tutorial
+Backends: ini
+Binding: cpp
+Devel/Test on: Debian Etch, Freebsd, Sun OS 4.0, AIX TRu64 Unix
+
+
+Yannick Lecaillez <yl@itioweb.com>
+Mandriva packaging, adaptation for using autotools & libtool, website hosting.
+Devel/Test on: Mandriva x86
+
+
+Jens Andersen <jens.andersen@gmail.com>
+nss-elektra, development and porting.
+Devel/Test on:
+
+
+Patrick Sabin <patricksabin@gmx.at>
+unstable development, Python kdb
+Bindings: python
+Devel/Test on: Debian Testing
+
+
+Pier Luigi Fiorini <pierluigi.fiorini@mockup.org>
+pkg-config file
+Devel/Test on:
+
+
+Rèmi <remipouak@yahoo.fr>
+XML schema
+Devel/Test on:
+
+
+Studio-HB <contact@studio-hb.com>
+Elektra logo, website design.
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..686759e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,30 @@
+This is a New BSD license as in http://opensource.org/licenses/bsd-license.php
+
+Copyright (c) 2004, Avi Alkalay
+ Some rights reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+- Neither the name of the Elektra Project nor the names of its contributors may
+  be used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..7cc091e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1267 @@
+* Tue 16 Oct 2008
+- ksCopy
+- ksNeedSort removed from public header
+- ksNeedsSort renamed
+- ksDup
+- ksClear removed from public header
+- ksSort: example, docu
+- ksGetSize
+- ksAppendKey, ksAppend
+- ksPop
+- ksHead, ksTail
+- ksGetCursor, ksSetCursor
+- ksLookup
+- removed ksLookupByString, ksLookupByBinary
+- ksNew
+- errno away from keyset
+- kdbOpen, kdbClose
+- kdbGet
+- stdio.h and errno.h away from kdbos.h
+- kdbGetByName
+- kdbGetKey, kdbSetKey
+- removed kdbStatKey (trivial)
+- kdbGetString, kdbSetString, kdbRemove
+
+* Wed 15 Oct 2008
+- keyName, keyBaseName and keyOwner return 0 on 0 pointer
+- keyOwner, keyGetOwnerSize, keyGetOwner
+- keySetOwner
+- keyValue, keyGetValueSize
+- keyGetString, keySetString, keySetRaw(removed)
+- keyGetBinary, keySetBinary
+- comments
+- keyNew
+- errno bei key*.c weg
+- errno in backend docu weg
+- DESIGN document updated
+- keyName, keyBaseName, keyOwner, keyValue, keyComment synced
+- test cases for binary data
+
+* Tue 14 Oct 2008
+- keySetDir, keyGetMode, keySetMode
+- enum types are now integer types
+- hide enum for flags: keyflag_t keyswitch_t
+- keyGetType, keySetType
+- ATime, MTime, CTime
+- keyName
+- keyGetName
+- keySetName
+- keyFullName
+- keyBaseName
+- keyAddBaseName, keySetBaseName
+
+* Mon 13 Oct 2008
+- keyStat, keyRemove
+- remove SYNC flag when removing key
+- keyNeedsStat, keyNeedsSync, keyNeedsRemove
+- keyNeedsStat, keyNeedsSync, keyNeedsRemove renamed to
+  keyNeedStat, keyNeedSync, keyNeedRemove
+- keyIsSystem, keyIsUser
+- keyIsBelow, keyIsDirectBelow
+- keyIsInactive
+- keyIsDir, keyIsBinary, keyIsString
+- @errors renamed to @err
+- keyGetUID, keySetUID, keyGetGID, keySetGID
+- keyGetType, keySetType
+
+* Sat 11 Oct 2008
+- Versioning for backends
+- check all functions again:
+- keyDup
+- keyCopy: removed keyClear, keySetName(,0)
+- keyDel, keyIncRef, keyDecRef, keyGetRef: example
+
+* Wed 08 Oct 2008
+- print_info outputs limits, constants and sizeof of elektra types
+- examples now compile
+- added iterate example
+- BUG: segfault on xml import/export (reason: closed library too early)
+- xml is again default mode for exporting
+- always export recursive
+- run test_script
+- add xml tests in scripts
+- validate -cmp files
+- now use type_t for types
+- eliminate [u]int8,16,32_t
+- eliminate kdbStrError
+- make examples more iso c
+- remove keyCompare and ksGetCommonParentName
+
+* Thu 07 Oct 2008
+- Copy autoconf files instead of linking
+- exportsymbols.sh now generate more ansi conforming code
+- libloader: extern in header and more strict function prototype
+- fstab.h added, better declarations
+- passwd.h better declarations
+- template.h better declarations
+- dont use (), use (void)
+- small fixes in headers
+- libelektratools
+- make not exported functions static
+- uid, gid at one place
+- header cleanup
+- compile with icc -Wall
+- dont use multiple function calls as parameters
+- keyIsBelowOrSame static
+- some casting
+- dont define DEBUG by flag, use config.h
+- define VERBOSE (as 0)
+- only use valgrind when available
+- check for stdio.h
+- some things I broke with icc transition
+- valgrind should now check correctly
+- kdbOpen(void) does not accept arguments anymore
+- source environment for zsh works
+- removed some TODOs
+- remove some spaces in kdb.h
+- kdbos.h cleanup: POSIX and WIN32 part
+- KeyCursor renamed to cursor_t
+- use the enum option_t instead of unsigned long
+- KDBOptions renamed to option_t
+
+* Thu 02 Oct 2008
+- svn version for latest test
+- ELEKTRA_VERSION_API in configure.ac
+- check funcs of testing suite
+- search path
+- Docu about search path
+- libdir, ulibdir added in search path
+- hlvlbackenddir used for libelektratools
+- typedef corrected
+
+* Wed 01 Oct 2008
+- BUG 37: uid/gid mismatch
+- kdbSetKey use const as second parameter
+- don use name elektra in kdb.h
+- some updates to KDB in cpp binding
+- Mac Port
+- dont use -n or -e for echo
+- dont use symbols starting with _
+
+* Mon 29 Sep 2008
+- add canonical target
+- remove AC_LIBTOOL_WIN32_DLL
+- using libtool support from sox
+- use INCLUDES for -I include and INCLTDL
+- dont cd to LIBLTDL, it could be -lltdl (on grml 1.1 second install)
+- check HAVE_LIBLTDL header
+- dont use ENOKEY (not posix) use EFAULT instead
+
+* Fri 26 Sep 2008
+- xsltproc works again
+- don't prefix slash at end of docbook path
+- check the files and not the path
+- cleaned up capability (will use other api later on)
+- dont provide mount API
+- exclude most of kdbc* functions (not needed)
+- capability removed from documentation
+- add get (ByName) in c++ binding
+- remove subsections in main page
+- rename keyName to keyname in parameter
+- give trivial kdbRemoveKey away
+- regroup kdb*
+- renaming of libhelper files
+- module section naming
+- use singular
+- KeyDB renamed to KDB
+- changed pictures
+- KDBHandle renamed to KDB
+- kdbGetByName now handles mountpoints correctly
+(and deserves to be in highlevel)
+- preload now directly uses /etc/hosts and /etc/fstab for system
+- but continues to use /tmp for user
+- kdbGetHelper now used by kdbGetKey and kdbGetByName
+- hosts comments
+- use bigger buffer
+- be more safe on buffer limits
+- add comments in hosts.xml
+- write examples and update tutorial
+- kdbGetKey should set errno to KDB_ERR_NOTFOUND
+- versions fixed, provide kdbStrVersion
+- use rm -f instead of -rm
+- remove keyName functions and keyNext (why was that there?)
+- the great renaming: bringing key in its modules
+- bringing keyset in correct order
+- ksAppendKeys renamed to ksAppend (last plural eliminated)
+- ksAppend renamed to ksAppendKey
+- issues on numbers fixed
+- all numbers may return -1
+- removed print_splitted
+
+* Thu 25 Sep 2008
+- KDB_O_NORECURSIVE now only gives back keys direct below keys
+- fix a bug in compare_keyset (it removed value/comments on request)
+- merge details of kdbSet and kdbGet together
+- doxygen updated (to 1.5.6)
+- document parameters
+- UID, GID docu update
+- make keyIsDir more clearly
+- removed kdbGetRootKeys
+- removed kdbSetKeys and kdbGetKeys (plural)
+- removed kdbGetChildKeys (has swapped parameters)
+- introduce kdbGetByName with cascading (replaced kdbGetChildKeys)
+- fixed name bugs in ksLookup()
+- remove user code of kdb-tool which implemented the same
+- 32 bit issues
+- make clean more robust
+- kdb info prints where key is located
+
+* Wed 24 Sep 2008
+- introduce libhelper
+for system dependent but backend independent functions
+- source and folders in CODING document
+- helpers is not a backend anymore
+- remove kdbc properties
+- check for failed opendir (may fail because of permissions)
+- place readlock in hosts after check if open failed
+- use const char for kdbc* char properties
+- give correct uid, gid so that keys can be compared
+- use DEBUG && VERBOSE to be only verbose when debugging is enabled
+- rename access to mode
+- make uninstall install runs fine again
+- --help prints default locations
+- check for sed
+- remove hook for libelektratools
+- provide uninstall hook for default backend
+- will now use "default" backend rather then "filesys" as default
+
+
+* Tue 23 Sep 2008
+- add backend helpers
+- use c++98 std for c++
+- add dependency to filesys and preload
+- kdbbackendhelpers.c disappeared
+(libelektra should now be clean ansi build, see next commit)
+- now compiles without warnings (but not ansi flags)
+- core and cpp binding compile with ansi flags
+- all parts should have DBG and OPT cflags
+- added time.h header
+- remove kdbi* functions out of kdb-tool
+- ifndef TIME_H should be ifdef!
+- bring ks and key streaming functions in other order
+- header files rewriting, always HAVE_FILE_H ifdef around
+- implement kdbiStrCaseCmp
+- ctype for islower
+- move APPROXIMATE_NR_OF_BACKENDS to header
+- delete langinfo.h, sys/* dependencies
+- removed kdbPrintError
+- core can only print something when DEBUG is enabled
+- changed examples and kdb-tool a bit to work without kdbPrintError
+- remove unistd.h header files
+- errnosaver added for filesys, hosts and fstab
+- dont fill up uid, gid and username properties
+- dont fill up uid and gid in keyInit
+- text for keyCompare reformated
+- dont link against helper library
+
+
+* Mon 22 Sep 2008
+- autoconf setup revisited
+- group together similar setup items
+- define DEBUG in config.h
+- define the conditional DEBUG
+(should always be set together and not confuse)
+- DESIGN document updates
+- some more code examples
+- rename keyGetReference to keyGetRef
+- don't use KEY_NEW as first parameter, use 0
+
+* Son 21 Sep 2008
+- added REGEXP and explanation how to use
+- regenerate error codes
+- leave comments of sections in error codes
+- add url for bug reports
+- clear returned so that updating keyset works
+
+* Fri 19 Sep 2008
+- disable iconv conversations by default
+- Use correct help strings for debug
+- don't check iconv header file, fork and signal
+- add tests for code conversations
+- Filesys will now return -1 on UTF8Engine errors
+- added tests for UTF8 needed and conversation
+- added more docu to UTF8 engine about the problem
+
+* Thu 18 Sep 2008
+- remove dependency from pthread.h
+- dont store pid and tid in kdbhandle
+- locking was disabled
+- check for more headers, funcs
+- dont fail if no locking in hosts
+- use KDB_ERR_NOLOCK
+- make distcheck works
+- leak: delete append keyset in filesys in error condition
+- use init() for every testcase
+- fail without error when backends are not available
+- srcdir file now works with empty environment
+- delete own malloc for tests
+- dont use own setenv declaration (fails when its in header)
+- dont leave files behind
+- cleanup in error condition in test_kdb
+- some more cleanup
+
+* Tue 16 Sep 2008
+- functional example
+- remove ksForEach and ksFilter out of elektra (too easy to implement)
+- delete keyset in test_kdb
+- ksPop behaves more sane
+- keyDel is much more safer (hard to do it wrong now)
+- remove functional from example
+- decrement is now something else then delete (cpp binding)
+- keyIncRef and keyDecRef introduced (keyInc renamed)
+- links will not make it into 0.7.0
+
+* Mon 15 Sep 2008
+- kdb -R implemented
+- passwd no segfault when it cant open file
+- REMOVEONLY works
+- missing sort before kdbSet_backend()
+- document kdbGet() options much better
+- filesys now only stat and do not open file
+- propagate KDB_O_NOSTAT to parentKey
+- add testcases for nostat and statonly
+- better always duplicate parentKey
+- done that in fstab (rewrite get)
+- clear returned before appending appended
+- filesys now handles stat and lock
+- filesys optimization: not appending to returned
+- now remove with KDB_O_REMOVEONLY
+- use init() in test_kdb
+- only remove value/comment when backend supports kdbcGetnoStat
+
+* Fri 12 Sep 2008
+- preserving of comments in /etc/hosts now work
+- wrote new append_comment function to catch comment and empty lines
+- wrote find_comment to deduce correct length (sscanf could not do that)
+- write init() function for tests (clearenv + loadToolsLib)
+- remove Link keys
+(not working anyway, not fitting to access logic and not tested...)
+- remove *parent functions
+(real C programmers know how to concat strings)
+- renamed enums
+- BUG: changed KEY_VALUE flag? in keyNew
+- BUG: keyCompare returned KEY_FLAG_SYNC
+- give away the nasty keyCompare code
+- test keyCompare
+- renamed warn_if to warn_if_fail
+- lock/unlock in hosts
+- kdbbLock functions set errno themselves
+- making hosts backend much faster
+
+* Thu 11 Sep 2008
+- The final libelektratools release for 0.7
+(more work will be investigated after release)
+- libelektratools and kdb-tool is not part of the official 0.7 release
+- remove streaming of cpp binding
+- add enum for streaming into kdbtools.h
+- DYN_LINK macro for weak dependency (not shown in ldd)
+- add -G -O options in kdb-tool to generate c code and output per line
+- move ksLookupRE and ksCompare to libelektratools (commented out)
+- asprintf removed (not ansi)
+one step closer to use only kdbi* memory management
+- command export now also exports root key
+
+* Wed 10 Sep 2008
+- Don't track .gdb_history files
+- document only full get
+- always add the parentKey in filesys
+- watch out that all keys clear their KEY_FLAG_SYNC flag
+- note, every modification resets the sync flag
+- reorganize kdb options
+- rename KDB_O_IGNORESYNC to KDB_O_SYNC
+- rename KDB_O_DELPARENT to KDB_O_DEL
+- rename KDB_O_POPPARENT to KDB_O_POP
+- write nomeclature in kdbmain.c
+- delete not used options
+- reorganize test_kdb (create_root_key and create_conf)
+- Test cases reorganized
+- use setenv
+- create_root_key now creates for a specific backend
+- create_conf for a specific file
+- #ifdef all headers, 
+- remove warings
+- xml cleanup: #ifdefs in includes
+- memory leak in ksToStream
+- remove grp and pwd dependency
+- remove non-ansi dependence (isdigit, mkstemp, write)
+- give code example how to check if xml is valid
+- test uid, gid and access
+
+* Tue 09 Sep 2008
+- delete ksPop()ed keys
+- doxygen: sort by group names
+- update mainpage
+- make sections in kdbGet() and overview of backends
+- update and explain conditions more detailed
+- add an example for kdbGet_backend
+- add option_t::KDB_O_NOSTAT in docu
+- define groups for backendhelpers, backendhandle
+- add section cascading
+- stat logic changed, STAT remains after key was stated
+- SYNC will be deleted for every key synced in backend
+- use POP for returned keyset
+- Introduced more error codes to be used by backends
+- explain how to properly update the keyset
+- give some more links @see in documentation
+- BUG: ksAppendKeys may be one byte to small
+
+* Mon 08 Sep 2008
+- Reset the update kdbGet mechanism
+- add keyCopy and keyClear
+- fix docu for keyDel
+- add testcase for keyCopy, keyClear
+- some warnings less
+- BUG: keySetName(k,"system*") did not reset owner
+- add ksCopy, ksClear
+- make keyset more robust (memory errors)
+- warnings in test cases
+- Introduced KDB_O_POP
+- BUG: ksSetCursor on rewinded keyset
+- make sections in docu for ksLookup
+- test cases for lookup with pop
+
+
+* Fri 05 Sep 2008
+- KDB_O_WITHOWNER was wrong in docu of ksLookup
+- extend and test example application
+- add keyIsDirectBelow() for direct relation ship (parent to son)
+- update ksLookup Docu (KDB_O_WITHREMOVE?)
+- BUG: ksLookup() did not sort!
+- updating of a keyset now works
+- functional programming: ksForEach, ksFilter added
+
+
+* Thu 04 Sep 2008
+- write documentation about ENVIRONMENT
+- KDB_USER now can override USER
+- make sure that proper owner is written (+ testcases)
+- don't add owner in keyGet* methods
+- KDB_DIR let you specify the exact user-configuration directory
+- preload where prints the path where your configuration lies
+- document errors for kdbbGetFullFilename
+- added KDB_ERR_TOOLONG for a string too long which cant be trunced
+- added KDB_ERR_CONVERT for utf8 convert errors
+- BUG: incorrect return values (-1) for keyIsUser and keyIsSystem lead
+to keyGetNamespace failure, kdbbGetFullFilename was affected
+- BUG: handle situation correctly where buffer does not fit filename
+- BUG: dont remove escaped slashes
+- simplify kdbGetFilesys a lot by using kdbbGetFullKeyName
+- kdbbFilenameToKeyName handle too long string correctly
+- test cases for encoding
+- Locking of files in filesys implemented
+NOTE: thus only a single key is locked it only prevents from shredded files
+- keys in kdbGet will now be updated when already in returned
+
+
+* Tue 02 Sep 2008
+- introduced internal kdbi* functions in internals.c
+- renamed strblen to kdbiStrLen
+- introduced kdbiMalloc, kdbiFree, kdbiStrDup
+- renamed srealloc to kdbiRealloc
+- enhanced backendhelpers and renamed to kdbb* functions
+- renamed kdbbLock and kdbbUnlock
+- renamed kdbbEncode and kdbbDecode
+- renamed kdbbEncodeChar and kdbbDecodeChar
+- all functions are defined in kdbbackend.h
+- added test for backendhelpers
+- added test for internals (with small multichar seq test)
+- distinguish between PATH_SEPARATOR and KEY_SEPARATOR
+- added MAX_KEY_LENGTH to say explicit that the length of
+the key name is limited for filesys
+- KDB_KEY_MOUNTPOINTS holds keyname for mountpoint database
+(system/elektra/mountpoints)
+- KDB_KEY_USERS holds keyname for user database (system/users)
+- the *_LEN makros give their sizeof value
+- introduced makro variables for KDB_DB_HOME (fallback where /home resides)
+- keys for user/ hierarchy may have different locations on disk now, see
+kdbbGetFullFilename
+- memory leak in passwd backend + fclose was missing
+- renamed kdbbKeyNameToRelativeFilename and kdbbKeyCalcRelativeFilename
+and return -1 on failure
+- preload now return error codes if it worked
+
+* Mon 12 May 2008
+- updated documentation of kdbGet_backend and kdbSet_backend
+- use post, invariant and pre conditions to document
+- introduce @err tag to describe how errno is propagated
+- add example path to include files in documentation from there
+- Use @since tag for pluggable backend description
+- updated kdbSet and kdbGet documentation:
+-   Errors, Post, Pre cond, Return values
+- Explain return value in @return value
+- rewind keyset
+- basic implementation of kdb class in cpp binding
+- add keyRemove and keyStat in keymeta
+
+* Thu 08 May 2008
+- enabling and disabling of backends now work
+- ini compiles again
+- mntent.h fix
+- berkleydb compiles fine but does not link
+- added enabling and disabling of bindings
+- cpp binding keyset finished except iterator
+
+* Thu 24 Apr 2008
+- Key support Reference Pointer Semantics
+- Some Basic Functions for Keyset (non-functional)
+- Some Basic Tests for Keyset
+- ksLookup (KeySet, Key,..) added to docu
+- sed changed for macosx compatibility
+- 0.7.0rc3 announcement
+
+* Mon 21 Apr 2008
+- kdb::KeySet output, generate,..
+- added test_keyset.cpp
+- fixed signature ssize_t ksToStream
+- changed signature keyDel()
+- added keyInc() (increment Reference)
+- keyDel() also before appending (always after ksPop()!)
+- updated documentation for ksPop(), ksCurrent(), ksNext()...
+- Streaming got its own Module in documentation (group keystream)
+
+* Sun 20 Apr 2008
+- also set CXXFlags
+- namespace directly in kdb.h for c++
+- Key for cpp binding now complete with lots of tests
+- use const
+- casting between kdb::Key and ckdb::Key for KeySets
+- copy constructors
+- adding keyVNew and ksVNew to pass va_list and use it in c++ binding
+
+* Tue 15 Apr 2008
+- Don't output error for positive kdbSet return value
+- xml pretty printing
+- fix wrong parent at end in ksToStream
+- string is default type for xml without type=""
+- fix extra_dist some missing files
+- Add version macros
+- remove , at end of enumeration lists
+
+* Sat 22 Mar 2008
+- kdbSetKey highlevel now works with capabilities
+- kdbSet docu
+- make parentkeys to work in kdbSet
+- splitting keys now works in n*b instead of n^2*b and preservers order
+- split keysets in kdbprivate.h, test it in test_split.c
+- 0.7.0rc2 announcement
+
+* Thu 26 Feb 2008
+- Cleanup options
+- Initialized mask removed in keyswitch
+- Changed indentation to tabs
+- Lookup and Stream have their own options
+- don't make uid gid depended on who exports it
+- some newlines removed
+- keyOutput: only output what is desired
+- Adding comparing files in Testcase
+- ksLookup* set cursor (ksCurrent) correctly
+- ksCurrent set correctly after failed kdbSet()
+
+* Sat 09 Feb 2008 Markus Raab
+- atime, mtime, ctime document + set functions (kdb.h)
+- document kdb*_backend functions
+- document kdb functions
+- make capabilities obligatory
+- add authors, licence in capabilites
+- capabilities workaround in kdbGetKey and kdb get
+- make mountpoint when mounting a backend with preload
+- passwd does not need to declare onlyFullGet
+- add kdbGetCapability and kdbGetMountpoint in namespace kdb
+- rename kdbGetValue, kdbSetValue, kdbSetValueByParent and kdbSetValueByParent
+to kdbGetString, kdbSetString, kdbSetStringByParent and kdbSetStringByParent
+- keyValue unchanged because it is related to value, that means string, binary
+or any other type
+- keyGetValueSize also unchanged because of above reason
+- writing tests for it in test_mount
+- check return value kdbGet and kdbSet of -1 if you want to check error
+
+
+* Thu 06 Dec 2007 Markus Raab
+- Renaming of header files
+- kdbMount now takes a key mountpoint, which has as value the backend
+- KDBHandle is now like Key and KeySet a private structure with public typedef
+- KDBBackend is now gone and #define KDBHandle to void too
+- helpers are now in kdbprivate.h
+- kdbbackend.h now will do include everything for backends
+- kdb.h should do include everything needed for extern programs
+- Documentation for KDBHandle
+- TODO update
+- added capability methods
+- document kdbMount, kdbUnmount
+- don't rely on private structures in tests.h
+- fix make dist
+- 0.7.0rc1 announcement
+
+
+* Wed 05 Dec 2007 Markus Raab
+- KDB_ERR_NODOMAIN renamed to KDB_ERR_NOOWNER
+- KDB_O_NOSORT renamed back to KDB_O_SORT
+- ksLookup KDB_O_NOALL introduced
+- ksNeedSort() and ksGetAlloc() introduced
+- lookup: owner strcmp more efficient and also sorted that way
+- BUG: ksGetCommonParentKeyName works even if nothing found
+- dont ksDup in ksGetCommonParentKeyName
+- size_t used for keysets (we also want large keysets)
+- resize keyset down to KEYSET_SIZE
+- remove kdbGetBackendName and kdbGetMountpoint
+- kdbhGetMountpoint is a key: name is point, value is backend
+- allocation testing
+- kdbMount, kdbUnmount to kdb.c
+- use kdbOpenBackend and kdbCloseBackend as only place to open/close backends
+- OpenMapper, CloseMapper are function pointers to open and close a backend
+
+
+* Wed 21 Nov 2007 Markus Raab, Patrick Sabin
+- BUG: keyIsLink broken fixed
+- BUG: when there is no comment, value filesys won't store type fixed
+- BUG: keyAdd/SetBasename do realloc something wrong and do not set nameSize
+- rewrite serialization
+- keyCompose: serialize a key without any malloc
+- added keyGenerate and ksGenerate
+- use FILE *stream for Output/Generate
+
+* Wed 19 Nov 2007 Markus Raab
+- Capabilites added
+- Backends can now represent what they support
+
+* Sun 11 Nov 2007 Markus Raab
+- Make test cases run again
+- Find memory leaks
+- Naming conventions
+- Parameter discarding of qualifiers
+- Header conventions
+- File headings
+- Variable names + delete unneeded variables
+- make all tests pass again
+- make fstab compile again
+
+
+* Wed 07 Nov 2007 Markus Raab
+- Code Coverage Tests included
+- With --enable-gcov you enable building of code coverage
+- Use it to see what lines of code are covered by tests
+
+* Tue 06 Nov 2007 Patrick Sabin
+- introduced Trie for lookup of mountpoints
+- dynamic mounting with kdbMount and kdbUmount
+- test cases for trie
+- trie resides in KDBHandle
+- kdbLibMounting.h introduced
+- kdbos.h introduced
+- ksLookup only search in not removed keys
+
+* Fri 02 Nov 2007
+- Remove next Pointer in Key
+- Use Array in Keyset
+- Introduce new style ksNew
+- ksDup introduced
+- some const identifiers introduced for keyset
+- InsertKey(s) is history
+- Cursor functions added for keyset
+- next, start and end pointer removed
+- reference for keys: keysets will only delete keys when they
+       have the only copy of it
+- ksToStream, ksCompare, ksLookupRE mailfunc, commented out
+
+* Wed 24 Oct 2007
+- Reintroduce kdbGet recursive code
+- Now delete keys when not needed
+- kdbGetKeys needs KDB_O_INACTIVE to get a single key
+- When pop a key, ks->next is now 0
+- Makros in tests now {embraced}
+- to see if a function failed, take the difference of nbError
+
+* Wed 17 Oct 2007
+- use old type system
+- getset tests
+- access types tests
+- KEY_SWITCH_MODE renamed to KEY_SWITCH_ACCESS
+- Introduce ksStart to replace ksRewind
+
+* Thu 04 Oct 2007
+- valgrind support for tests added
+- keySet/Get String/Comment for NULL changed
+- keyGetValue/CommentSize returns 1 to have appropriate space to malloc 1 byte
+- xml tests commented out, problem with change above
+- userDomain (?) renamed to owner
+- updated documentation of _Key
+- recordSize deleted
+- benchmarks added: threads, large
+- keyDir: Use executable bits to mark directories
+- filesys cleanup: don't support old key-style and remove pointless functions
+- valgrind integrated for tests and benchmarks
+- KEY_SWITCH_DOMAIN -> KEY_SWITCH_OWNER
+       
+* Sun 23 Sep 2007
+- kdbHandle and KDBEXPORT only insist of 4 functions (open, close, get, set)
+- kdbHighLevel only require kdbGet and kdbSet, but considered broken
+- kdbLibSym returns kdbLibFunc, which is a pointer to a function, not data
+- added -debug compiler options
+- removed warnings all over the code
+- keyvalue cleanup, same meanings and documentation of errno
+
+* Mon 03 Sep 2007 Markus Raab <elektra@markus-raab.org> and Patrick Sabin
+- kdbSet restructure: only kdbSetKeys communicate with backend
+- _KDBInfo added
+- kdbcapability.c added, old KDBInfo is there
+- kdbResolveKey is in backendhelpers.c
+- kdbprivate.h need not to be included if kdbbackend.h is
+- docu for highlevel functions too
+- doxygen fixes
+- BUG: recordSize, not address of operator (resolved)
+- Sort KEY_FLAG_REMOVE correctly
+- KEY_FLAG_STAT first hit
+- BUG: next pointer was missed in kdbSetKey (resolved)
+- BUG: access not correct for stat key
+- ksPopLast removed because it needs iterating over all keys
+
+
+* August 2007 Markus Raab <elektra@markus-raab.org> and Patrick Sabin
+- Doxygen warnings/error away for cleaner documentation
+- Writing exhaustive backend tests: xml, kdb
+- keyIsInactive added, testcases for inactive keys
+- .. and . are handled correctly in keySetName
+- Trailing slashes at system and user are handled correctly
+- some kdb (1) tests added
+- keyNeedsRemove added
+- keyRename test cases
+- sorting now considers if marked for remove [more testing needed]
+- tests.c now have the code from tests.h
+- ksSort testcase added
+- keyNew supports KEY_SWITCH_REMOVE
+- bug in keyNeedsSync and keyNeedsRemove, not 1 returned
+- prefix with 0 in date (-d, -l options)
+
+
+* July 2007 Markus Raab <elektra@markus-raab.org>
+- xml test update
+- xml files validate
+- validate .xml files in tests
+- updated xmlschema documentation
+- schema marked as unstable
+- schema licence bsd
+
+* May 2007 Markus Raab <elektra@markus-raab.org>
+- kdbKeyChildKeys in backend contains no sort, filter and recursive logic
+- Getting Code stable to begin implementing
+
+* March 2007 Markus Raab <elektra@markus-raab.org>
+- Start support multiple backends
+- using git-svn
+- TODO updated
+- get out binary presentations
+- kdb doc return value ChildKeys update
+- added XML tests
+- get some common code to tests.h
+
+* Dezember 2006 Markus Raab <elektra@markus-raab.org>
+- unstable fork
+- using darcs
+- c++ compatible includes
+- amc_time documentation updates
+- keyDup now really duplicates a key.
+- Give Link, Rename and Remove away
+- Remove monitor functions
+- Support removing with setting type to KDB_TYPE_REMOVE
+- Support atomic SetKeys
+- Give kdbOpenDefault, kdbOpenBackend away
+- Change all function to use KDBHandle *
+
+* Mon Oct 23 2006 Avi Alkalay <avi@unix.sh>
+- API started to change to support dir keys with value
+- Initial support in filesys backend, mainly on kdbSetKey_filesys()
+
+* Sun Oct  8 2006 Patrice Dumas <pertusus@free.fr>
+- fix under and over quoting in configure.ac
+
+* Fri Sep 11 2006 Avi Alkalay <avi@unix.sh>
+- Removed setuid() and +s from kdbd
+- Changed daemon socket file to /var/run/kdbd/elektra.sock
+- Created an init script for the daemon
+- Updated spec for changes
+- The daemon now creates and removes pid and sock files in/from standard dirs (/var/run/kdbd)
+
+* Thu Sep 07 2006 Avi Alkalay <avi@unix.sh> 0.6.4
+- Fixed a key ownership bug on BDB::keyFromBDB() when running together with the daemon
+- kdbhGetBackendName() now returns correct daemon backend name
+- The daemon now calls setuid() for correct access permissions
+- kdbd now installs with mode u+s with RPM
+- Closing version 0.6.4
+
+* Thu 24 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- build examples as part of make check
+- fix doc to build out of source
+- distribute doc files that requires special tools
+- use $(transform) when installing man pages
+- condition man2html build on man2html being detected
+- put doc generic targets out of conditionalized sections
+- clean a bit doc dependencies, avoid redundancy, try to be more portable
+
+* Fri 18 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- have static libelektra depend only on backends static libraries
+- call AC_GNU_SOURCE if CFLAGS is empty
+- add -std=c99 -pedantic to CFLAGS if CFLAGS is empty
+
+* Wed 16 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- don't set _GNU_SOURCE in the default case, plus minor fixes
+- fixes for experimental features
+
+* Tue 15 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- add a m4 directory for autoconf macros
+- use an autoconf macro from gnulib to set POSIX exensions on various systems
+- fix in BSDgetopt
+
+* Mon 14 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- determine which backends are compiled in and if libelektratools is compiled or not in ./configure, an substitute correct value in SUBDIR (idea of Yannick)
+- rewrite exportsymbols.sh to only use backend names to recreate exported_symbols.c
+- autogenerate kdbprivate.h in order to avoid hardcoding /etc/kdb, but instead honor sysconfdir
+- Fixes to have -std=c99 proceed until the end
+- Detect a working getopt, and if not found use getopt from openssh
+
+* Sun 13 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- fixes for compatibility with sunos 5.8 (solaris 2.8, solaris 8)
+- use LIBICONV in link commands
+- compile fstab backend conditionally on having glibc-like getmntent and friends
+- compile daemon conditionally on having struct ucred defined
+- changes in the nm and amk call to accomodate for solaris 8
+- always use install_sh since install -c needs a directory arg on solaris 8 (an autoconf bug?)
+
+* Sat 5 Jun 2006 Yannick Lecaillez <sizon5@gmail.com>
+- fixed keyFileSerialize() in filesys.c : When binary source data
+- fixed kdbGetKeyWithOptions() in berkeleydb.c : key->next was reset, key lost position in the keyset.
+- fix keySetRaw() in key.c: When data is NULL or size = 0, key's data was cleaned but key->dataSize wasn't set to 0 was 1 byte long, write to non-allocated address occured.
+
+* Fri 4 Jun 2006 Yannick Lecaillez <sizon5@gmail.com>
+- Add default implementation of kdbRename()
+- Removed some debugging output from kdbd
+- Add a static version of kdbd
+- fixed kdbRename() in kdbd/libelektra-daemon
+
+* Thu 3 Jun 2006 Yannick Lecaillez <sizon5@gmail.com>
+- Fix backends/daemon compilation warnings
+- Add a testing suite for key struct and backends
+- Enhance elektra static library generation (exportobjects.sh and exportsymbols.sh)
+- Add section 'libs.private' into elektra.pc for static compilation
+- Enhance build dependency
+- Add a resume text in the end of configure script
+
+* Wed 2 Aug 2006 Patrice Dumas <pertusus@free.fr>
+- remove -Wall (and -pedantic -ansi) from Makefile.am
+- add -Wall in configure.ac if CFLAGS is empty and compiler is gcc
+- change make to $(MAKE)
+
+* Sun 30 Jul 2006 Patrice Dumas <pertusus@free.fr>
+- fix many compilation warnings
+
+* Sat 29 Jul 2006 Patrice Dumas <pertusus@free.fr>
+- replace __STATIC by ELEKTRA_STATIC
+
+* Wed 26 Jul 2006 Avi Alkalay <avi@unix.sh>
+- Added -s option to 'kdb ls' to make it more scripts friendly
+- Separated the descriptive help functions of kdb program to help.c
+
+* Wed 26 Jul 2006 Patrice Dumas (applyied by Avi) <pertusus@free.fr>
+- arrange doxygen for out-of-source build
+- add some source files for experimental backends and bindings
+- clean up the clean target in doc/Makefile.am, add an uninstall target
+- don't test for default_backend presence in ./configure since it breaks out of source builds
+- really use the --enable-experimental value
+- libproto.la, backends and tests depends on ../../libelektra/libelektra.la
+- use ../../libelektra/libelektra.la instead of -lelektra to link against in-source library
+- add a link for libelektratools in /usr/lib/elektra
+- build files required by static libelektra before libelektra, and libelektra before the backends
+- use check_ for tests
+- put high level backends in /usr/lib/elektra
+- use .$(OBJEXT) instead of .o
+- honor default_backend for static libelektra dependencies
+- add AC_SUBST(default_dbackend)
+
+* Tue 27 Jun 2006 Yannick Lecaillez <sizon5@gmail.com>
+- New kdbd daemon !
+- Fix UTF8Engine : Check for a null size string
+- Fix Berkeleydb backend :
+- - Check if an UTF8 convertion is done before free memory
+-   (before, there was case where keyToDBD freed Comment and/or Value key pointers.)
+- - Chown user's kdb-berkeleydb dir.
+- - Set correct UID/GID for initial creation of the "user" key.
+- Fix key.c : Set KEY_SWITCH_DOMAIN flags into keySetName()
+
+* Wed Jun 06 2006 Markus Raab 0.6.3
+- added cascading features to kdb
+- kdb ls / and kdb ls /path is possible
+- kdb get /path/to/key gets user or system key
+- Moved help functions of the 'kdb command' to src/kdb/help.c
+
+* Fri Jun 02 2006 Avi and Yannick <yl@itioweb.com> 0.6.2
+- Renamed some sorces on src/libelektra/
+- Backends installation directory moved to /lib/elektra/
+- Adding testing suite for key's name (examples/tstkeyname.c)
+- Adding escaping support for filesys backend (filesys/filesys.c)
+- Fixed segfault when USER env. variable isn't set (libelektra/key.c, filesys/filesys.c)
+- Changed elektraenv.sh to use mktemp instead of ${RANDOM}
+- The doc/standards dir is now included in main RPM
+- Moved bindings dir to src/bindings
+- Fixed a bug in keyAddBasename() related to multiple '/' cleanup
+- All string-size_t-returning methods return sizes including ending NULL
+- Implemented keySetDir() for highlevel dir key permissions and type setup
+- Implemented keySetUAccess() for correct backend key permissions setup
+- Added KEY_SWITCH_UMODE to keyNew()
+- Few backends cleanup to make them more suitable to work under daemon context
+- Removed all memory problems with Valgrind
+- Library version changed to 2.0.1
+- Fixed many build details
+- Fixed many spec details to meet Fedora Extras requirements and standards
+- Updated presentation with some desktop related charts
+- Converted to OpenDocumentFormat::Presentation
+- Included presentation german translation from Markus Raab
+- Presentations taken out from tarball. Now present only on repo
+- Included note on kdb.1 about the dependency on libelektratools.so
+
+* Thu Mar 30 2006 Yannick and Avi <yl@itioweb.com> 0.6
+- Handlerized all kdb*() methods. This is a radical API change thowards a thread-safe API.
+- Changed all backends to work with this change
+- Included kdbh*() methods for better backend manipulation, thowards the possibility of having remote backends
+- Included kdb*Error() methods as suggested by Markus
+- Added KDB_O_ALL option to ksLookupByName & ksLookupRE
+- Fixed bug in keySetName which caused faulty write when key name have trailing '/'
+- Fixed bug in configure.ac to allow installation into lib64
+- Fixed bug in keySetName caused segfault on x86_64
+- Updated Mandriva spec file to allow x86_64 RPMs build
+- Updated old Xorg & SysVinit patches for current API
+- Removed installation of libloader (its a convenient lib)
+
+* Sun Mar 03 2006 Avi Alkalay <avi@unix.sh> 0.5.9
+- Added the ksGetCommonParentName() method to help in XML exporting
+- ksToStream() now correctly handles KDB_O_HIER flag, for nicer XML exports
+- keySetName() now removes repeated '/' when setting key names
+- Fixed a bug in filesys.c::kdbStatKey_filesys() related to link keys
+- Fixed a very stupid bug in key.c::keyCompare()
+- Added logic to key.c::keySetName() to remove extra '/' anywhere on key name
+- Fixed a bug on keyset.c::ksCompare() related to the NEEDSYNC flag
+- Added kdbprivate.h::KDB_METAINFO_SIZE(Key *key) macro, usefull for backends
+- Make 'kdb edit' more robust in terms of completeness when exporting to XML
+- Much improved and working berkeleydb backend. Going towards a high-quality backend.
+- BDB backend moved out from EXPERIMENTAL backends, to the mainstream, cause it is usable.
+- Updated scripts/benchmark-createtree to output nice XML
+- Reorganized #includes
+- Improved some API documentations
+- Included some casts to get rid of compiler warnings
+- Added some screenshots to the documentation
+- DocBook documentation is being reorganized and improved to soon become part of the site
+- Updated the presentation
+- Updated screenshot for 'kdb edit' command
+- Updated some doxy cosmetics
+- Updated kdbOpenBackend() documentation and error codes
+- Updated ksLookupByName() documentation to include more about the method behavior
+- Organizing the daemon backend
+
+* Fri Feb 17 2006 Markus Raab 0.5.7
+- ksLookupByValue now supports cascading
+- Ini Backend is marked experimental
+- Big updates in Ini Backend:
+- posix and parser specific parts seperated
+- support for binary values containing \0 and \n
+- changed to more performantic kdbSetKeys (very buggy)
+
+* Tue Feb 16 2006 Avi Alkalay <avi@unix.sh> 0.5.7
+- Old libregistry and examples are not being compiled anymore
+- BerkeleyDB and GConf backend are set as experimental, and removed from main build
+- Improved documentation for kdbRemoveKey()
+- Created keyIsString() method
+- Corrected link handling on src/backends/filesys/filesys.c::keyFromStat()
+- Benchmark script now sets random key types
+- ksFromXMLfile() now receives a (const char *) instead of (char *) as filename parameter
+- Doxygen is generated with a browsing tree pane
+- Created elektratools.pc for pkg-config
+
+* Sun Feb 12 2006 Avi Alkalay <avi@unix.sh> 0.5.6
+- Fixed a pointer positioning bug in keyStealBaseName()
+- The filesys backend now correctly sets key permissions
+- Fixed correct value comparation on keyCompare()
+
+* Sat Jan 28 2006 Avi Alkalay <avi@unix.sh> 0.5.3
+- Added specification for system's user, group and password
+- Added specification for system initialization
+- Fixed bug related to setting real binary keys
+
+* Sat Dec 03 2005 Avi Alkalay <avi@unix.sh> 0.5.2
+- Cross platform handling (when possible) for UTF-8 conversions with iconv()
+- More detailed error messages on kdb command
+- New kdbGetInfo() and friends for library info gathering
+- Better XML import for nested <key>s
+- Multiple <comment>s allowed in one <key> on XML
+- Pretty complete network configuration tree layout specification
+- Pretty good system signature tree layout specification
+- Tree layout specification for environment vars and shell aliases
+- Tree layout specification for a filesystem mount table
+
+* Wed Oct 26 2005 Avi Alkalay <avi@unix.sh> 0.5.1
+- Migrated to autotools, and updated documentation for that
+- Changed some help stuff on kdb command
+- Included libltdl for cross-platform compatibility
+
+* Sun Oct 02 2005 Avi Alkalay <avi@unix.sh> 0.5.0.5
+- Some final fixes for the complete libkdb.so -> libelektra.so renaming
+- Removed a bug in filesys.c::keyFromStat() regarding reuse of folder key struct
+- Changed some Makefiles to handle the new Homepage/ directory
+- Fixed Makefile regarding the convert-* scripts
+
+* Mon Jul 25 2005 Avi Alkalay <avi@unix.sh> 0.5.0.4
+- libkdb*.[so|a] renamed to libelektra*.[so|a] to avoid conflicts with Kerberos v4
+- Documentation updated to fit
+- Corrected duplicate error codes in the KDBErr enum
+- Some realloc robusteness was added with assert(), thanks to Bardur Arantsson
+- Added in Doxygen: a small guide on how to #include, compile and link with Elektra
+
+* Tue Mar 20 2005 Avi Alkalay <avi@unix.sh> 0.5
+- An almost ready Berkeley DB backend
+- Key, KeySet and KDB classes earned robusteness through the implementation of a new backend
+- All size_t-returning functions now return ssize_t, so a -1 can be checked for errors
+- kdbGetChildKeys() method and similars now return the number of keys retrieved instead of 0
+- The import/export XML file can have nested keys, a real tree of hierarchical keys
+- No need for special XSL stylesheets to build the HTML page. DocBook XSL 1.65.1 is good enough
+- Markus' ini backend now compiles by default and is included in the main RPM
+
+
+* Tue Jan 23 2005 Avi Alkalay <avi@unix.sh> 0.4.11
+- kdbOpen() uses $KDB_BACKEND
+- kdbOpenDefault() ignores env
+- kdbOpenBackend() is pure workhorse for both.
+- Created libkdbtools.so intended to have higher level functions that should not be in libkdb.so
+- libxml dependent code used by kdb.c moved to libkdbtools.so (kdbtools.c)
+- localkdb.c moved to libkdb.c, finally
+- Finished GConf backend, including error handling
+- Added 'undefined' key type to XML schema
+- Improved documentation for backend API
+- New layout style and improvements for API documentation with many new links
+- Better and more examples to the kdb command man page
+- Moved the dtd/ dir to xmldtd/
+
+* Tue Jan 10 2005 Avi Alkalay <avi@unix.sh> 0.4.10
+- Added the libkdb-fstab.so backend to treat the /etc/fstab file as a backend storage
+- Divided Key documentation in several parts
+- Appeared ksPopLast()
+- Appeared the keySteal*() methods
+- Removed env var that defines default backend
+- Experimental GConf backend
+
+* Tue Dec 27 2004 Avi Alkalay <avi@unix.sh> 0.4.9
+- Full nice elegant documented support for dynamic pluggable backends
+- kdb now validates an imported XML against an XML schema, thanks to Remi Pouak
+- -devel RPM includes the framework and examples for backends development
+- elektraenv script is more secure now, thanks to Johan Kiviniemi (ion at ion.yi.org)
+- Removed unused #includes
+- new kdbOpenBackend() and ksPop() methods
+- install examples in elektra-devel/examples/ directory
+
+* Sat Dec 06 2004 Avi Alkalay <avi@unix.sh> 0.4.8
+- Whish me a happy birthday
+- All sources moved to src/
+- New 'kdb help' to show some usage examples
+- enum KeyFlags changed to enum KeySwitch (KeyFlags values maps to new KeySwitch for compatibility)
+- New keyNew(), keyDel(), ksNew(), ksDel() advanced constructors and destructors
+- All documentation and examples changed to increase usage of keyNew()
+- ksSort(), ksHead(), ksTail() methods appeared and documented.
+- ksLookupByName(), ksLookupByValu(), ksLookupRE() appeared as beta and documented.
+- #ifdef __cplusplus to make it C++ safe.
+- Many small fixes that appeared in the switch to keyNew()
+- Many other fixes, code robustness and cleanup.
+- User-defined key types greatly improved, fixed and tested.
+- XML export can now supress obvious UID and GID for 'user/*' keys
+- XML export can now supress user domain for 'user/*' keys
+- New keyNewExample.c and lookup.c on example/
+- New example/keyset.xml with example of complex keys, and how they look XML-exported.
+- XML DTD were droped. Now we have XML schemas, thanks to RĂ©mi <remipouak@yahoo.fr>.
+- pkgconfig support, thanks to Pier Luigi Fiorini <pierluigi.fiorini@mockup.org>
+- Library now has soname etc, to make binary compatibility safe
+
+* Fri Nov 28 2004 Avi Alkalay <avi@unix.sh> 0.4.7
+- Better BuildPrereq in spec file
+- Small updates to documentation
+- New 'kdb mv' command (needs improvement)
+- Updated kdb.1 manpage to include 'kdb mv'
+- The KDB_ROOT env var to kdb command (needs manpage), thanks to Markus Raab
+- Fixed a bug in spec file, section %post
+- Acknowledged comments from https://bugzilla.fedora.us/show_bug.cgi?id=2250
+
+* Fri Nov 05 2004 Avi Alkalay <avi@unix.sh> 0.4.6
+- New method: kdbRename()
+- Updates to spec files, to conform to Fedora conventions
+- First GPG sign
+- Ready to be sent to fedora.us
+
+* Mon Nov 02 2004 Avi Alkalay <avi@unix.sh> 0.4.5
+- Fixed a bug in kdbGetKeyByParentKey()
+- Changed the behavior of kdbSetKeys(): now developer may know where errors occured
+- Changed kdb to to fit new kdbSetKeys behavior
+- Small fixes in the doxygen documentation, improved code examples
+- More efficient documentation build
+
+* Sun Oct 25 2004 Avi Alkalay <avi@unix.sh> 0.4.4
+- Added documentation on how to write new backends
+- Fixed "kdb get" when getting binary values
+- Fixed a bug when converting binary key values to hex digits
+- Fixed a bug in the reverse way: from hex to binary (jens)
+- Added -b to kdb, to set key values with the content of a file
+- Trying to fix something wrong in kdbGetKey()
+
+* Wed Sep 22 2004 Avi Alkalay <avi@unix.sh> 0.4.3
+- Removed kdbOption instantiation from kdb.h
+
+* Sun Sep 12 2004 Avi Alkalay <avi@unix.sh> 0.4.2
+- Fixed a gzip problem in the build system
+- Added ksGetSize() method
+
+* Tue Sep 09 2004 Avi Alkalay <avi@unix.sh> 0.4.1
+- Fixed some wrong charset handling
+- License changed to BSD
+- Updates to documentation
+
+* Tue Aug 31 2004 Avi Alkalay <avi@unix.sh> 0.4
+- Name changed to Elektra
+- rg command changed to kbd
+- registry.h to kdb.h
+- libregistry.so to libkbd.so
+- The former still provided as a wrapper for binary compatibility
+- Most #defines now are enums
+- Enums now documented
+- "*NameSpace()" to "*Namespace()"
+- Appeared keyClearFlag()
+- Changed documentation due to new name
+- Greatly improved documentation. It is now 100% complete
+- API man pages are now from Doxygen
+- Tested build in BSD: iconv is a separate library there
+- Changed some error codes for BSD compatibility
+- Used the "const" parameter modifier everyplace that is needed
+- 'kdb export' now allways exports as UTF-8, regardeless of current encoding
+- 'kdb get' now has a shell mode with -s option
+- elektrasetenv now uses the new 'kdb get' command, and it is less comples
+- elektrasetenv now set environment from system/env and user/env
+- Changes in the XML DTD header due to web site change
+
+* Mon Aug 17 2004 Avi Alkalay <avi@unix.sh> 0.3.1
+- Fixed a bug in keyToStream() regarding XML export of comments
+- Make rg.c compile with older XML library
+- .spec now uses gmake instead of make
+- Moved ENODATA to ENOMSG, for better BSD compatibility
+- Improved the README documentation with packaging info
+
+* Mon Aug 10 2004 Avi Alkalay <avi@unix.sh> 0.3
+- Registry now supports key and key sets CHANGE NOTIFICATIONS
+- New 'rg monitor' command, using notifications framework
+- 100% fully documented methods with Doxygen
+- Thinking seriously in a project name change. I have an idea for a new name
+
+* Fri Jul 30 2004 Avi Alkalay <avi@unix.sh> 0.2.9
+- Doxygen finaly generates methods documentation. Not very nice though.
+- Included in -devel RPM
+- Included Advogato project page in the documentation
+
+* Sun Jul 25 2004 Avi Alkalay <avi@unix.sh> 0.2.8
+- Added the export and import subcommands to rg.
+- Updated man page for that.
+- Build environments that don't have xsltproc and the DocBook styles won't fail to build manual pages.
+
+* Thu Jul 4 2004 Avi Alkalay <avi@unix.sh> 0.2.7
+- First atempt to workaround the manpage creation when XSL tools are not available.
+
+* Thu Jun 24 2004 Avi Alkalay <avi@unix.sh> 0.2.6
+- Moved responsability of creating dirs in the installation process from spec to Makefile.
+- Detected a problem of manpage creation in other distros, thanks to Tim Witham from OSDL. Not fixed yet.
+
+* Sat Jun 02 2004 Avi Alkalay <avi@unix.sh> 0.2.5
+- Fixed the absense of keyClose in some places. Thanks to Victor.
+- In registryStatKey(), handled error if file can't be found. Thanks to Jens Andersen.
+- keyInit is now less smart: it will not test if the key is initialized or not. This is now a programmer responsability.
+- Code migrated to Subversion, thanks to Sean Russel. http://germane-software.com/repositories/tlr/
+- Added $Id: etc to source files.
+- Added 'make commit' to deal with 'svn ci'
+
+* Sat May 22 2004 Avi Alkalay <avi@unix.sh> 0.2.4
+- Improved rgsetenv login script: now set aliases too.
+- New methods: registryGetKeyByParent, registryGetKeyByParentKey, registryGetValueByParent, registrySetValueByParent
+- First attempts to create a bianry 'rg' with libxml statically linked.
+- New libregistry.a provided in -devel package, for creation of static binaries.
+- API manpages can now be accessed by method name (example: man registryGetKey)
+
+
+* Thu May 06 2004 Avi Alkalay <avi@unix.sh> 0.2.3
+- Defined an XML DTD for keys
+- Defined keyToStream() and ksToStream() that dumps a key or keyset to XML format
+- Defined keyCompare() and ksCompare()
+- New 'rg edit' subcommand, to XML edit subtrees of the Regitry
+- Improved registryGetChildKeys() for better handling of symlinks
+- An /etc/profile.d script to set user environment is available in package now
+- New "Society of Softwares" section in documentation
+- Improved CSS of the HTML documentation
+
+
+* Thu Apr 13 2004 Avi Alkalay <avi@unix.sh> 0.2.2
+- Implemented -u and -g in rg command to set Key UID and GID.
+- Included RG_O_SORT option for registyrGetChildKeys, to returned a KeySet sorted by key name.
+- Now every user/* key has an associated Owner defined, which defaults to $USER, when not specified in keySetName().
+- Upgrade in the key file format. Changes in the data type field.
+- New handleOldKeyFileVersion() to keep backwards compatibility with older key files.
+- Updates to documentation for this version changes.
+- Symbolic links between keys are now consolidated. There is a formal key type for them: RG_KEY_TYPE_LINK
+- Fixed a minor problem related to reading last \n in key comments
+- Updated userd-convert script to be make a more NIS compatible conversion
+- Included a new migration script: hwconfingKudzu-convert. To migrate kudzu data to the registry
+
+
+* Thu Apr 06 2004 Avi Alkalay <avi@unix.sh> 0.2.1
+- Change in key names: instead of (.) as hierarchy delimitator, we have (/) now. This is better for POSIX file name compliancy.
+- Consolidation of inactive keys.
+- Updates to documentations to include inactive keys concepts, etc.
+- Written key.3 manual page for key*() methods.
+- New conversion script to convert /etc/fstab
+- Removed a bug in UTF8Engine(). Data was written with a \n in the end. Thanks to Jens Andersen.
+
+* Thu Apr 01 2004 Avi Alkalay <avi@unix.sh> 0.1.6
+- Introduction to inactive keys
+- registryGetChildKeys by default do not include inactive keys in resulting KeySet. Use RG_O_INACTIVE instead.
+- rg.c changed, specially in the list subcommand, to work with new behavior
+
+* Wed Mar 31 2004 Avi Alkalay <avi@unix.sh> 0.1.5
+- Fixed in registryGetKey: Keys allways needed sync
+- Fixed bug releated to NEEDSYNC: ~RG_KEY_FLAG_NEEDSYNC needed a special cast to use the right number of bits
+- Improved HOWTO documentation and manual. Link to ReiserFS.
+- Started writting of the key.3 manual
+- One more bug related to strblen in registryFileUnserialize(), causing wrong data size calculation
+
+* Mon Mar 29 2004 Avi Alkalay <avi@unix.sh> 0.1.4
+- Written manual pages rg(1), registry(7), registry(5) and registry(3) using DocBook
+- Ported the OpenOffice.org specification to DocBook, using the modularized man pages
+- RPMs include manpages
+- RPMs have prerequesites now
+- Fixed bug regarding \0 in end of key names on recursive stats
+- More robust build system, catching warnings
+- New method: registryGetRootKeys(KeySet *returned)
+- Fixed some bugs related to usage of strblen()
+- Introduction of the RG_O_* method options
+- Removed bug related to permission when stat()ing keys
+- Removed bugs in some KeySet methods
+- Much improved 'rg ls' command, specially in recursive mode
+- New key flag, NEEDSYNC, to indicate the change status of a key
+- New method, registrySetKeys, to commit all modified keys on a KeySet
+
+* Tue Mar 16 2004 Avi Alkalay <avi@unix.sh> 0.1.2
+- The Linux Registry API is 100% usable
+- The rg command is very usable
+- Full support to UTF-8. Everything stored in the registry is UTF-8, regardeless of user's LANG. Conversions are on the fly.
+- Fixed bug on keySetName(), regarding wrong keyNameSize
+- Introduction of registryRemoveKey()
+- rg subcommands: ls, get, set, rm
+- rg now supports set octal permissions from command line
+- Key and Registry support for DIR type
+- Documentation process (man pages) in the very begining
+- Include files in a separate package
+
+* Sat Mar 06 2004 Avi Alkalay <avi@unix.sh> 0.0.9
+- Now no more IPC, Berkeley DB, threads, setuid, and other complexities
+- Simple library that accesses files on disk
+- rg has 'set' and partial 'ls' subcommands implemented
+- API can already be used
+
+* Sat Feb 15 2004 Avi Alkalay <avi@unix.sh> 0.0.2
+- Based on IPC, Berkeley DB, Threads, and many other complexities
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..29273b6
--- /dev/null
@@ -0,0 +1,20 @@
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src
+EXTRA_DIST=elektra.pc
+
+pkgconfigdir = $(ulibdir)/pkgconfig
+pkgconfig_DATA = elektra.pc elektratools.pc elektracpp.pc
+
+RPMARCH=`rpmbuild --showrc | awk '/^build arch/ {print $$4}'`
+
+rpm: dist
+       mkdir -p rpmdir/BUILD \
+       rpmdir/SPECS \
+       rpmdir/SOURCES \
+       rpmdir/SRPMS \
+       rpmdir/RPMS/$(RPMARCH)
+       rpmbuild --clean -ta $(PACKAGE)-$(VERSION).tar.gz --define "_topdir $$PWD/rpmdir"
+       @echo "============================================================"
+       @echo "Finished - the packages are in rpmdir/RPMS and rpmdir/SRPMS!"
+
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..38283bb
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,335 @@
+0.7.0
+
+Finally it is done, the 0.7.0 API is finished.
+The API covers everything shown in kdb.h and all
+releases in 0.7.0 will be 100% ABI and API compatible
+supported for at least a year.
+Of course except for the bugs, but there should not be
+many any more, because every method is at least checked
+twice and most have hundred of test cases.
+
+There will be a separate release for libelektratools,
+backend interface which will be supported from that
+moment on.
+
+kdb-tool and kdbedit and so will also have separate
+releases, but because they are not a library there
+is no API or ABI issue there.
+
+Download it from:
+(sourceforge?)
+http://www.markus-raab.org/ftp/elektra-0.7.0svn.tar.gz
+ftp://www.markus-raab.org/elektra-0.7.0svn.tar.gz
+
+
+
+
+
+
+
+---------------
+
+
+
+
+0.7.0rc5 The last release candidate before 0.7.0
+
+I am proud to present the finished  0.7.0 external API in
+kdb.h [0].
+
+A lot changed to 0.6.10 but the fundamentals are clearer then
+ever before: 3 Classes with their methods, a clear, easy and
+mature API. It is very hard to use it wrong but you get
+best results with very short code:
+
+http://www.libelektra.org/Tutorial
+
+Other parts are not finished, like libelektratools, the
+kdb-tool and the backend interface. They are not part of
+the official 0.7 release and get their own release later.
+
+The reasons to release within the next week are:
+ - The API is finished
+ - Oyranos needs a stable release from elektra
+ - the last release is long ago
+ - unstable has been much more stable than stable since some time
+ - there have been no commits to the stable branch since
+   unstable existed
+ - Elektra grows just too complex to wait for all parts to finish
+
+Now some highlights from one month of work from 333 ChangeLog
+lines:
+ - many many bugs were fixed
+ - the core completely compiles with -ansi -pedantic -std=c99
+ - all code compiles with -Wall -Werror
+ - the test-suite is now complete
+ - much cleaner infrastructure within the code
+ - many autoconf/automake issues fixed
+ - static, dynamic building, linking with defaults backends
+   everything works!
+ - many new options to stat and remove all keys and so on..
+ - environment variables and passwd database allow subtle
+   influence of where the user configuration resides
+ - kdb info now prints where configuration lies on hard disc
+
+
+
+
+---------------
+
+
+
+
+0.7.0rc4 - 12.05.2008
+I am proud to present the fourth release candidate of elektra
+0.7.0.
+
+It is now possible to enable and disable Backends and Bindings
+with configure switches. The documentation was improved.
+The c++ binding now basically works, see the tests for examples.
+Ini and Berkleydb backends now compile again.
+There were a lot of bug fixes, like mntent.h fix for Mac OS X.
+
+
+
+
+---------------
+
+
+
+
+0.7.0rc3 - 24.04.2008
+Call for 0.7 finish
+
+Unfortunately there is not much response about the 0.7 release
+candidates but there is still a lot to do to have a mature
+library worth to be announced as stable.
+
+Specifically there are following things to do and we really need
+help to achieve that faster than end of this year:
+
+There are now only 3 backends working well tested but undistinguished
+in handling error scenarios. At least 3 more Backends are needed
+also tested in real world applications. Favoured are existing but
+not ported backends like berkleydb, ini, winregistry and uniconf.
+
+Working bindings have a lot meaning in how complete and useful
+the API is. It would be preferable to have at least 3 bindings
+working with at least an example application like kdb-tool.
+They could be cpp, python and one open.
+
+A lot of bug squashing is left open. Please don't hesitate to open
+and fix more bugs on http://bugs.libelektra.org.
+
+Documentation is not yet completely updated to 0.7. Especially the
+homepage has a lot of outdated information, but also the API documentation
+needs another eye on it and the tutorial needs a rewrite.
+
+There might be some licencing issues in some parts of elektra,
+everything should be BSD, if you find something please open a bug.
+
+A partial blocker is the kdb-tool, where a rewrite would be nice but
+is not necessary for 0.7 (the library counts not the environment).
+
+Other things like portability are taken seriously but are not blockers,
+they can be fixed later too.
+
+What is really good working in elektra is the infrastructure like
+svn, homepage, bug tracking, many thanks to JosĂ© Luis TallĂ³n for
+that!
+
+Now some words about 0.7.0rc3:
+
+Version macros were added to have static information about
+the elektra version compiled against. There were many
+enhancements in the C++ Binding, the Key Class is now
+quite well complete.
+
+While writing the C++ binding I realized that there is need
+for keyInc() the opposite of keyDel() to increase the
+viability of a key object.
+
+keyVNew() and ksVNew() were added to make it possible to pass
+the va_list from the C++ binding.
+
+The blocker bug that libelektra assumes system/elektra/mountpoints
+to exist was solved, many thanks to Patrick Sabin. The tests
+now work without any preloading without memory leaks.
+
+And lots of bug fixes (Many thanks to Kai-Uwe Behrmann):
+- sed changed for macosx compatibility
+- fixed signature ssize_t ksToStream
+- also set CXXFlags
+- fix wrong parent at end in ksToStream
+- fix return value of successful kdb import
+- string is default type for xml without type=""
+- fix extra_dist some missing files
+- remove , at end of enumeration lists
+
+The svn id is unstable@1352:
+https://svn.libelektra.org/svn/elektra/unstable/
+
+The release candidate is available under:
+http://www.markus-raab.org/ftp/elektra-0.7.0rc3.tar.gz
+
+
+
+---------------
+
+
+
+0.7.0rc2 - 23.03.2008
+Second Release Candidate of unstable repository.
+
+We proudly present the second release candidate for Elektra 0.7.0. Many
+bugs were fixed, kdbGet() and kdbSet() is now really stable, useful and
+well documentated. test_kdb also tests hosts and fstab next to filesys
+with 6598 test cases.
+
+kdbSet() now supports a parent key to only set a part of the
+configuration passed by the keyset, allowing to e.g. save system and
+user configuration seperately. It only calls kdbSet_backend() when it
+is actually necessary. The splitting works much more efficient with n*b
+instead of n^2*b.
+
+The highlevel functions kdbGetKey() and kdbSetKey() now work well with
+Capabilities. This allows very easy changing of keys inside backends
+even with some lacking capabilities, see GetStartedMounting.
+
+Patrick Sabin and I wrote our bachelor thesis about mounting backends for
+configuration. It gives a detailed introduction in problem and choices
+and the actual implementation.
+See: doc/elektra.pdf doc/elektra.ps.gz
+
+
+
+
+
+---------------
+
+
+
+
+0.7.0rc1 - 06.12.2007
+First Release Candidate of unstable repository.
+= Large Changes =
+
+== Multiple Backends ==
+
+Elektra now supports multiple backends at once. That means every path
+like user/sw or system/filesystems can reside in a different backend.
+
+Use cases for backend mounting: 1. There are many configurationfiles lying
+on every system that can't be replaced for various reasons. These files
+can be fade in into the global elektra namespace without any notice by
+applications using these files. Backends for /etc/fstab, /etc/mtab and
+/etc/passwd exist right now.
+
+2. Users or administrators might get used by ordained files or syntax
+without wanting to change the whole configuration using elektra. The
+mounting technique allows them to choose.
+
+3. Specific programs may have very complex and large configuration. Binary
+files with index may give them a fairly better performance without
+missing the connection to the global namespace.
+
+4. Configuration provided by network or local daemons for notification
+and caching can't be used for every program. Configuration related to
+bootup or some system users need to be available without them, but should
+also be accessed by applications needing configuration from network.
+
+If you are not convinced - Its just about choice and you can go on using
+only one backend.
+
+See http://www.libelektra.org/Backends what is and might be possible.
+
+== kdbOpen(), kdbClose(), kdbGet() and kdbSet() ==
+
+These 4 core functions do now everything related to backend
+communication. All the other functions are build upon it. This makes
+backend writing much easier. Removing and Stat works by setting a flag
+in a key.
+
+KDB * is now a typedef for the _KDB structure and not a
+void* pointer.
+
+== Keyset ==
+
+Starting with ksNew you can give a hint how large the keyset may grow. You
+can also give a list of keys which should be added at startup.
+
+ksGenerate() makes use out of that and generates you a keyset in C-Code
+which you can use in your applications or for regression testing.
+
+The internal structure of keyset is now a growing (and shrinking)
+array. Keys may belong to multiple keysets now. The last ksDel() deletes
+the key automatically (reference counter).
+
+This implementation is a lot faster, the benchmark is for 0.7.0rc1 better
+then for 0.6.*, even though the mounting logic takes a bit of performance,
+but very little and something about zero when no backend is mounted.
+
+== Key ==
+
+The key now uses sizes for name, owner, value and comment. This allows a
+complete new technique of serializing: keyCompose(). With that you can
+serialize a key without a single malloc(). keySerialize() is rewritten
+for the new key struct too.
+
+== Access Types ==
+
+The directory is now marked by the executable flags. That means you can
+disallow other users or groups to list your keys.
+
+== Capabilites ==
+
+Some backends fullfill the whole specification of kdbGet_template() and
+kdbSet_template() and can be used by any program for any purpose. Other
+backends have principle limitations and do not fullfill the specification,
+but can do more than enough to be useful.
+
+To handle this problem we created a data structure describing what
+capabilites a backend does not have. With that technique you can use the
+testing framework from early steps developing the backend on. To do so,
+just declare your backend can do nothing and delete step by step while
+your backend evolves.
+
+== Testing framework ==
+
+There is now a large testing framework with 11 collections with up to
+8128 tests each. Most conditions of the whole sourcecode are checked,
+that means if you random change something in the code you have a good
+chance that a test case will fail.
+
+There are 2 new flags:
+ --enable-valgrind-tests
+   Allows you to enable valgrind for testing, a full list of memory
+   leaks is printed after every test.
+ --enable-gcov
+   Use it to see what lines of code are covered by tests.
+
+The testcases are without any memory leak (dozens of leaks are fixed,
+especially in xml code).
+
+
+0.6.10 - 10.03.2007 Elektra received some stability updated from Patrice
+Dumas.
+
+0.6.6 - 21.12.2006 support for directories with values and comments on
+filesys, berkeleydb and daemon backends.
+
+0.6.4 - 08.09.2006 Tons of improvements and standarizations to the build
+system from Patrice Dumas and Yannick.  Many improvements to the daemon
+backend from Yannick.  Several other bug fixes.  We are getting closer
+to a production daemon.
+
+
+0.6.2 - 02.06.2006 - Includes more robust key name handling and
+intelligent duplicate "/" removal - Escaping of "/" on key names is
+now supported - Better automatic UTF-8 conversions - More tunnings for
+better future daemon backend support - More robust berkeleydb backend -
+Backends are now installed on /lib/elektra/ - The build system was tunned
+to be included as a Fedora Extras package
+
+
+0.6.0 - 30.03.2006 Public release of the new API.  Check it out in SF.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d094e5e
--- /dev/null
+++ b/README
@@ -0,0 +1,33 @@
+ABOUT
+=====
+
+Elektra provides a universal and secure framework to store configuration
+parameters in a hierarchical key-value pair mechanism, instead of each program
+using its own text configuration files. This allows any program to read and
+save its configuration with a consistent API, and allows them to be aware of
+other applications' configurations, permitting easy application integration.
+While architecturally similar to other OS registries, Elektra does not have
+most of the problems found in those implementations.
+
+
+DOWNLOAD
+========
+
+Elektra stable source code as tarballs can be found at:
+
+  http://sourceforge.net/project/showfiles.php?group_id=117521
+
+Elektra Subversion repository with the latest stable source code can be checked
+out with these commands:
+
+  bash:~$ cd src
+  bash:~/src$ svn co https://svn.libelektra.org/svn/elektra/trunk elektra
+  bash:~/src$ cd elektra
+  bash:~/src/elektra$ # files are here
+
+
+INSTALL
+=======
+
+See the INSTALL document for informations how to install the software.
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..06e83f4
--- /dev/null
+++ b/TODO
@@ -0,0 +1,148 @@
+#For bugs and enhancements go to http://bugs.libelektra.org
+#For visions and drafts go to http://www.libelektra.org
+#For possible extensions in future, see FUTURE
+
+backends:
+       using libraries:
+               nickel
+               libconfig
+               eet
+       using keysets:
+               bind
+               memory
+       update old backends:
+               berkleydb
+               ini
+               winregistry
+               uniconf
+
+bindings: using swig
+       cpp
+       python
+       scheme
+       java
+
+bugs:
+       bug squashing
+       fix open bugs of http://bugs.libelektra.org
+
+documentation:
+       update all parts of docu to 0.7.0
+       update homepage to 0.7.0 related informatin
+       update Tutorial to 0.7.0 kdbGet()
+
+bsd licence:
+       check for documentation, homepage, modules,...
+
+kdb rewrite: [partial blocker]
+       kdb-ls alike commands
+       rm -r
+       cp -r
+       mv -r
+       ls pretty format
+       preload (mount, config backends)
+       generate
+       meta-info get/set
+       info about backends
+       add capabilities support in kdb tool
+       export xml validation
+
+integration: in other software
+       make autoconf snippet
+       shell-script (like in oyranos)
+
+extend:
+       re-add other backends (berkleydb, gconf)
+       build and test without gconf,... installed
+       ksToStream in stream.c
+       Reintroduce and test Regular Expressions in keyset.c
+       ksCompare in keyset.c
+       mount user/elektra hierarchy
+
+reliably code:
+       error states in kdbSet
+       mode checks 0-7
+       error codes
+
+dynamic memory management:
+       improve realloc, use srealloc
+       don't use sprintf, asprintf, strdup, strndup
+       use only own functions where available
+       use different compilers, libc too
+
+packaging:
+       build with minimal system
+       debian packages
+       fedora packages
+
+all exported methods of elektra need to be:
+       useful in current state
+       well documented in behavior
+       having test cases for behavior
+       behavior not overlapping with another function
+       well documented error cases, return and errno values
+       having test cases for error cases
+       otherwise fix or mark it obsolete
+
+version:
+       tag everything with 0.7.0
+       get version with macro
+       get version at runtime
+       check exported symbols
+       use symbol table
+
+#portability
+
+bugs::
+       mntent.h fix for macosx
+       dont use libltdl under linux
+
+portability:
+       cross compilation
+       bsd, aix,...
+       glibc functions away
+
+portability:
+       Build on different platforms
+       Statical build testing
+       build and test without iconv, libxml2
+
+compilers:
+       diet-gcc
+       intel cc
+
+#testing
+
+testing coverage:
+       -fprofile-arcs
+       -ftest-coverage
+       reasoning which parts are tested
+
+advanced testing:
+       get and set a real big amount of keys
+       multithreading testing
+
+#performance
+
+general:
+       scripts for performance and stability testing
+       testing optimizations (-O2)
+       profile the code and fix optimization leaks
+
+
+profiler:
+       search for slow parts
+
+malloc trace:
+       reduce mallocs
+
+benchmarks:
+       are large keysets handled?
+
+keySet:
+       make statistics how many keys typical applications need
+       be efficient for typical needs
+
+integer overflows: sizes must not exceed SSIZE_MAX
+check for null pointers and return -1
+
diff --git a/benchmarks/Makefile.am b/benchmarks/Makefile.am
new file mode 100644 (file)
index 0000000..6d75407
--- /dev/null
@@ -0,0 +1,30 @@
+AM_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LTDLINCL)
+
+#TESTS = large threads
+#TESTS = large
+
+#check_PROGRAMS = large threads
+#check_PROGRAMS = large
+
+if VALGRINDTESTS
+TESTS_ENVIRONMENT = $(VALGRIND) --quiet --show-reachable=yes --leak-check=yes
+else
+TESTS_ENVIRONMENT = time -p
+endif
+
+EXTRA_DIST =
+
+#threads_SOURCES = threads.c benchmarks.c benchmarks.h
+#threads_LDADD = $(privatelibs) ../src/libelektra/libelektra.a -lpthread
+
+#large_SOURCES = large.c benchmarks.c benchmarks.h
+#large_LDADD = $(privatelibs) ../src/libelektra/libelektra.a 
+
+../src/libelektra/libelektra.a:
+       cd ../src/libelektra && $(MAKE) libelektra.a
+
+clean-local:
+       rm -rf .kdb
+       rm -f *.gcno *.gcda *.gcno
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..79e7a8f
--- /dev/null
@@ -0,0 +1,774 @@
+#
+# Autoconf script for Elektra
+#
+# Markus Raab <elektra@markus-raab.org>
+# Yannick Lecaillez <sizon5@gmail.com>
+#
+
+AC_PREREQ(2.59)
+AC_REVISION([$Revision$])
+
+m4_define(VERSION_MAJOR, [0])
+m4_define(VERSION_MINOR, [7])
+m4_define(VERSION_MICRO, [0])
+
+AC_INIT(elektra, VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO, http://bugs.libelektra.org)
+
+AC_SUBST(KDB_VERSION, "VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO")
+AC_SUBST(KDB_VERSION_MAJORMINOR, "VERSION_MAJOR.VERSION_MINOR")
+AC_SUBST(KDB_VERSION_MAJOR, "VERSION_MAJOR")
+AC_SUBST(KDB_VERSION_MINOR, "VERSION_MINOR")
+AC_SUBST(KDB_VERSION_MICRO, "VERSION_MICRO")
+AC_SUBST(PACKAGE_URL, [http://www.libelektra.org/])
+
+AC_SUBST(ELEKTRA_VERSION_API, [3:0:0])
+AC_SUBST(ELEKTRATOOLS_VERSION_API, [2:0:0])
+AC_SUBST(BACKEND_VERSION_API, [0:0:0])
+
+
+
+
+
+
+#
+# Os Specific checks
+#
+
+# get host_os variable
+AC_CANONICAL_SYSTEM
+AC_CANONICAL_TARGET
+
+dnl workaround an incompatible install on solaris
+INSTALL='\$(install_sh)'
+AC_SUBST(INSTALL)
+
+AC_CONFIG_SRCDIR([src/libelektra/kdb.c])
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADER([src/include/config.h])
+
+dnl select POSIX extensions, useful on solaris, for example
+gl_USE_SYSTEM_EXTENSIONS
+
+#Check whether we are compiling for win32
+case $host_os in
+mingw* | pw32*)
+  # If compiling for windows we need HAVE_WIN32 defined
+       AC_DEFINE(HAVE_WIN32, 1, [Define if you are compiling for win32])
+esac
+
+# Disable libtool static lib
+#AC_DISABLE_STATIC
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Compile and test specific flags.
+#
+
+dnl this macro is used to get the arguments supplied
+dnl to the configure script (./configure --enable-debug)
+dnl Check if we have enabled debug support.
+AC_MSG_CHECKING(whether to enable debugging and take control over compiler flags)
+AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug],
+               [turn on debug symbols and enable debug messages @<:@default=no@:>@])])
+
+if test "x$enable_debug" = "xyes";
+then
+       AC_MSG_RESULT(yes)
+
+       dnl remove the optimization flag
+       dnl gcc has -g -O2 as default (if none given)
+       CFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9]*[ ]*//g'`
+       CXXFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9]*[ ]*//g'`
+
+       CFLAGS=`echo "$CFLAGS" | sed -e 's/-g[ ]*//g'`
+       CXXFLAGS=`echo "$CFLAGS" | sed -e 's/-g[ ]*//g'`
+
+       dnl STD flags will only be included where no POSIX is required
+       CSTDFLAGS="-std=c99 -ansi -pedantic"
+       COPTFLAGS="-O0"
+       CDBGFLAGS="-g -ggdb"
+
+       CXXSTDFLAGS="-std=c++98 -ansi -pedantic"
+       CXXOPTFLAGS="-O0"
+       CXXDBGFLAGS="-g -ggdb"
+
+       AC_DEFINE([DEBUG], 1,[enables debug output messages])
+       AC_DEFINE([VERBOSE], 0,[change here to be very verbose])
+else
+       AC_MSG_RESULT(no)
+
+       AC_DEFINE([DEBUG], 0,[disable debug output messages])
+       AC_DEFINE([VERBOSE], 0,[disable verbose output messages])
+fi
+#take control over FLAGS
+#AM_CONDITIONAL (USE_FLAGS, [test "x$enable_debug" = "xyes" ])
+AM_CONDITIONAL(DEBUG, test x$enable_debug = xyes)
+
+
+# Enable compilation of experimental things (unfinished
+# backends at this time)
+AC_ARG_ENABLE(experimental,
+             [AC_HELP_STRING([--enable-experimental],
+             [Specify to compile or not experimental parts (could cause compilation failure) @<:@default=no@:>@])],
+             [elektra_experimental=$enableval],
+             [elektra_experimental=no])
+# for the c++ binding
+AM_CONDITIONAL(EXPERIMENTAL, [ test "x$elektra_experimental" = "xyes" ])
+
+
+# Run self-tests under valgrind?
+AC_MSG_CHECKING([whether self tests are run under valgrind])
+AC_ARG_ENABLE(valgrind-tests,
+       AS_HELP_STRING([--enable-valgrind-tests],
+       [run self tests under valgrind @<:@default=no@:>@]))
+if test "x$enable_valgrind_tests" = "xyes";
+then
+       AC_MSG_RESULT(yes)
+       AC_CHECK_PROGS(VALGRIND, valgrind)
+else
+       AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(VALGRINDTESTS, [ test "x$VALGRIND" != "x" ])
+
+
+dnl thanks to the wine project, author: Aaron Arvey
+dnl Check for --enable-gcov and add appropriate flags for gcc
+dnl Note that these extra switches are NOT applied to the loader
+AC_MSG_CHECKING(whether to enable gcov)
+AC_ARG_ENABLE(gcov, AC_HELP_STRING([--enable-gcov],
+               [turn on code coverage analysis tools @<:@default=no@:>@]))
+if test "x$enable_gcov" = "xyes";
+then
+       AC_MSG_RESULT(yes)
+       AC_CHECK_PROGS(LCOV, lcov, false)
+       AC_CHECK_PROGS(GENHTML, genhtml, false)
+
+       CDBGFLAGS="$CDBGFLAGS -fprofile-arcs -ftest-coverage"
+       CXXDBGFLAGS="$CXXDBGFLAGS -fprofile-arcs -ftest-coverage"
+
+       LDFLAGS="$LDFLAGS -lgcov"
+
+       dnl Turn off optimization so code coverage tool
+       dnl can get accurate line numbers
+       CFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9]*[ ]*//g'`
+       CXXFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9]*[ ]*//g'`
+       COPTFLAGS="-O0"
+       CXXOPTFLAGS="-O0"
+else
+       AC_MSG_RESULT(no)
+fi
+
+AC_SUBST(CSTDFLAGS)
+AC_SUBST(COPTFLAGS)
+AC_SUBST(CDBGFLAGS)
+
+AC_SUBST(CXXSTDFLAGS)
+AC_SUBST(CXXOPTFLAGS)
+AC_SUBST(CXXDBGFLAGS)
+
+
+
+
+
+
+
+
+
+#
+# set some custom path
+#
+
+# usr/lib path
+AC_SUBST(libdir)
+echo "LIBDIR=$libdir"
+
+AC_ARG_WITH(ulibdir,
+           [AC_HELP_STRING([--with-ulibdir=ULIBDIR>],
+            [Set the path for usr lib.])],
+            [ulibdir=$withval],
+            [ulibdir='${prefix}/'`basename $libdir`]
+            )
+AC_SUBST(ulibdir)
+
+# backend dlopended libraries path
+AC_ARG_WITH(backenddir,
+           [AC_HELP_STRING([--with-backenddir=<path where backend libraries are>],
+            [Set the path for backend libraries. @<:@LIBDIR/elektra@@:>@])],
+            [backenddir=$withval],
+            [backenddir='${libdir}/elektra']
+            )
+AC_SUBST(backenddir)
+
+# high level backend dlopended libraries path
+AC_ARG_WITH(hlvlbackenddir,
+           [AC_HELP_STRING([--with-hlvl-backenddir=<path where high level backend libraries are>],
+            [Set the path for high level backend libraries. @<:@ULIBDIR/elektra@:>@])],
+            [hlvlbackenddir=$withval],
+            [hlvlbackenddir='${ulibdir}']
+            )
+AC_SUBST(hlvlbackenddir)
+
+# /usr/share/doc path
+AC_ARG_WITH(docdir,
+           [AC_HELP_STRING([--with-docdir=<path where doc will be installed>],
+            [Set the path for documentation. @<:@DATADIR/doc/elektra@:>@])],
+            [docdir=$withval],
+            [docdir='${datadir}/doc/elektra']
+            )
+AC_SUBST(docdir)
+
+# /usr/share/doc/elektra-api
+AC_ARG_WITH(develdocdir,
+           [AC_HELP_STRING([--with-develdocdir=<path where elektra-api doc will be installed>],
+            [Set the path for elektra api documentation. @<:@DATADIR/doc/elektra-devel@:>@])],
+            [develdocdir=$withval],
+            [develdocdir='${datadir}/doc/elektra-devel']
+            )
+AC_SUBST(develdocdir)
+
+# Select docbook.xsl
+AC_ARG_WITH(docbook,
+           [AC_HELP_STRING([--with-docbook=<path to docbook.xsl>],
+            [Set path to docbook.xsl used for generate manpage. @<:@/usr/share/sgml/docbook/xsl-stylesheets@:>@])],
+            [default_docbook=$withval],
+            [default_docbook=/usr/share/sgml/docbook/xsl-stylesheets]
+            )
+AC_SUBST(default_docbook)
+
+# sgml/elektra-<VERSION>
+AC_ARG_WITH(kdbschema,
+           [AC_HELP_STRING([--with-kdbschemadir=<relative path to kdb schema>],
+            [Set the path for elektra.xsd. DATADIR will be prefixed. @<:@/sgml/elektra-$PACKAGE_VERSION@:>@])],
+            [kdbschemadir=$withval],
+            [kdbschemadir="/sgml/elektra-$PACKAGE_VERSION"]
+            )
+AC_DEFINE_UNQUOTED([KDB_SCHEMA_PATH], DATADIR "${kdbschemadir}", path for elektra.xsd)
+
+
+# Default backend selection (Set to 'filesys' as default)
+AC_ARG_WITH(backend,
+           [AC_HELP_STRING([--with-default-backend=<backend>],
+            [Set backend elektra will be linked to. @<:@filesys@:>@])],
+            [default_backend=$withval],
+            [default_backend=filesys]
+            )
+AC_SUBST(default_backend)
+
+# Default daemon backend selection (Set to 'berkeleydb' as default)
+AC_ARG_WITH(dbackend,
+           [AC_HELP_STRING([--with-default-dbackend=<daemon backend>],
+            [Set the default backend for the kdbd daemon to use. @<:@berkeleydb@:>@])],
+            [default_dbackend=$withval],
+            [default_dbackend=berkeleydb]
+            )
+AC_SUBST(default_dbackend)
+
+
+
+
+
+
+
+#
+# Checks for needed programs.
+#
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+
+AC_PROG_CXX
+
+AC_PROG_LN_S
+AC_PROG_SED
+AC_CHECK_PROG(NM, nm, nm, nm-is-missing)
+AC_PROG_MAKE_SET
+
+PKG_PROG_PKG_CONFIG
+
+# Check needed programs for generate doc
+
+# xsltproc
+AC_PATH_PROG(xsltproc, xsltproc, "no")
+# Check if have style-sheet too
+if test "x$default_docbook" != "xno"; then
+       AC_CHECK_FILE([$default_docbook/manpages/docbook.xsl],
+                     [dbroot=$default_docbook],
+                     [dbroot="no"])
+       if test "x$default_docbook" != "xno"; then
+               AC_CHECK_FILE([$default_docbook/template/titlepage.xsl],
+                             [dbroot=$default_docbook],
+                             [dbroot="no"])
+       fi
+else
+       dbroot="no"
+fi
+
+AC_SUBST(dbroot)
+AM_CONDITIONAL(HAVE_XSL, test x$xsltproc != xno -a x$dbroot != xno)
+
+# man2html
+AC_PATH_PROG(man2html, man2html, "no")
+AM_CONDITIONAL(HAVE_MAN2HTML, test x$man2html != xno)
+
+# Doxygen
+AC_PATH_PROG(doxygen, doxygen, "no")
+AM_CONDITIONAL(HAVE_DOXYGEN, test x$doxygen != xno)
+
+
+
+
+
+
+
+#
+# Checks for header files.
+#
+
+AC_HEADER_STDC
+AC_CHECK_HEADERS([ ctype.h stdlib.h unistd.h errno.h time.h stdio.h ])
+AC_CHECK_HEADERS([ langinfo.h locale.h ])
+AC_HEADER_TIME
+
+#types
+AC_CHECK_HEADERS([ inttypes.h limits.h ])
+AC_HEADER_STDBOOL
+
+#posix files and functions (needed by filesys and libhelper)
+AC_HEADER_DIRENT
+AC_CHECK_HEADERS([ fcntl.h ])
+
+
+
+
+
+
+#
+# libltld checks
+#
+
+#copied out from libsox
+
+AC_ARG_WITH(libltdl,
+    AC_HELP_STRING([--without-libltdl],
+        [Don't try to use libltdl for external dynamic library support]))
+
+using_libltdl=no
+if test "$with_libltdl" != "no"; then
+       dnl Disable libltdl support when building only static libraries
+       if test "$enable_shared" != "no"; then
+               using_libltdl=yes
+       fi
+
+       dnl Force off when building on w32, we have specific code for that
+       dnl in libloader.
+       case $host_os in
+               pw32* | mingw*)
+                       using_libltdl=no
+                       ;;
+       esac
+
+       dnl Force off using libltdl on targets that are know to have
+       dnl problems.
+       case $target in
+               *cygwin*)
+                       using_libltdl=no
+                       ;;
+       esac
+fi
+
+if test "$using_libltdl" != "no"; then
+  AC_LIBLTDL_INSTALLABLE
+  AC_LIBTOOL_DLOPEN
+fi
+AC_PROG_LIBTOOL
+AC_SUBST(LIBTOOL_DEPS)
+AC_CONFIG_SUBDIRS(libltdl)
+if test "$using_libltdl" != "no"; then
+  AC_SUBST(LIBLTDL)
+  AC_SUBST(LTDLINCL)
+  AC_CHECK_HEADERS(ltdl.h, AC_DEFINE([HAVE_LIBLTDL], 1, [Define to 1 if you have libltdl]), [using_libltdl=no])
+fi
+AM_CONDITIONAL(HAVE_LIBLTDL, test x$using_libltdl = xyes)
+
+
+
+
+
+
+
+
+#
+# Checks for libraries.
+#
+
+
+# Check for iconv
+# If iconv usage is enabled we need to check for iconv.
+# This may also result in linking against libiconv,
+# if the system requires this for a working iconv function.
+dnl Check if the user wants to enable iconv support and
+dnl hence enable charset conversions.
+dnl Default: dont enable it
+AC_MSG_CHECKING(whether to enable iconv)
+AC_ARG_ENABLE(iconv,
+               [AC_HELP_STRING([--enable-iconv],
+               [turns on automatic UTF-8 conversions. @<:@default=no@:>@])])
+
+if test "x$enable_iconv" = "xyes"; then
+       AC_MSG_RESULT(yes)
+       AM_ICONV()
+else
+       AC_MSG_RESULT(no)
+fi
+
+privatelibs=""
+
+# Check for libxml (Allow compilation of libelektratools)
+AM_PATH_XML2(, [elektra_have_xml="yes"], [elektra_have_xml="no"])
+AM_CONDITIONAL(HAVE_XML, [test x$elektra_have_xml = xyes])
+if test "x$elektra_have_xml" = "xyes"; then
+       elektratools=libelektratools
+       privatelibs=$privatelibs" -lxml2"
+fi
+
+AC_SUBST(elektratools)
+
+
+
+
+
+
+
+
+#
+# Backends enable and disable logic.
+#
+
+BACKENDS=""
+TESTBACKENDS=""
+
+# $1 is the backend name
+# $2 is the check for prerequisite
+# $3 is the default value YES or NO
+AC_DEFUN([ALLOW_BACKEND],
+[
+       TESTBACKENDS="$TESTBACKENDS
+       $1      check: $2,      default: $3"
+       if test "$3" == YES; then
+               BACKENDS="$BACKENDS $1";
+       fi
+       AC_ARG_ENABLE([$1],
+               AC_HELP_STRING([--enable-$1], [compile $1 backend @<:@default=$3@:>@]),
+               [if test "$enableval" = yes; then
+                       if $2; then
+                               BACKENDS="$BACKENDS $1";
+                       else
+                               #thus only default backends are in BACKENDS and
+                               #they don't have prerequisites, don't remove
+                               AC_MSG_WARN([not building backend $1, prerequisite missing])
+                       fi
+               else
+                       #removed backend out of BACKENDS list
+                       BACKENDS=$(echo "$BACKENDS" | sed 's/[[:space:]]*$1//')
+               fi
+       ])
+])
+
+ALLOW_BACKEND(filesys, true, YES)
+
+ALLOW_BACKEND(hosts, true, YES)
+
+ALLOW_BACKEND(ini, true, NO)
+
+elektra_have_db=no
+# Check for libdb (Allow compilation of libelektra-berkeley.so)
+AC_CHECK_HEADERS([db.h],[
+  AC_CHECK_LIB([db], [db_create], [elektra_have_db="yes"])
+])
+ALLOW_BACKEND(berkeleydb, [test x$elektra_have_db = xyes], NO)
+
+
+# Check for libGconf (Allow compilation of libelektra-gconf.so)
+PKG_CHECK_MODULES(gconf, gconf-2.0, [elektra_have_gconf="yes"], [elektra_have_gconf="no"])
+ALLOW_BACKEND(gconf, [test x$elektra_have_gconf = xyes], NO)
+
+elektra_glibc_mntent=no
+AC_CHECK_HEADERS([mntent.h],[
+   AC_TRY_LINK([#include <stdio.h>
+#include <mntent.h> ],
+[
+struct mntent *m;
+FILE *f;
+char *fsname, *dir, *type, *opts;
+int freq, passno;
+f=setmntent("/etc/fstab", "r");
+m=getmntent(f);
+fsname=m->mnt_fsname;
+dir=m->mnt_dir;
+type=m->mnt_type;
+opts=m->mnt_opts;
+freq=m->mnt_freq;
+passno=m->mnt_passno;
+],
+dnl DEFINE is not used in the code
+   [ AC_DEFINE(GETMNTENT_GLIBC, 1, 
+     [Define this if mntent follows glibc conventions])
+   elektra_glibc_mntent=yes],
+   []
+)])
+
+
+AC_MSG_CHECKING([if mntent follows the glibc conventions])
+if test x$elektra_glibc_mntent = xyes; then
+   AC_MSG_RESULT(yes)
+else
+   AC_MSG_RESULT(no)
+fi
+ALLOW_BACKEND(fstab, [test x$elektra_glibc_mntent = xyes], NO)
+
+AC_CHECK_HEADERS([pwd.h],
+   [ AC_DEFINE(HAVE_PWD_H, 1, 
+     [Define this if pwd.h is present on your system])
+   elektra_pwd_h=yes]
+)
+ALLOW_BACKEND(passwd, [test x$elektra_pwd_h = xyes], NO)
+
+# daemon depends on struct ucred in ipc.c
+elektra_ucred=no
+AC_CHECK_MEMBERS([struct ucred.pid], [elektra_ucred=yes], [elektra_ucred=no],
+                 [#include <sys/socket.h>])
+ALLOW_BACKEND(daemon, [test x$elektra_ucred = xyes], NO)
+
+AC_SUBST(BACKENDS)
+
+backend_static_libs=
+for backend in $BACKENDS; do
+       backend_static_libs="$backend_static_libs ../backends/${backend}/libelektra-${backend}.a"
+done
+
+AC_SUBST(privatelibs)
+AC_SUBST(backend_static_libs)
+
+
+
+
+
+
+
+#
+# Bindings enable and disable logic.
+#
+
+BINDINGS=""
+
+# $1 is the binding name
+# $2 is the check for prerequisite
+# $3 is the default value YES or NO
+AC_DEFUN([ALLOW_BINDING],
+[
+       if test "$3" == YES; then
+               BINDINGS="$BINDINGS $1";
+       fi
+       AC_ARG_ENABLE([$1],
+               AC_HELP_STRING([--enable-$1], [compile $1 binding  @<:@default=$3@:>@]),
+               [if test "$enableval" = yes; then
+                       if $2; then
+                               BINDINGS="$BINDINGS $1";
+                       else
+                               #thus only default bindings are in BINDINGS and
+                               #they don't have prerequisites, don't remove
+                               AC_MSG_WARN([not building binding $1, prerequisite missing])
+                       fi
+               else
+                       #removed bindings out of BINDINGS list
+                       BINDINGS=$(echo "$BINDINGS" | sed 's/[[:space:]]*$1//')
+               fi
+       ])
+])
+
+ALLOW_BINDING(cpp, true, YES)
+
+#AC_CHECK_PROGS(SCHEME, scheme, false)
+#ALLOW_BINDING(scheme, [test "x$SCHEME" = xscheme], NO)
+
+AC_CHECK_PROGS(PYTHON, python, false)
+ALLOW_BINDING(python, [test "x$PYTHON" = xpython], NO)
+
+AC_SUBST(BINDINGS)
+
+# inspired from daemontools
+AC_CHECK_HEADERS([signal.h],[
+   AC_TRY_LINK([#include <signal.h>],
+[
+sigset_t ss;
+sigemptyset(&ss);
+sigaddset(&ss,SIGCHLD);
+sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+],
+    [AC_DEFINE([HASSIGPROCMASK],[],[POSIX signal available])])
+    AC_TRY_LINK([#include <signal.h>],
+[
+int sig;
+struct sigaction sa;;
+sa.sa_handler = f;
+sa.sa_flags = 0;
+sigemptyset(&sa.sa_mask);
+sigaction(sig,&sa,(struct sigaction *) 0); 
+],
+    [AC_DEFINE([HASSIGACTION],[],[sigaction available])])
+])
+
+
+
+
+
+
+
+
+#
+# Checks for typedefs, structures, and compiler characteristics.
+#
+
+AC_C_CONST
+AC_TYPE_UID_T
+AC_CHECK_TYPE(gid_t, int)
+AC_CHECK_TYPE(time_t, unsigned long)
+AC_TYPE_MODE_T
+
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+
+AC_MSG_CHECKING([For a working getopt])
+AC_TRY_LINK([#include <unistd.h>],
+[
+int opt;
+char** myargv;
+opt=getopt(1,myargv,"a");
+if (optind == 2) {opt = 3;};
+],
+[
+  AC_DEFINE([HASGETOPT],[],[Working getopt])
+  AC_MSG_RESULT(yes)
+],
+[AC_MSG_RESULT(no)])
+
+
+
+
+
+
+
+
+
+
+#
+# Checks for library functions.
+#
+#AC_FUNC_MALLOC
+#AC_FUNC_MEMCMP
+#AC_FUNC_REALLOC
+
+AC_FUNC_CHOWN
+AC_FUNC_CLOSEDIR_VOID
+AC_FUNC_GETMNTENT
+#AC_FUNC_MALLOC
+#AC_FUNC_MEMCMP
+#AC_FUNC_REALLOC
+AC_FUNC_STAT
+
+#old version
+AC_CHECK_FUNCS([memset nl_langinfo setenv setlocale strcasecmp strchr strrchr index rindex strtol ctime_r getuid getgid sigprocmask sigaction])
+#for testing suite
+AC_CHECK_FUNCS([clearenv setenv])
+#generated by autoscan, TODO. merge together
+AC_CHECK_FUNCS([ftruncate getmntent gettimeofday memmove memset mkdir nl_langinfo putenv rmdir setenv setlocale socket strcasecmp strchr strndup strrchr strstr strtol])
+
+
+
+
+
+
+
+#
+# Output
+#
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([elektra.spec])
+AC_OUTPUT([src/Makefile
+
+          src/libhelper/Makefile
+          src/libloader/Makefile
+
+          src/libelektra/Makefile
+          src/libelektra/exportobjects.sh
+
+          src/libelektratools/Makefile
+
+          src/preload/Makefile
+          src/kdb/Makefile
+
+          src/backends/Makefile
+          src/backends/berkeleydb/Makefile
+          src/backends/filesys/Makefile
+          src/backends/fstab/Makefile
+          src/backends/passwd/Makefile
+          src/backends/hosts/Makefile
+          src/backends/gconf/Makefile
+          src/backends/template/Makefile
+          src/backends/ini/Makefile
+          src/backends/daemon/Makefile
+
+          src/bindings/Makefile
+          src/bindings/cpp/Makefile
+          src/bindings/cpp/tests/Makefile
+          src/bindings/python/Makefile
+
+          src/include/Makefile
+
+          doc/Makefile
+          doc/images/Makefile
+          doc/standards/Makefile
+
+          scripts/Makefile
+
+          xmlschema/Makefile
+
+          elektra.pc
+          elektratools.pc
+          elektracpp.pc
+
+          benchmarks/Makefile
+          examples/Makefile
+          tests/Makefile
+          ])
+
+echo "--------------------------------"
+echo " libelektra                     "
+echo "--------------------------------"
+echo ""
+echo "Test backends: $TESTBACKENDS"
+echo ""
+echo "Compile backends: $BACKENDS"
+echo "Compile bindings: $BINDINGS"
+echo -n "Compile libelektratools: "
+if test "x$elektra_have_xml" = "xyes"; then
+       echo "yes"
+else
+       echo "no"
+fi
+echo ""
+echo "type: make && make install"
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100755 (executable)
index 0000000..8ddc44a
--- /dev/null
@@ -0,0 +1,7 @@
+elektra (0.7.0-slp1) unstable; urgency=low
+
+  * Initial Release.
+  * Git: pkgs/e/elektra
+  * Tag: elektra_0.7.0-slp1
+
+ -- Hyungdeuk Kim <hd3.kim@samsung.com>  Wed, 7 Ju 2011 13:52:02 +0900
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100755 (executable)
index 0000000..8e0021f
--- /dev/null
@@ -0,0 +1,23 @@
+Source: elektra
+Section: devel
+Priority: optional
+Maintainer: Hakjoo Ko <hakjoo.ko@samsung.com>, Hyungdeuk Kim <hd3.kim@samsung.com>
+Uploaders: Hyungdeuk Kim <hd3.kim@samsung.com>
+Build-Depends: debhelper (>= 5), autotools-dev, libxml2-dev
+Standards-Version: 3.7.2
+
+Package: libelektra-dev
+Section: libdevel
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: a framework to get system and user values
+ The objective of the Elektra Project is to help create a pervasive, 
+ ubiquitous configuration system. This is an entire ecosystem, much
+ more than a piece of code. This section lists the work of some 
+ people that are helping to build it.
+
+Package: libelektra-dbg
+Section: debug
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: a framework to get system and user values (unstripped)
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..e1b09fa
--- /dev/null
@@ -0,0 +1,11 @@
+Copyright (c) www.libelektra.org
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
+Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..5502ed8
--- /dev/null
@@ -0,0 +1,3 @@
+NEWS
+README
+TODO
diff --git a/debian/libelektra-dev.install.in b/debian/libelektra-dev.install.in
new file mode 100644 (file)
index 0000000..cc23af1
--- /dev/null
@@ -0,0 +1,3 @@
+@PREFIX@/include/*
+@PREFIX@/lib/lib*.{a,so,la}
+@PREFIX@/lib/pkgconfig/elektra.pc
diff --git a/debian/libelektra.install.in b/debian/libelektra.install.in
new file mode 100644 (file)
index 0000000..0336f9a
--- /dev/null
@@ -0,0 +1 @@
+@PREFIX@/lib/lib*.so.*
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..a8ac818
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+CFLAGS = -Wall -g -fno-short-enums -DSQLFS_BACKEND -DTUNNING_ELEKTRA_0_7 -fpic
+LDFLAGS = 
+PREFIX ?= /usr
+DATADIR ?= /opt
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0
+else
+       CFLAGS += -O2
+endif
+
+
+configure: 
+       touch $(CURDIR)/config.rpath
+       autoheader
+       libtoolize --ltdl --copy --force
+       aclocal -Im4 -Ilibltdl
+       autoconf
+       automake --add-missing --copy --force
+
+config.status: configure
+       dh_testdir
+       # Add here commands to configure the package.
+       ./configure --prefix=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" --without-docbook --enable-cpp=no --enable-hosts=no --without-libltdl --enable-shared=no
+
+
+build: build-stamp
+
+build-stamp:  config.status
+       dh_testdir
+
+       # Add here commands to compile the package.
+       $(MAKE)
+       #docbook-to-man debian/elektra.sgml > elektra.1
+
+       for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
+               cat $$f > $${f%.in}; \
+               sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
+               sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
+       done
+
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp 
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) distclean
+ifneq "$(wildcard /usr/share/misc/config.sub)" ""
+       cp -f /usr/share/misc/config.sub config.sub
+endif
+ifneq "$(wildcard /usr/share/misc/config.guess)" ""
+       cp -f /usr/share/misc/config.guess config.guess
+endif
+
+       for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
+               rm -f $${f%.in}; \
+       done
+
+#      rm -f /usr/include/ltdl.*
+#      rm -f /usr/lib/libltdl.*
+
+       -rm configure
+       -rm config.status *.stamp
+
+       dh_clean 
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k 
+       dh_installdirs
+
+       # Add here commands to install the package into debian/tmp.
+       $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
+
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_install --sourcedir=debian/tmp
+       dh_installman
+       dh_link
+       dh_strip --dbg-package=libelektra-dbg
+       dh_compress
+       dh_fixperms
+       dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install 
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..24149f8
--- /dev/null
@@ -0,0 +1,254 @@
+# $Id$
+
+EXTRA_DIST=kdb.1.xml elektra.7.xml elektra.5.xml
+EXTRA_DIST+=author.xml bestpract.xml rgexample.xml rgcmd.xml storage.xml overview.xml apiexample.xml api.xml compared.xml garbage.xml html-params.xsl html-titlepage-layout.tpl society.xml storage.xml UPLOAD
+EXTRA_DIST+=docbook.css doxygen.css
+#EXTRA_DIST+=elektra.odp
+EXTRA_DIST+=$(all_mans)
+EXTRA_DIST+= $(HTML_TITLE_LAYOUT_XSL)
+
+SUBDIRS=images standards
+
+# Dependencies for Doxygen documentation
+# main...
+DOXYSOURCES=$(srcdir)/../src/libelektra/*.c $(srcdir)/../src/include/*.h $(srcdir)/../src/Doxyfile
+
+# tools...
+DOXYSOURCES+=$(srcdir)/../src/libelektratools/kdbtools.c
+
+# kdb command...
+DOXYSOURCES+=$(srcdir)/../src/kdb/kdb-tool.c
+
+# backend stuff...
+DOXYSOURCES+=$(srcdir)/../src/backends/doc/backend.c
+
+if HAVE_XSL
+
+SUFFIXES: .xml .html .pdf .ps .rtf .xsl .fo .tpl
+#
+## Build man documentation
+#
+man3_MANS =
+# man pages are installed in any case, but not distributed if they
+# annot be built
+man1_MANS = kdb.1
+man5_MANS = elektra.5
+man7_MANS = elektra.7
+
+all_mans = $(man1_MANS) $(man5_MANS) $(man7_MANS)
+
+kdb.1: kdb.1.xml author.xml bestpract.xml rgexample.xml rgcmd.xml
+       $(xsltproc) $(dbroot)/manpages/docbook.xsl $<
+
+elektra.7: elektra.7.xml author.xml overview.xml bestpract.xml rgexample.xml
+       $(xsltproc) $(dbroot)/manpages/docbook.xsl $<
+
+elektra.5: elektra.5.xml author.xml storage.xml
+       $(xsltproc) $(dbroot)/manpages/docbook.xsl $<
+
+
+#
+# HTML and doc targets for the previous web site. Obsolete, unused for now
+#
+
+# howto.html dependencies and target (unused)
+.xml.html:
+       $(xsltproc) -path . -o $@ ${HTML_TITLE_LAYOUT_XSL} $<
+
+howto.html: howto.xml elektra.7.xml author.xml garbage.xml overview.xml bestpract.xml rgexample.xml rgcmd.xml storage.xml api.xml society.xml compared.xml api.xml apiexample.xml xorgpatch.xml initpatch.xml csource.xml ${HTML_TITLE_LAYOUT_XSL}
+
+# Layout and configurations to generate HTML for the web site
+HTML_TITLE_LAYOUT_XSL=html-titlepage-layout.xsl
+#HTML_TITLE_LAYOUT=html-titlepage-layout.tpl
+HTML_TITLE_LAYOUT=html-params.xsl
+
+$(HTML_TITLE_LAYOUT_XSL): $(HTML_TITLE_LAYOUT)
+       -$(xsltproc) -o ${HTML_TITLE_LAYOUT_XSL} $(dbroot)/template/titlepage.xsl $(srcdir)/${HTML_TITLE_LAYOUT}
+
+# web site
+libelektra.org: howto.xml elektra.7.xml author.xml garbage.xml overview.xml bestpract.xml rgexample.xml rgcmd.xml storage.xml api.xml society.xml compared.xml api.xml apiexample.xml xorgpatch.xml initpatch.xml csource.xml elektra-api.tar.gz $(man_html) ${HTML_TITLE_LAYOUT_XSL}
+       $(xsltproc) -path . -stringparam base.dir $@ ${HTML_TITLE_LAYOUT_XSL} $<
+       -cp $(srcdir)/docbook.css $@/
+       -mkdir $@/images/
+       -cp -r images/*png images/*gif $@/images
+       -find elektra-api/html | cpio -pdvm $@
+       -cp elektra-api.tar.gz $@
+       -cp $(man_html) $@
+
+else
+# in case there are no man pages in source dir and the xsl tools
+# are not installed, make install will fail without the files.
+# this situation should be very rare, it could only happen
+# from cvs (and without xsl tools) or after a make maintainer-clean
+# or after a modification of the xml sources.
+kdb.1: kdb.1.xml author.xml bestpract.xml rgexample.xml rgcmd.xml
+       touch $<
+
+elektra.7: elektra.7.xml author.xml overview.xml bestpract.xml rgexample.xml
+       touch $<
+
+elektra.5: elektra.5.xml author.xml storage.xml
+       touch $<
+
+
+endif
+
+# html generated from man pages.
+if HAVE_MAN2HTML
+man_html = kdb.1.html elektra.7.html elektra.5.html kdb.3.html key.3.html keyset.3.html
+
+kdb.1.html: kdb.1
+       $(man2html) -r $< | sed -e 's|Content-type: text/html||; s|\"\.\.\/man.\/|\"|g; s|\"\.\.\/|\"|g;' > kdb.1.html
+
+elektra.5.html: elektra.5
+       $(man2html) -r $< | sed -e 's|Content-type: text/html||; s|\"\.\.\/man.\/|\"|g; s|\"\.\.\/|\"|g;' > elektra.5.html
+
+elektra.7.html: elektra.7
+       $(man2html) -r $< | sed -e 's|Content-type: text/html||; s|\"\.\.\/man.\/|\"|g; s|\"\.\.\/|\"|g;' > elektra.7.html
+
+kdb.3.html: elektra-api
+       dir=.; \
+       if test -f elektra-api/man/man3/kdb.3; then dir=elektra-api/man/man3 ;\
+       else \
+         if test -f $(srcdir)/elektra-api/man/man3/kdb.3; then dir=$(srcdir)/elektra-api/man/man3 ; fi; \
+       fi; \
+       $(man2html) -r < $$dir/kdb.3 | sed -e 's|Content-type: text/html||; s|\"\.\.\/man.\/|\"|g; s|\"\.\.\/|\"|g;' > kdb.3.html
+
+key.3.html: elektra-api
+       dir=.; \
+       if test -f elektra-api/man/man3/key.3; then dir=elektra-api/man/man3 ;\
+       else \
+         if test -f $(srcdir)/elektra-api/man/man3/key.3; then dir=$(srcdir)/elektra-api/man/man3 ; fi; \
+       fi; \
+       $(man2html) -r < $$dir/key.3 | sed -e 's|Content-type: text/html||; s|\"\.\.\/man.\/|\"|g; s|\"\.\.\/|\"|g;' > key.3.html
+
+keyset.3.html: elektra-api
+       dir=.; \
+       if test -f elektra-api/man/man3/keyset.3; then dir=elektra-api/man/man3 ;\
+       else \
+         if test -f $(srcdir)/elektra-api/man/man3/keyset.3; then dir=$(srcdir)/elektra-api/man/man3 ; fi; \
+       fi; \
+       $(man2html) -r < $$dir/keyset.3 | sed -e 's|Content-type: text/html||; s|\"\.\.\/man.\/|\"|g; s|\"\.\.\/|\"|g;' > keyset.3.html
+endif
+
+
+# all: documentation
+
+# html doxygen
+# documentation: docbookman doxygen
+# docbookman: kdb.1 elektra.7 elektra.5
+
+develdoc_DATA = elektra-api
+apihtmldir = $(develdocdir)/api-html
+
+if HAVE_DOXYGEN
+#
+# Generate doxygen documentation
+# This generate man for API too (see src/Doxyfile)
+#
+elektra-api.tar.gz: elektra-api
+       tar -zcf elektra-api.tar.gz elektra-api/html
+
+elektra-api: $(DOXYSOURCES)
+       @echo "Making Doxygen..."
+       cd ../src/; srcdir=$(top_srcdir)/src $(doxygen) $(top_srcdir)/src/Doxyfile
+
+clean-local:
+       rm -rf elektra-api
+       rm -rf libelektra.org
+
+
+else
+# fake target that is required when doxygen isn't found and the directory
+# has to be created (make install, make dist)
+elektra-api:
+       mkdir elektra-api
+
+clean-local:
+       rm -rf elektra-api
+
+endif
+
+CLEANFILES = csource.xml \
+       $(man_html) \
+       $(HTML_TITLE_LAYOUT_XSL) \
+       xorgpatch.xml initpatch.xml \
+       elektra-api.tar.gz
+
+MAINTAINERCLEANFILES = howto.html \
+       kdb.1 elektra.5 elektra.7
+
+# putting in EXTRA_DIST doesn't work, since in that case it may be
+# copied from srcdir and also from the current dir, leading to an error
+dist-hook:
+       if test -d elektra-api/html; \
+         then cp -pR elektra-api $(distdir); \
+       else \
+         if test -d $(srcdir)/elektra-api; \
+           then cp -pR $(srcdir)/elektra-api $(distdir); \
+         fi; \
+       fi
+
+install-develdocDATA: $(api_DATA)
+       @$(NORMAL_INSTALL)
+       $(mkinstalldirs) $(DESTDIR)$(apihtmldir)
+       @if test -d elektra-api/html/; then api_d=elektra-api/html/; \
+         else api_d=$(srcdir)/elektra-api/html/; \
+       fi; \
+       list=`find $$api_d`; for p in $$list; do \
+       if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+       f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo " $(develdocDATA_INSTALL) $$d$$p $(DESTDIR)$(apihtmldir)/$$f"; \
+         $(develdocDATA_INSTALL) $$d$$p $(DESTDIR)$(apihtmldir)/$$f; \
+       done
+
+uninstall-develdocDATA:
+       -if test -d elektra-api/html/; then api_d=elektra-api/html/; \
+         else api_d=$(srcdir)/elektra-api/html/; \
+       fi; \
+       list=`find "$$api_d"`; for p in $$list; do \
+       f="`echo $$p | sed -e 's|^.*/||'`"; \
+         echo "rm -f $(DESTDIR)$(apihtmldir)/$$f"; \
+         rm -f $(DESTDIR)$(apihtmldir)/$$f; \
+        done
+       -rmdir $(DESTDIR)$(apihtmldir)
+
+#      $(mkinstalldirs) $(DESTDIR)$(apimandir)
+#      @list=`find elektra-api/man/man3`; for p in $$list; do \
+#      if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+#      f="`echo $$p | sed -e 's|^.*/||'`"; \
+#        echo " $(develdocDATA_INSTALL) $$d$$p $(DESTDIR)$(apimandir)/$$f"; \
+#        $(develdocDATA_INSTALL) $$d$$p $(DESTDIR)$(apimandir)/$$f; \
+#      done
+
+install-man3: $(man3_MANS)
+       @$(NORMAL_INSTALL)
+       test -z "$(man3dir)" || $(mkdir_p) "$(DESTDIR)$(man3dir)"
+       @if test -d elektra-api/man/; then man_d=elektra-api/man/; \
+         else man_d=$(srcdir)/elektra-api/man/; \
+       fi; \
+       list=`find $$man_d -name "k[db,ey,s]*.3"` ; for i in $$list; do \
+       if test -f "$$i"; then d=; else d="$(srcdir)/"; fi; \
+         ext='3'; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " $(INSTALL_DATA) $$d$$i $(DESTDIR)$(man3dir)/$$inst"; \
+         $(INSTALL_DATA) $$d$$i $(DESTDIR)$(man3dir)/$$inst; \
+       done
+
+
+uninstall-man3:
+       @$(NORMAL_UNINSTALL)
+       -if test -d elektra-api/man/; then man_d=elektra-api/man/; \
+         else man_d=$(srcdir)/elektra-api/man/; \
+       fi; \
+       list=`find $$man_d -name "k[db,ey,s]*.3"` ; for i in $$list; do \
+         ext='3'; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " rm -f '$(DESTDIR)$(man3dir)/$$inst'"; \
+         rm -f "$(DESTDIR)$(man3dir)/$$inst"; \
+       done
+
diff --git a/doc/UPLOAD b/doc/UPLOAD
new file mode 100644 (file)
index 0000000..4a5ffe7
--- /dev/null
@@ -0,0 +1,23 @@
+
+To update http://elektra.sf.net . . . . .
+
+Put this in Konqueror and start copying:
+
+       fish://aviram@shell.sf.net/home/groups/e/el/elektra/htdocs
+
+
+
+
+
+Or
+
+       scp *html aviram@shell.sf.net:/home/groups/e/el/elektra/htdocs
+
+
+
+
+
+Or simply:
+
+       make pub
+
diff --git a/doc/api.xml b/doc/api.xml
new file mode 100644 (file)
index 0000000..5fd9413
--- /dev/null
@@ -0,0 +1,23 @@
+<!--
+$Id$
+-->
+<section id="api"><title>Example C Program</title>
+       <para>Elektra's API consists of 3 classes of objects and methods to manipulate them. The main implementation of the library is written in C and is <ulink url="elektra-api/html/index.html">fully documented in Doxygen</ulink>.</para>
+       <graphic srccredit="classes" fileref="images/classes.png" label="the classes" align="center"/>
+       <para>The classes are:</para>
+       <variablelist>
+               <varlistentry><term><type>KDB</type></term>
+                       <listitem><simpara>A class of static methods only. You can't instantiate an object of this class. All the business logic for retrieving and commiting key data to physical media is owned by this class. Example methods are <function>kdbSetKey</function>, <function>kdbGetKey</function>, <function>kdbGetValue</function>, <function>kdbGetChildKeys</function>, etc. This last is one of the most powerfull, returning a <type>KeySet</type> containing all child keys of a folder key, with the option to retrieve them recursively, sorted, dereferencing links, etc.</simpara></listitem>
+               </varlistentry>
+               <varlistentry><term><type>Key</type></term>
+                       <listitem><simpara>The obvious entity class. Contains key's name, data and metadata. Example of its methods are <function>keyInit</function>, <function>keySetName</function>, <function>keySetUID</function>, <function>keyGetString</function>, <function>keyIsDir</function>, etc.</simpara></listitem>
+               </varlistentry>
+               <varlistentry><term><type>KeySet</type></term>
+                       <listitem><simpara>A group of <type>Key</type>s. Real applications use to get and set several keys, and this class is used to store corelated keys that were retrieved together in one shot. Example of its methods are <function>ksInsert</function>, <function>ksAppend</function>, <function>ksInsertKeys</function>, etc.</simpara></listitem>
+               </varlistentry>
+       </variablelist>
+       <para>The API is documented in manual pages <citerefentry><refentrytitle>key</refentrytitle><manvolnum>3</manvolnum></citerefentry> and <citerefentry><refentrytitle>kdb</refentrytitle><manvolnum>3</manvolnum></citerefentry> or you can <ulink url="elektra-api/html/index.html">browse</ulink> or <ulink url="elektra-api.tar.gz">download</ulink> the full API documentation.</para>
+
+       &apiexample;
+       
+</section>
diff --git a/doc/apiexample.xml b/doc/apiexample.xml
new file mode 100644 (file)
index 0000000..899a149
--- /dev/null
@@ -0,0 +1,19 @@
+<!--
+$Id$
+-->
+       <bridgehead id="exprm">Example program</bridgehead>
+       <para>Here is an example of how to use the Elektra API. This program will get all keys that matter under <varname>system/sw/MyApp</varname> manipulate some, and re-save them.</para>
+       &csource;
+       <para> Things to note in this source:</para>
+       <orderedlist>
+               <listitem><simpara>The keys are manipulated in <function>changeConfig()</function> after we called <function>kdbClose()</function>. You don't need the key database to be open to manipulate keys.</simpara></listitem>
+               <listitem><simpara>We called <function>kdbOpen()</function> again in <function>saveConfig()</function>, to prepare to commit the changes.</simpara></listitem>
+               <listitem><simpara>We called <function>ksClose()</function> in the end to free all memory resources we used.</simpara></listitem>
+       </orderedlist>
+       
+
+       
+       <bridgehead id="linking">Compiling and Linking</bridgehead>
+       <para>All Elektra methods are declared in the <filename>/usr/include/kdb.h</filename> file and defined in the <filename>/lib/libelektra.so</filename> library. So to compile it you simply need to:</para><screen><prompt>bash$ </prompt><command>cc `pkg-config --libs elektra` -o myapp myapp.c</command></screen>
+       <para>or, if you don't have <command>pkg-config</command>:</para><screen><prompt>bash$ </prompt><command>cc -L /lib -lelektra -o myapp myapp.c</command></screen>
+
diff --git a/doc/author.xml b/doc/author.xml
new file mode 100644 (file)
index 0000000..0535051
--- /dev/null
@@ -0,0 +1,21 @@
+
+<!--
+$Id$
+$LastChangedBy$
+-->
+
+               <copyright><year>2004</year><holder>Avi Alkalay</holder></copyright>
+               <date>March 2004</date>
+               <productname>Elektra Initiative</productname>
+               <authorgroup>
+                       <author>
+                               <firstname>Avi</firstname>
+                               <surname>Alkalay</surname>
+                               <email>avi at unix.sh</email>
+                               <affiliation>
+                                       <shortaffil>Linux Market Developer</shortaffil>
+                                       <orgdiv>Senior IT and Software Architect</orgdiv>
+                                       <orgname>IBM Linux Impact Team :: <ulink url="http://ibm.com/linux">ibm.com/linux</ulink></orgname>
+                               </affiliation>
+                       </author>
+               </authorgroup>
diff --git a/doc/bestpract.xml b/doc/bestpract.xml
new file mode 100644 (file)
index 0000000..d0a5e6b
--- /dev/null
@@ -0,0 +1,17 @@
+
+<!--
+$Id$
+$LastChangedBy$
+-->
+       <section id="bestpract"><title>Best Practices When Creating Keys</title>
+               <para>When using Elektra to store your application's configuration and state, please keep in mind the following rules:</para>
+                       <itemizedlist>
+                               <listitem><para>You are not allowed to create keys right under <filename>system</filename> or <filename>user</filename>.</para></listitem>
+                               
+                               <listitem><para>You are not allowed to create folder keys right under <filename>system</filename> or <filename>user</filename>. They are reserved for very essential OS subsystems.</para></listitem>
+                               
+                               <listitem><para>The keys for your application, called say <replaceable>MyApp</replaceable>, should be created under <filename>system/sw/MyApp</filename> and/or <filename>user/sw/MyApp</filename>.</para></listitem>
+                       </itemizedlist>
+       </section>
+
+       
diff --git a/doc/compared.xml b/doc/compared.xml
new file mode 100644 (file)
index 0000000..d94fb85
--- /dev/null
@@ -0,0 +1,75 @@
+<!--
+$Id$
+-->
+
+       <section id="compared">
+               <title>Elektra and Other Registry-like Softwares</title>
+               <para>In the last years, many hierarchical configuration systems appeared, and all of them have the same goal: avoid configuration code rewriting, integration between applications, and separation of configuration parameters from their mother software, so then other software can use same configurations.</para>
+               <para>Here we will compare some of them with Elektra.</para>
+               
+               <section id="compare.winreg"><title>Windows Registry</title>
+                       <para>The biggest problem with the Windows Registry is that it is Windows-specific and proprietary in design, API, and key naming conventions.</para>
+                       <para>Cross platform applications can't afford to use the winregistry API because there is nothing similar in other systems.</para>
+                       <para>Hopefully, Elektra will change that:</para>
+                       <orderedlist>
+                               <listitem><simpara>Elektra is highly portable, and there are developers porting and testing Elektra on Windows.</simpara></listitem>
+                               <listitem><simpara>There is a winreg Elektra backend (that works as a wrapper for the Windows Registry), so a cross platform program can use Elektra API, which maps Windows Registry's keys to a more cross platform namespace.</simpara></listitem>
+                       </orderedlist>
+                       <para><graphic srccredit="crossplat" fileref="images/crossplat.png" label="Elektra cross platform status" align="center"/></para>
+               </section>
+               
+               <section id="compare.gconf"><title>GConf</title>
+                       <para>The "G" comes from Gnome, which makes GConf part of the Gnome project. Some GConf characteristics:</para>
+                       <orderedlist>
+                               <listitem><simpara>Architected, designed and written for desktop applications only. Its developers have whishlists to make it system global, but a lot in the essential design will have to change, making it practically a new system, and the redesign did not started yet.</simpara></listitem>
+                               <listitem><simpara>GConf's namespaces were not architected for global use.</simpara></listitem>
+                               <listitem><simpara>GConf is a daemon, so if one day it will be system global, it will represent a single point of failure.</simpara></listitem>
+                               <listitem><simpara>GConf storage backend are XML files, which are big, take long time and system resources to parse or update.</simpara></listitem>
+                               <listitem><simpara>GConf seems to be not preocupied with access permissions, making it again a good solution only for single user desktop applications.</simpara></listitem>
+                               <listitem><para>GConf is a high level software with many library dependencies, perfect for high level desktop frameworks, but its dependencies are unacceptable for low level early boot stage programs like <filename>/sbin/init</filename>. GConf's dependencies look like this:</para>
+                                       <screen>$ ldd /usr/lib/libgconf-2.so
+    libgobject-2.0.so.0 => /usr/lib/libgobject-2.0.so.0 (0x0024b000)
+    libORBit-2.so.0 => /usr/lib/libORBit-2.so.0 (0x03dc7000)
+    libm.so.6 => /lib/tls/libm.so.6 (0x00b66000)
+    libgmodule-2.0.so.0 => /usr/lib/libgmodule-2.0.so.0 (0x00dda000)
+    libdl.so.2 => /lib/libdl.so.2 (0x00b8b000)
+    libgthread-2.0.so.0 => /usr/lib/libgthread-2.0.so.0 (0x0099d000)
+    libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0x00101000)
+    libpthread.so.0 => /lib/tls/libpthread.so.0 (0x00c7c000)
+    libc.so.6 => /lib/tls/libc.so.6 (0x00a3a000)
+    libpopt.so.0 => /usr/lib/libpopt.so.0 (0x0077b000)
+    /lib/ld-linux.so.2 (0x00a21000)</screen>
+                               <para>This is useless for an early boot stage program (<filename>/usr/lib</filename> may be still unmounted), and for a very small OS installation that won't require desktop features, like a router, small firewall, or any other appliance. On the other hand, a program that wants to use Elektra will only link to libelektra.so which has a minimum of natural dependencies:</para>
+                               <screen>$ ldd /lib/libelektra.so
+    libc.so.6 => /lib/tls/libc.so.6
+    /lib/ld-linux.so.2 => /lib/ld-linux.so.2</screen>
+                               </listitem>
+                               <listitem><simpara>All Gnome programs use GConf, which makes them already integrated in the configuration level.</simpara></listitem>
+                               <listitem><simpara>In the Gnome context, Elektra fits well as a GConf backend, this way no program needs to be modified. With an additional Elektra backend for KDE's KConfig-XT, this integration will span a single framework, and we'll see KDE programs using Gnome's configurations and vice-versa.</simpara></listitem>
+                       </orderedlist>
+                       
+                       <para>Compared to GConf, Elektra is not a daemon, was designed from the begining for global system usage, is lightweight, is concerned about security and avoids any dependencies.</para>
+               </section>
+               
+               <section id="compare.dconf"><title>D-Conf</title>
+                       <para>D-Conf generated a long discussion on FreeDesktop.org's XDG mailling list. Its objective is to standarize configuration across desktop frameworks like Gnome and KDE, so no focus is being devoted to global non-desktop system configurations.</para>
+                       <para>It was very difficult to find the D-Conf website (not found in the end), and seems that all is available is that XDG discussion, and a wiki page containing analisys of what is required, and development directions.</para>
+                       <para>Conclusion: D-Conf is vaporware.</para>
+               </section>
+               
+               <section id="compare.other"><title>LDAP and Other</title>
+                       <bridgehead id="compare.uniconf">UniConf</bridgehead>
+                       <para>UniConf started as configuration library and daemon to be used in some specific programs. It claims to be very flexible, capable of having backends and frontends, as any configuration library can have.</para>
+                       <para>Its drawbacks are, as in GConf, some dependencies, and a plus (or minus): it was developed in C++ which makes it also dependent on higher level libraries, and require client programs and programmers to be written in C++, unless they get a pure C frontend.</para>
+                       <para>Some UniConf developers started to write a UniConf backend for Elektra, which means that you'll be able to use Elektra's simple API and tools, and UniConf will be responsible for all low level I/O.</para>
+                       <para>Elektra and UniConf can live together in an entirely Elektrified world, leveraging UniConf flexibility as a storage backend.</para>
+               
+                       <bridgehead id="compare.config4gnu">Config 4 GNU</bridgehead>
+                       <para>Looks like this is a dead project, since nobody talks about it for long time. Last website update was in 2004.</para>
+                       <para>Very ambitious, this project claimed they will write many backends, one for each configuration file available, and after that, some can access say Samba's configurations normalized through their API and tools, what they didn't realized is that to write many different config file parsers (compilers) and keep tracking their changes, is way more difficult than to write patches for each software.</para>
+               
+                       <bridgehead id="compare.hiveconf">Hiveconf</bridgehead>
+                       <para>This is a dead project. It is a Python library that accesses ini-style configuration files.</para>
+               </section>
+
+       </section>
diff --git a/doc/docbook.css b/doc/docbook.css
new file mode 100644 (file)
index 0000000..23c23c4
--- /dev/null
@@ -0,0 +1,275 @@
+/* $Id */
+
+<style type="text/css">
+
+       body, .article, .titlepage, .toc, .section, .appendix, .glossary {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+       }
+
+
+       /* Titlepage stuff */
+
+       .titlepage .title {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 26px;
+               color: #ff6600;
+               text-align: left;
+       }
+
+       .titlepage .pubdate {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 14pt;
+       }
+
+
+
+
+/* Author related stuff */
+
+       .authorgroup {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               margin-left: 30pt;
+               margin-top: 10pt;
+               margin-bottom: 0pt;
+               border-left-style: dotted;
+               border-width: 1pt;
+               border-color: #ff9933;
+               padding-left: 10pt;
+       }
+
+       h3.author {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 14px;
+               margin-top: 15pt;
+               margin-bottom: 0pt;
+       }
+
+       .contrib {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               text-align: justify;
+       }
+
+
+
+
+/* Table of Contents rendering */
+
+       div.toc {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 18px;
+               margin-bottom: 50pt;
+               margin-left: 60pt;
+       }
+
+       .toc p {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 26px;
+               color: #6565FF;
+       }
+
+       .toc dl dt {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               margin-left: 20pt;
+               font-size: 18px;
+               color: #ff6600;
+       }
+
+       .toc dl dt a {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 18px;
+               color: #ff6600;
+       }
+
+       .toc dl dd dl dt {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 16px;
+               margin-left: 0pt;
+               color: #ff9933;
+       }
+
+       .toc dl dd dl dt a {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 16px;
+               color: #ff9933;
+       }
+
+
+
+
+
+/* Section and appendix stuff */
+
+       .section, .appendix, .glossary {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               text-align: justify;
+               margin-top: 50pt;
+               margin-bottom: 0pt;
+               margin-left: 30pt;
+               margin-right: 30pt;
+       }
+
+       /* Nested sections */
+       .section .section, .appendix .appendix {
+               margin-left: 0pt;
+               margin-right: 0pt;
+               margin-top: 10pt;
+       }
+
+       .section .titlepage .title, .appendix .title, .glossary .title {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 22px;
+               color: #ff6600;
+               margin-top:0pt;
+               border-top-style: dotted;
+               border-width: 1pt;
+               border-color: #6565FF;
+       }
+
+
+       .section .section .titlepage .title {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 18px;
+               color: #ff9933;
+               margin-top:0pt;
+               border-top-style: dotted;
+               border-width: 1pt;
+               border-color: #6565FF;
+       }
+
+       .bridgehead {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 18px;
+               color: #ff9933;
+               margin-bottom: 0pt;
+       }
+
+
+
+/* Tips, warnings, etc */
+
+       .note p, .tip p, .important p, .warning p, .caution p {
+               color: #ff9933;
+       }
+
+
+       
+
+/* Tables, examples and screens rendering */
+
+       .table .title, .example .title, .figure .title {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               color: black;
+               border: 0pt;
+               margin: 0pt;
+               margin-top: 5pt;
+       }
+
+       .glossary dt {
+               font-weight: bold;
+       }
+
+       .table table {
+               background-color: #FFF7EA;
+       }
+
+       .table table th {
+               font-family: Tahoma, Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               color: black;
+               border-width: 0pt;
+               border-bottom-width: 2pt;
+               border-style: solid;
+               border-color: #6565FF;
+       }
+
+       .table table td {
+               font-family: Tahoma, Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               color: black;
+               border-width: 0pt;
+               border-bottom-width: 1pt;
+               border-style: solid;
+               /*background: #FFF7EA;*/
+       }
+
+       .table table td .strong em {
+               font-family: Tahoma, Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               font-weight: bold;
+       }
+
+       .programlisting, .screen {
+               font-family: lucidatypewriter, lucida console, courier, tahoma;
+               font-size: 12px;
+               color: #555555;
+               background-color: #FFF7EA;
+               border-width: 2pt;
+               border-style: solid;
+               border-color: #CCCCCC;
+       }
+
+       .variablelist dt {
+               margin-top: 20pt;
+       }
+
+
+
+/* Inline elements rendering */
+
+       .command {
+               color: #298952;
+       }
+
+       .type {
+               color: #29AA52;
+               font-weight: bold;
+       }
+
+       p {
+               font-family: Verdana, Helvetica, Lucida, Arial;
+               font-size: 11px;
+               text-align: justify;
+       }
+
+       .filename, .envar, tt {
+               font-family: lucidatypewriter, lucida console, courier, tahoma;
+               font-weight: bold;
+               /*      font-size: 10px; */
+       }
+
+       a:link {
+               text-decoration: none;
+       }
+
+       a:active {
+               color: blue;
+       }
+
+       a:visited {
+               color: #3355cc;
+               text-decoration: none;
+       }
+
+       a:hover {
+               color: #4477cc;
+               text-decoration: underline;
+       }
+
+
+
+/* Misc */
+
+       hr {
+               border-color: #6565FF;
+       }
+
+       ul li {
+               list-style-image: url(images/img_arrow.png);
+       }
+
+</style>
diff --git a/doc/doxygen.css b/doc/doxygen.css
new file mode 100644 (file)
index 0000000..2476d4d
--- /dev/null
@@ -0,0 +1,278 @@
+
+// $Id$
+
+H1 {
+       text-align: center;
+       font-family: Verdana, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-weight: bold;
+       font-size: 28px;
+       color: #ff6600;
+}
+
+H2 {
+       font-family: Verdana, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-weight: bold;
+       font-size: 22px;
+       color: #ff6600;
+}
+
+H3 {
+       font-size: 18px;
+       color: #ff9933;
+}
+
+CAPTION { font-weight: bold }
+
+CODE {
+       font-weight: bold;
+}
+
+DIV.qindex { width: 100%;
+             background-color: #eeeeff;
+             border: 4px solid #eeeeff;
+             text-align: center;
+             margin-bottom: 2px
+}
+
+A.qindex { text-decoration: none; font-weight: bold; color: #0000ee }
+A.qindex:visited { text-decoration: none; font-weight: bold; color: #0000ee }
+A.qindex:hover { text-decoration: none; background-color: #ddddff }
+A.qindexHL { text-decoration: none; font-weight: bold;
+             background-color: #6666cc;
+             color: #ffffff
+           }
+A.qindexHL:hover { text-decoration: none; background-color: #6666cc; color: #ffffff }
+A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff }
+A.el { text-decoration: none; font-weight: bold }
+A.elRef { font-weight: bold }
+A.code { text-decoration: none; font-weight: normal; color: #4444ee }
+A.codeRef { font-weight: normal; color: #4444ee }
+A:hover { text-decoration: none; background-color: #f2f2ff }
+
+DL.el { margin-left: -1cm }
+
+DL {
+       font-family: Verdana, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 11px;
+}
+
+DIV.fragment {
+       width: 98%;
+       border: 1px solid #CCCCCC;
+       background-color: #f5f5f5;
+       padding-left: 4px;
+       margin: 4px;
+}
+
+DIV.ah {
+       background-color: black;
+       font-weight: bold;
+       color: #ffffff;
+       margin-bottom: 3px;
+       margin-top: 3px
+}
+
+TD.md {
+       color: #ff6600;
+       font-family: Tahoma, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 18px;
+       font-weight: bold;
+}
+
+TD.mdname1 {
+       font-weight: bold;
+       font-size: 18px;
+       color: #ff6600;
+}
+
+TD.mdname {
+       font-weight: bold;
+       font-size: 18px;
+       color: #ff6600;
+}
+
+DIV.groupHeader {
+       margin-left: 16px;
+       margin-top: 12px;
+       margin-bottom: 6px;
+       font-weight: bold
+}
+
+DIV.groupText {
+       margin-left: 16px;
+       font-style: italic;
+       font-size: smaller
+}
+
+BODY {
+       font-family: Verdana, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 11px;
+       background: white;
+       color: black;
+       margin-right: 20px;
+       margin-left: 20px;
+}
+
+P {
+       font-family: Verdana, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 11px;
+}
+
+EM {
+       font-family: Tahoma, Bitstream Vera Sans, Helvetica, sans-serif;
+       color: #ff6600;
+       font-style: normal;
+       text-decoration: underline;
+}
+
+TD {
+       font-family: Verdana, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 11px;
+}
+
+TD.indexkey {
+   background-color: #eeeeff; 
+   font-weight: bold; 
+   padding-right  : 10px; 
+   padding-top    : 2px; 
+   padding-left   : 10px; 
+   padding-bottom : 2px; 
+   margin-left    : 0px; 
+   margin-right   : 0px; 
+   margin-top     : 2px; 
+   margin-bottom  : 2px  
+}
+
+TD.indexvalue { 
+   background-color: #eeeeff; 
+   font-style: italic; 
+   padding-right  : 10px; 
+   padding-top    : 2px; 
+   padding-left   : 10px; 
+   padding-bottom : 2px; 
+   margin-left    : 0px; 
+   margin-right   : 0px; 
+   margin-top     : 2px; 
+   margin-bottom  : 2px  
+}
+
+TR.memlist {
+   background-color: #f0f0f0; 
+}
+
+P.formulaDsp { text-align: center; }
+IMG.formulaDsp { }
+IMG.formulaInl { vertical-align: middle; }
+
+SPAN.keyword       { color: #008000 }
+
+SPAN.keywordtype   { color: #604020 }
+
+SPAN.keywordflow   { color: #e08000 }
+
+SPAN.comment       { color: #800000 }
+
+SPAN.preprocessor  { color: #806020 }
+
+SPAN.stringliteral { color: #002080 }
+
+SPAN.charliteral   { color: #008080 }
+
+.mdTable {
+       margin-top:0pt;
+       margin-bottom:0pt ! important;
+       border-top-style: groove;
+       border-left-style: groove;
+       border-width: 4pt;
+       border-color: #6565FF;
+}
+
+.mdRow {
+       padding: 8px 20px;
+}
+
+.mdescLeft {
+       font-size: smaller;
+       font-family: Tahoma, Bitstream Vera Sans, Helvetica, sans-serif;
+       background-color: #FAFAFA;
+       padding-left: 8px;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+
+.mdescRight {
+       font-family: Tahoma, Bitstream Vera Sans, Helvetica, sans-serif;
+       background-color: #FAFAFA;
+       padding-left: 4px;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+       padding-bottom: 0px;
+       padding-right: 8px;
+}
+
+.memItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-style: solid;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-family: Tahoma, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 11px;
+}
+
+.memItemRight {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-style: solid;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-family: Tahoma, Bitstream Vera Sans, Helvetica, sans-serif;
+       font-size: 11px;
+}
+
+.search     {
+       color: #0000ee;
+       font-weight: bold;
+}
+
+FORM.search {
+       margin-bottom: 0px;
+       margin-top: 0px;
+}
+
+INPUT.search {
+       font-size: 75%;
+       color: #000080;
+       font-weight: normal;
+       background-color: #eeeeff;
+}
+
+TD.tiny      {
+       font-size: 75%;
+}
diff --git a/doc/elektra-api/html/doxygen.css b/doc/elektra-api/html/doxygen.css
new file mode 100644 (file)
index 0000000..22c4843
--- /dev/null
@@ -0,0 +1,473 @@
+BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
+       font-family: Geneva, Arial, Helvetica, sans-serif;
+}
+BODY,TD {
+       font-size: 90%;
+}
+H1 {
+       text-align: center;
+       font-size: 160%;
+}
+H2 {
+       font-size: 120%;
+}
+H3 {
+       font-size: 100%;
+}
+CAPTION { 
+       font-weight: bold 
+}
+DIV.qindex {
+       width: 100%;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       padding: 2px;
+       line-height: 140%;
+}
+DIV.navpath {
+       width: 100%;
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       padding: 2px;
+       line-height: 140%;
+}
+DIV.navtab {
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       margin-right: 15px;
+       padding: 2px;
+}
+TD.navtab {
+       font-size: 70%;
+}
+A.qindex {
+       text-decoration: none;
+       font-weight: bold;
+       color: #1A419D;
+}
+A.qindex:visited {
+       text-decoration: none;
+       font-weight: bold;
+       color: #1A419D
+}
+A.qindex:hover {
+       text-decoration: none;
+       background-color: #ddddff;
+}
+A.qindexHL {
+       text-decoration: none;
+       font-weight: bold;
+       background-color: #6666cc;
+       color: #ffffff;
+       border: 1px double #9295C2;
+}
+A.qindexHL:hover {
+       text-decoration: none;
+       background-color: #6666cc;
+       color: #ffffff;
+}
+A.qindexHL:visited { 
+       text-decoration: none; 
+       background-color: #6666cc; 
+       color: #ffffff 
+}
+A.el { 
+       text-decoration: none; 
+       font-weight: bold 
+}
+A.elRef { 
+       font-weight: bold 
+}
+A.code:link { 
+       text-decoration: none; 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.code:visited { 
+       text-decoration: none; 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.codeRef:link { 
+       font-weight: normal; 
+       color: #0000FF
+}
+A.codeRef:visited { 
+       font-weight: normal; 
+       color: #0000FF
+}
+A:hover { 
+       text-decoration: none;  
+       background-color: #f2f2ff 
+}
+DL.el { 
+       margin-left: -1cm 
+}
+.fragment {
+       font-family: monospace, fixed;
+       font-size: 95%;
+}
+PRE.fragment {
+       border: 1px solid #CCCCCC;
+       background-color: #f5f5f5;
+       margin-top: 4px;
+       margin-bottom: 4px;
+       margin-left: 2px;
+       margin-right: 8px;
+       padding-left: 6px;
+       padding-right: 6px;
+       padding-top: 4px;
+       padding-bottom: 4px;
+}
+DIV.ah { 
+       background-color: black; 
+       font-weight: bold; 
+       color: #ffffff; 
+       margin-bottom: 3px; 
+       margin-top: 3px 
+}
+
+DIV.groupHeader {
+       margin-left: 16px;
+       margin-top: 12px;
+       margin-bottom: 6px;
+       font-weight: bold;
+}
+DIV.groupText { 
+       margin-left: 16px; 
+       font-style: italic; 
+       font-size: 90% 
+}
+BODY {
+       background: white;
+       color: black;
+       margin-right: 20px;
+       margin-left: 20px;
+}
+TD.indexkey {
+       background-color: #e8eef2;
+       font-weight: bold;
+       padding-right  : 10px;
+       padding-top    : 2px;
+       padding-left   : 10px;
+       padding-bottom : 2px;
+       margin-left    : 0px;
+       margin-right   : 0px;
+       margin-top     : 2px;
+       margin-bottom  : 2px;
+       border: 1px solid #CCCCCC;
+}
+TD.indexvalue {
+       background-color: #e8eef2;
+       font-style: italic;
+       padding-right  : 10px;
+       padding-top    : 2px;
+       padding-left   : 10px;
+       padding-bottom : 2px;
+       margin-left    : 0px;
+       margin-right   : 0px;
+       margin-top     : 2px;
+       margin-bottom  : 2px;
+       border: 1px solid #CCCCCC;
+}
+TR.memlist {
+       background-color: #f0f0f0; 
+}
+P.formulaDsp { 
+       text-align: center; 
+}
+IMG.formulaDsp {
+}
+IMG.formulaInl { 
+       vertical-align: middle; 
+}
+SPAN.keyword       { color: #008000 }
+SPAN.keywordtype   { color: #604020 }
+SPAN.keywordflow   { color: #e08000 }
+SPAN.comment       { color: #800000 }
+SPAN.preprocessor  { color: #806020 }
+SPAN.stringliteral { color: #002080 }
+SPAN.charliteral   { color: #008080 }
+SPAN.vhdldigit     { color: #ff00ff }
+SPAN.vhdlchar      { color: #000000 }
+SPAN.vhdlkeyword   { color: #700070 }
+SPAN.vhdllogic     { color: #ff0000 }
+
+.mdescLeft {
+       padding: 0px 8px 4px 8px;
+       font-size: 80%;
+       font-style: italic;
+       background-color: #FAFAFA;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+.mdescRight {
+        padding: 0px 8px 4px 8px;
+       font-size: 80%;
+       font-style: italic;
+       background-color: #FAFAFA;
+       border-top: 1px none #E0E0E0;
+       border-right: 1px none #E0E0E0;
+       border-bottom: 1px none #E0E0E0;
+       border-left: 1px none #E0E0E0;
+       margin: 0px;
+}
+.memItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memItemRight {
+       padding: 1px 8px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplItemLeft {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: none;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplItemRight {
+       padding: 1px 8px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: none;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.memTemplParams {
+       padding: 1px 0px 0px 8px;
+       margin: 4px;
+       border-top-width: 1px;
+       border-right-width: 1px;
+       border-bottom-width: 1px;
+       border-left-width: 1px;
+       border-top-color: #E0E0E0;
+       border-right-color: #E0E0E0;
+       border-bottom-color: #E0E0E0;
+       border-left-color: #E0E0E0;
+       border-top-style: solid;
+       border-right-style: none;
+       border-bottom-style: none;
+       border-left-style: none;
+       color: #606060;
+       background-color: #FAFAFA;
+       font-size: 80%;
+}
+.search { 
+       color: #003399;
+       font-weight: bold;
+}
+FORM.search {
+       margin-bottom: 0px;
+       margin-top: 0px;
+}
+INPUT.search { 
+       font-size: 75%;
+       color: #000080;
+       font-weight: normal;
+       background-color: #e8eef2;
+}
+TD.tiny { 
+       font-size: 75%;
+}
+a {
+       color: #1A41A8;
+}
+a:visited {
+       color: #2A3798;
+}
+.dirtab { 
+       padding: 4px;
+       border-collapse: collapse;
+       border: 1px solid #84b0c7;
+}
+TH.dirtab { 
+       background: #e8eef2;
+       font-weight: bold;
+}
+HR { 
+       height: 1px;
+       border: none;
+       border-top: 1px solid black;
+}
+
+/* Style for detailed member documentation */
+.memtemplate {
+       font-size: 80%;
+       color: #606060;
+       font-weight: normal;
+       margin-left: 3px;
+} 
+.memnav { 
+       background-color: #e8eef2;
+       border: 1px solid #84b0c7;
+       text-align: center;
+       margin: 2px;
+       margin-right: 15px;
+       padding: 2px;
+}
+.memitem {
+       padding: 4px;
+       background-color: #eef3f5;
+       border-width: 1px;
+       border-style: solid;
+       border-color: #dedeee;
+       -moz-border-radius: 8px 8px 8px 8px;
+}
+.memname {
+       white-space: nowrap;
+       font-weight: bold;
+}
+.memdoc{
+       padding-left: 10px;
+}
+.memproto {
+       background-color: #d5e1e8;
+       width: 100%;
+       border-width: 1px;
+       border-style: solid;
+       border-color: #84b0c7;
+       font-weight: bold;
+       -moz-border-radius: 8px 8px 8px 8px;
+}
+.paramkey {
+       text-align: right;
+}
+.paramtype {
+       white-space: nowrap;
+}
+.paramname {
+       color: #602020;
+       font-style: italic;
+       white-space: nowrap;
+}
+/* End Styling for detailed member documentation */
+
+/* for the tree view */
+.ftvtree {
+       font-family: sans-serif;
+       margin:0.5em;
+}
+/* these are for tree view when used as main index */
+.directory { 
+       font-size: 9pt; 
+       font-weight: bold; 
+}
+.directory h3 { 
+       margin: 0px; 
+       margin-top: 1em; 
+       font-size: 11pt; 
+}
+
+/* The following two styles can be used to replace the root node title */
+/* with an image of your choice.  Simply uncomment the next two styles, */
+/* specify the name of your image and be sure to set 'height' to the */
+/* proper pixel height of your image. */
+
+/* .directory h3.swap { */
+/*     height: 61px; */
+/*     background-repeat: no-repeat; */
+/*     background-image: url("yourimage.gif"); */
+/* } */
+/* .directory h3.swap span { */
+/*     display: none; */
+/* } */
+
+.directory > h3 { 
+       margin-top: 0; 
+}
+.directory p { 
+       margin: 0px; 
+       white-space: nowrap; 
+}
+.directory div { 
+       display: none; 
+       margin: 0px; 
+}
+.directory img { 
+       vertical-align: -30%; 
+}
+/* these are for tree view when not used as main index */
+.directory-alt { 
+       font-size: 100%; 
+       font-weight: bold; 
+}
+.directory-alt h3 { 
+       margin: 0px; 
+       margin-top: 1em; 
+       font-size: 11pt; 
+}
+.directory-alt > h3 { 
+       margin-top: 0; 
+}
+.directory-alt p { 
+       margin: 0px; 
+       white-space: nowrap; 
+}
+.directory-alt div { 
+       display: none; 
+       margin: 0px; 
+}
+.directory-alt img { 
+       vertical-align: -30%; 
+}
+
diff --git a/doc/elektra-api/html/doxygen.png b/doc/elektra-api/html/doxygen.png
new file mode 100644 (file)
index 0000000..f0a274b
Binary files /dev/null and b/doc/elektra-api/html/doxygen.png differ
diff --git a/doc/elektra-api/html/err.html b/doc/elektra-api/html/err.html
new file mode 100644 (file)
index 0000000..1f1627c
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Error</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1><a class="anchor" name="err">Error </a></h1><a class="anchor" name="_err000002"></a> <dl>
+<dt>Global <a class="el" href="group__backendhelper.html#g89aa8c310a5720766639c305e643c069">kdbbReadLock</a>  </dt>
+<dd>sets KDB_ERR_NOLOCK when locking failed </dd>
+</dl>
+<p>
+<a class="anchor" name="_err000003"></a> <dl>
+<dt>Global <a class="el" href="group__backendhelper.html#gb969d5d25762464c5f719f4f90757fe8">kdbbUnlock</a>  </dt>
+<dd>sets KDB_ERR_NOLOCK when locking failed </dd>
+</dl>
+<p>
+<a class="anchor" name="_err000001"></a> <dl>
+<dt>Global <a class="el" href="group__backendhelper.html#gcea5819334a71744a54a6b290a8c3bdc">kdbbWriteLock</a>  </dt>
+<dd>sets KDB_ERR_NOLOCK when locking failed </dd>
+</dl>
+<p>
+<a class="anchor" name="_err000004"></a> <dl>
+<dt>Global <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend</a>  </dt>
+<dd>In normal execution cases a positive value will be returned. But in some cases you are not able to set keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added with 0.7.1)<p>
+</dd>
+</dl>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/graph_legend.dot b/doc/elektra-api/html/graph_legend.dot
new file mode 100644 (file)
index 0000000..4df0f1a
--- /dev/null
@@ -0,0 +1,22 @@
+digraph G
+{
+  edge [fontname="FreeSans",fontsize=10,labelfontname="FreeSans",labelfontsize=10];
+  node [fontname="FreeSans",fontsize=10,shape=record];
+  Node9 [shape="box",label="Inherited",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",fillcolor="grey75",style="filled" fontcolor="black"];
+  Node10 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid",fontname="FreeSans"];
+  Node10 [shape="box",label="PublicBase",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="black",URL="$classPublicBase.html"];
+  Node11 -> Node10 [dir=back,color="midnightblue",fontsize=10,style="solid",fontname="FreeSans"];
+  Node11 [shape="box",label="Truncated",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="red",URL="$classTruncated.html"];
+  Node13 -> Node9 [dir=back,color="darkgreen",fontsize=10,style="solid",fontname="FreeSans"];
+  Node13 [shape="box",label="ProtectedBase",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="black",URL="$classProtectedBase.html"];
+  Node14 -> Node9 [dir=back,color="firebrick4",fontsize=10,style="solid",fontname="FreeSans"];
+  Node14 [shape="box",label="PrivateBase",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="black",URL="$classPrivateBase.html"];
+  Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid",fontname="FreeSans"];
+  Node15 [shape="box",label="Undocumented",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="grey75"];
+  Node16 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid",fontname="FreeSans"];
+  Node16 [shape="box",label="Templ< int >",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="black",URL="$classTempl.html"];
+  Node17 -> Node16 [dir=back,color="orange",fontsize=10,style="dashed",label="< int >",fontname="FreeSans"];
+  Node17 [shape="box",label="Templ< T >",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="black",URL="$classTempl.html"];
+  Node18 -> Node9 [dir=back,color="darkorchid3",fontsize=10,style="dashed",label="m_usedClass",fontname="FreeSans"];
+  Node18 [shape="box",label="Used",fontsize=10,height=0.2,width=0.4,fontname="FreeSans",color="black",URL="$classUsed.html"];
+}
diff --git a/doc/elektra-api/html/graph_legend.html b/doc/elektra-api/html/graph_legend.html
new file mode 100644 (file)
index 0000000..81ee9ca
--- /dev/null
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Graph Legend</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Graph Legend</h1>This page explains how to interpret the graphs that are generated by doxygen.<p>
+Consider the following example: <div class="fragment"><pre class="fragment"><span class="comment">/*! Invisible class because of truncation */</span>
+<span class="keyword">class </span>Invisible { };
+<span class="comment"></span>
+<span class="comment">/*! Truncated class, inheritance relation is hidden */</span>
+<span class="keyword">class </span>Truncated : <span class="keyword">public</span> Invisible { };
+
+<span class="comment">/* Class not documented with doxygen comments */</span>
+<span class="keyword">class </span>Undocumented { };
+<span class="comment"></span>
+<span class="comment">/*! Class that is inherited using public inheritance */</span>
+<span class="keyword">class </span>PublicBase : <span class="keyword">public</span> Truncated { };
+<span class="comment"></span>
+<span class="comment">/*! A template class */</span>
+<span class="keyword">template</span>&lt;<span class="keyword">class</span> T&gt; <span class="keyword">class </span>Templ { };
+<span class="comment"></span>
+<span class="comment">/*! Class that is inherited using protected inheritance */</span>
+<span class="keyword">class </span>ProtectedBase { };
+<span class="comment"></span>
+<span class="comment">/*! Class that is inherited using private inheritance */</span>
+<span class="keyword">class </span>PrivateBase { };
+<span class="comment"></span>
+<span class="comment">/*! Class that is used by the Inherited class */</span>
+<span class="keyword">class </span>Used { };
+<span class="comment"></span>
+<span class="comment">/*! Super class that inherits a number of other classes */</span>
+<span class="keyword">class </span>Inherited : <span class="keyword">public</span> PublicBase,
+                  <span class="keyword">protected</span> ProtectedBase,
+                  <span class="keyword">private</span> PrivateBase,
+                  <span class="keyword">public</span> Undocumented,
+                  <span class="keyword">public</span> Templ&lt;int&gt;
+{
+  <span class="keyword">private</span>:
+    Used *m_usedClass;
+};
+</pre></div> If the <code>MAX_DOT_GRAPH_HEIGHT</code> tag in the configuration file is set to 240 this will result in the following graph:<p>
+<center><div align="center">
+<img src="graph_legend.png" alt="graph_legend.png">
+</div>
+</center> <p>
+The boxes in the above graph have the following meaning: <ul>
+<li>
+A filled gray box represents the struct or class for which the graph is generated. </li>
+<li>
+A box with a black border denotes a documented struct or class. </li>
+<li>
+A box with a grey border denotes an undocumented struct or class. </li>
+<li>
+A box with a red border denotes a documented struct or class forwhich not all inheritance/containment relations are shown. A graph is truncated if it does not fit within the specified boundaries. </li>
+</ul>
+The arrows have the following meaning: <ul>
+<li>
+A dark blue arrow is used to visualize a public inheritance relation between two classes. </li>
+<li>
+A dark green arrow is used for protected inheritance. </li>
+<li>
+A dark red arrow is used for private inheritance. </li>
+<li>
+A purple dashed arrow is used if a class is contained or used by another class. The arrow is labeled with the variable(s) through which the pointed class or struct is accessible. </li>
+<li>
+A yellow dashed arrow denotes a relation between a template instance and the template class it was instantiated from. The arrow is labeled with the template parameters of the instance. </li>
+</ul>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__backend.html b/doc/elektra-api/html/group__backend.html
new file mode 100644 (file)
index 0000000..2b74696
--- /dev/null
@@ -0,0 +1,556 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KDB Backends :: Elektra framework for pluggable backends</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KDB Backends :: Elektra framework for pluggable backends</h1>The tactics to create pluggable backends to libelektra.so.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Enumerations</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">enum &nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#ge857eadce7085ecd3a24671a1826940a">backend_t</a> { <br>
+&nbsp;&nbsp;<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940a4293cef5efac0aa0ad61c18e41c0848c">KDB_BE_OPEN</a> = 1, 
+<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940a28f303117ceb95838f92780007407955">KDB_BE_CLOSE</a> = 1&lt;&lt;1, 
+<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940ae1e894d0fe6e3f05f444f03de2bc7885">KDB_BE_GET</a> = 1&lt;&lt;2, 
+<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940ab2fe708111d49ea21669db6bd7276007">KDB_BE_SET</a> = 1&lt;&lt;3, 
+<br>
+&nbsp;&nbsp;<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940aeca3339f3b62cd2136646cb59678603e">KDB_BE_VERSION</a> = 1&lt;&lt;4, 
+<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940ac60eb75a1391a6a4d30420f4079e4244">KDB_BE_DESCRIPTION</a> = 1&lt;&lt;5, 
+<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940adc638374d367d0a52d9201f782eb9f5c">KDB_BE_AUTHOR</a> = 1&lt;&lt;6, 
+<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940ad3348e5ab9428da29ba7b51886b87bbe">KDB_BE_LICENCE</a> = 1&lt;&lt;7, 
+<br>
+&nbsp;&nbsp;<a class="el" href="group__backend.html#gge857eadce7085ecd3a24671a1826940adab7f08d13e598d0fafa2db1aa707b2f">KDB_BE_END</a> = 0
+<br>
+ }</td></tr>
+
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KDB *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport</a> (const char *backendName,...)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend</a> (KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend</a> (KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend</a> (KDB *handle, KeySet *returned, const Key *parentKey)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend</a> (KDB *handle, KeySet *returned, const Key *parentKey)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backend.html#g40bb01174ff716b57525c4dd7730aac8">KDBEXPORT</a> (backend)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+The tactics to create pluggable backends to libelektra.so. 
+<p>
+<h2><a class="anchor" name="intro">
+Introduction</a></h2>
+<dl class="since" compact><dt><b>Since:</b></dt><dd>Since version 0.4.9, Elektra can dynamically load different key storage backends.<p>
+Since version 0.7.0 Elektra can have multiple storage backends, called just backends henceforth, at once for different purposes.</dd></dl>
+<dl class="user" compact><dt><b>Definition: You refers to the implementation of the function in this specification.</b></dt><dd>If you read the documentation about <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a>, then the caller is <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> which is the only function which can and will call (invoke) you. The Preconditions will always be met by the caller, you can count on them. But you (as said before we speak about the function) need to take care that all Postconditions are met.</dd></dl>
+<h3><a class="anchor" name="overview">
+Overview</a></h3>
+The methods of class KDB that are backend dependent are only <a class="el" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend()</a>, <a class="el" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend()</a>, <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a>, <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend()</a> and <a class="el" href="group__backend.html#g40bb01174ff716b57525c4dd7730aac8">KDBEXPORT()</a> to export these methods. A backend must implement each of them. A detailed specification of these methods and methods needed in that context follows in this Documentation Module.<p>
+The other KDB methods are higher level. They use the above methods to do their job, and generally don't have to be reimplemented for a different backend, but there might be a solution to do so for higher performance in future. kdbh* methods are for access to the internals of KDB, which will be passed to all functions.<h3><a class="anchor" name="incl">
+Include Files</a></h3>
+The backend implementation must include: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdbbackend.h&gt;</span>
+</pre></div> to have direct access to the structs, which is currently needed to access the capability structure.<p>
+Don't include kdb.h, it will be automatically included and some macros will avoid redefining structs where you have more insight from a backend than you would normally have. Additionally you get the declaration of all functions described here, except the one you have to implement.<h3><a class="anchor" name="dyn">
+Dynamic Mounting</a></h3>
+An elektrified program will use elektra/libelektra-default.so as its default backend. This backend provides the system/ hierarchy and some base configuration in system/elektra for elektra itself. Everything below system/ and the other hierarchies can be stored in any different backend. This is allowed through the technique mounting. A backend can be mounted to any path except system/ and system/elektra.<p>
+A backends is guaranteed to be loaded whenever calling <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> or <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> requires the backend, but may already be loaded at <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a>. It might be loaded explizit by <a class="el" href="group__kdb.html#g40e35f26cc69bd43ef1b2207f4fa121b">kdbMount()</a> at any time after <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a>. Backends get a chance to initialize by calling <a class="el" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend()</a> whenever they are loaded.<p>
+Using <a class="el" href="group__kdb.html#g400ca66a9bdc04ecadb66d84dc06bd55">kdbUnmount()</a> a backend may closed during runtime. All backends will be closed when <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a> is called. Backends might be unloaded after some time of inactivity or other reasons. After loading backends get a chance to cleanup by calling <a class="el" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend()</a>.<p>
+That means it is not guaranteed that the backend live the whole time nor it will be loaded only one time. A tactic to handle this well is to build stateless backends referring to <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a> and <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend()</a>. That means that there is no more information present than in the storage itself. Be aware that you must not have any global variables in your backend. Read more about that in <a class="el" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend()</a>. But to be stateless you also have to consider not to store any other than caching information into <a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData()</a>. I repeat: it must be possible to restore everything dynamically stored without exception.<h3><a class="anchor" name="lib">
+Library Names</a></h3>
+Elektra source code or development package provides a skeleton and Makefile to implement a backend. Copy src/backends/template to have a good starting point. See the CODING document to know how to integrate the backend in the build system or how to compile it external.<p>
+A backend is defined by a single name, for example <code>BACKENDNAME</code>, that causes libelektra.so look for its library as <code>libelektra-BACKENDNAME.so</code>.<p>
+<dl class="user" compact><dt><b>Example of a complete backend:</b></dt><dd><div class="fragment"><pre class="fragment"><span class="comment">//</span>
+<span class="comment">// This is my implementation for an Elektra backend storage.</span>
+<span class="comment">//</span>
+<span class="comment">// To compile it:</span>
+<span class="comment">// $ cc -fpic `pkg-config --cflags elektra` -o myback.o -c myback.c</span>
+<span class="comment">// $ cc -shared -fpic `pkg-config --libs elektra` -o libelektra-myback.so myback.o</span>
+<span class="comment">//</span>
+<span class="comment">// To use it:</span>
+<span class="comment">// $ preload mount myback system/myback myback /tmp/nofile</span>
+<span class="comment">// $ kdb ls system/myback</span>
+<span class="comment">// $ kdb set system/myback/key "value"</span>
+<span class="comment">// $ kdb get system/myback/key</span>
+<span class="comment">//</span>
+
+<span class="preprocessor">#include &lt;kdbbackend.h&gt;</span>
+
+<span class="preprocessor">#define BACKENDNAME "backend"</span>
+<span class="preprocessor"></span>
+
+<span class="keywordtype">int</span> <a class="code" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend</a>(KDB *handle) {...}
+<span class="keywordtype">int</span> <a class="code" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend</a>(KDB *handle) {...}
+<span class="keywordtype">int</span> <a class="code" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend</a>(KDB handle, KeySet *returned, Key *key) {...}
+<span class="keywordtype">int</span> <a class="code" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend</a>(KDB handle, KeySet *returned, Key *key) {...}
+
+<a class="code" href="group__backend.html#g40bb01174ff716b57525c4dd7730aac8">KDBEXPORT</a>(backend) {
+        <span class="keywordflow">return</span> <a class="code" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport</a>(BACKENDNAME,
+                <a class="code" href="group__backend.html#gge857eadce7085ecd3a24671a1826940a4293cef5efac0aa0ad61c18e41c0848c">KDB_BE_OPEN</a>,  &amp;<a class="code" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend</a>,
+                <a class="code" href="group__backend.html#gge857eadce7085ecd3a24671a1826940a28f303117ceb95838f92780007407955">KDB_BE_CLOSE</a>, &amp;<a class="code" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend</a>,
+                <a class="code" href="group__backend.html#gge857eadce7085ecd3a24671a1826940ae1e894d0fe6e3f05f444f03de2bc7885">KDB_BE_GET</a>,   &amp;<a class="code" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend</a>,
+                <a class="code" href="group__backend.html#gge857eadce7085ecd3a24671a1826940ab2fe708111d49ea21669db6bd7276007">KDB_BE_SET</a>,   &amp;<a class="code" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend</a>,
+                <a class="code" href="group__backend.html#gge857eadce7085ecd3a24671a1826940adab7f08d13e598d0fafa2db1aa707b2f">KDB_BE_END</a>);
+}
+</pre></div></dd></dl>
+In the example, the *_backend() methods can have other random names, since you'll correctly pass them later to <a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a>. It is recommended to use names according to your backendname to avoid name clashes. Be aware that every symbol name in the linked application must be unique.<p>
+Don't copy above example out, use src/backends/template, it does compile as-is and does some initialization and cleanup already.<p>
+Elektra source code tree includes several backend implementations <a href="https://svn.libelektra.org/svn/elektra/trunk/src/backends/">https://svn.libelektra.org/svn/elektra/trunk/src/backends/</a> that can also be used as a reference.<h2><a class="anchor" name="backenddetail">
+Details</a></h2>
+<h3><a class="anchor" name="intro">
+Introduction</a></h3>
+Capabilities may make your live much easier. If it is impossible, very hard or would impact performance badly you may leave out some parts described here, but need to declare that you have done so with capabilites.<p>
+It is allowed to provide additional information, even if you declared you don't have it. If you declare that you are capable of doing something, you must provide it without exceptions.<h3><a class="anchor" name="owner">
+Owner</a></h3>
+You need to set the owner of keys by <a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner()</a>. Owner is the name to whom a specific key of the user/ hierarchy belongs. If you declare kdbcGetnoOwner() you need not to set the owner of the keys. It also means that even if you want to get keys from another user hierarchy you get yours.<h3><a class="anchor" name="value">
+Values</a></h3>
+Values are the central information of keys next to the name describing what informations it holds. Parse them out of your backend and put them into the key with <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a>. The information will be duplicated, so you might need to free() your string. Don't try to directly access key-&gt;data, things may change there and your backend might be compiled with a different libc than elektra. If you support types, you might want to use keySetRaw() to not change the key type. If you don't support values for all keys declare kdbcGetnoValue().<h3><a class="anchor" name="id">
+IDs</a></h3>
+You need to set uid respective gid for any key not having the uid and gid of the current process. This will be set by default in every key. You can do it with <a class="el" href="group__keymeta.html#gb5f284f5ecd261e0a290095f50ba1af7">keySetUID()</a> and <a class="el" href="group__keymeta.html#g9e3d0fb3f7ba906e067727b9155d22e3">keySetGID()</a>. Declaring kdbcGetnoUID() and kdbcGetnoGID() you need not set uid and gid.<h3><a class="anchor" name="mode">
+Mode</a></h3>
+Mode shows what can be done with the key having or not having the above uid and gid. Use <a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a> to set the correct mode description, read the description in <a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a> for the semantics of the 3 octal representation. Declaring kdbcGetnoMode() means mode will remain default.<p>
+The very related method <a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a> sets the executable bits of mode. Even if your backend does not support mode, it might support directories, meaning that keys have the mode 0664 or 0775 for directories. Declaring kdbcGetnoDir() means that the backend is flat, no key will be true for <a class="el" href="group__keytest.html#gc0a10c602d52a35f81347e8a32312017">keyIsDir()</a> and so can't have any subkeys.<h3><a class="anchor" name="timing">
+Timing</a></h3>
+Keys should have exact timing information of their modification and access times. Use <a class="el" href="group__keymeta.html#g995d8b84731673c88c7c01f3fed538b9">keySetATime()</a>, <a class="el" href="group__keymeta.html#g481d8997187992fe4bbf288bc8ef4db7">keySetMTime()</a> and <a class="el" href="group__keymeta.html#g9f502ecab8ab43f0b17220fcc95f3fa5">keySetCTime()</a> to store appropriate information. ATime need to be stored in database, if you stat a key the backend need to return the time <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> was last used for the keys. If you don't support this, declare kdbcGetnoATime() and simple store time(0) in the atime. This must be the same for every key for a single <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a>. If you only stat keys with <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>, see below, then the access time should not be updated. MTime is the last modification time of value or comment. If you don't support this, declare kdbcGetnoMTime() and simple store time(0) in the mtime. This must be the same for every key for a single <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a>. CTime is the last change time of any metadata or add/remove of subkeys. If you don't support this, declare kdbcGetnoCTime() and simple store time(0) in the ctime. This must be the same for every key for a single <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a>.<h3><a class="anchor" name="type">
+Types</a></h3>
+Keys having value and comment can be one of two fundamental types, string or binary, both called value. While string is a null terminated utf8 character sequence, binary is any data of a specific length. Be sure to use <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> for string and <a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary()</a> if you want to store binary data. If you do not support one of these, be sure to declare kdbcGetnoBinary() or kdbcGetnoString(), if you don't support both make sure to also declare kdbcGetnoValue().<p>
+Using keySetRaw() does not set the type, be sure to use <a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">keySetType()</a> afterwards. This can be KEY_TYPE_STRING and KEY_TYPE_BINARY or any other type in type_t, leading to same results as explained above, but also any other number in the range of type_t. Declare kdbcGetnoTypes() when your backend does not support arbitrary types. <hr><h2>Enumeration Type Documentation</h2>
+<a class="anchor" name="ge857eadce7085ecd3a24671a1826940a"></a><!-- doxytag: member="kdbbackend.h::backend_t" ref="ge857eadce7085ecd3a24671a1826940a" args="" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">enum <a class="el" href="group__backend.html#ge857eadce7085ecd3a24671a1826940a">backend_t</a>          </td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Switches to denote the backend methods. Used in calls to <a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a>. <dl compact><dt><b>Enumerator: </b></dt><dd>
+<table border="0" cellspacing="2" cellpadding="0">
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940a4293cef5efac0aa0ad61c18e41c0848c"></a><!-- doxytag: member="KDB_BE_OPEN" ref="gge857eadce7085ecd3a24671a1826940a4293cef5efac0aa0ad61c18e41c0848c" args="" -->KDB_BE_OPEN</em>&nbsp;</td><td>
+Next arg is backend for <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a> </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940a28f303117ceb95838f92780007407955"></a><!-- doxytag: member="KDB_BE_CLOSE" ref="gge857eadce7085ecd3a24671a1826940a28f303117ceb95838f92780007407955" args="" -->KDB_BE_CLOSE</em>&nbsp;</td><td>
+Next arg is backend for <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a> </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940ae1e894d0fe6e3f05f444f03de2bc7885"></a><!-- doxytag: member="KDB_BE_GET" ref="gge857eadce7085ecd3a24671a1826940ae1e894d0fe6e3f05f444f03de2bc7885" args="" -->KDB_BE_GET</em>&nbsp;</td><td>
+Next arg is backend for <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940ab2fe708111d49ea21669db6bd7276007"></a><!-- doxytag: member="KDB_BE_SET" ref="gge857eadce7085ecd3a24671a1826940ab2fe708111d49ea21669db6bd7276007" args="" -->KDB_BE_SET</em>&nbsp;</td><td>
+Next arg is backend for <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940aeca3339f3b62cd2136646cb59678603e"></a><!-- doxytag: member="KDB_BE_VERSION" ref="gge857eadce7085ecd3a24671a1826940aeca3339f3b62cd2136646cb59678603e" args="" -->KDB_BE_VERSION</em>&nbsp;</td><td>
+Next arg is char * for Version </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940ac60eb75a1391a6a4d30420f4079e4244"></a><!-- doxytag: member="KDB_BE_DESCRIPTION" ref="gge857eadce7085ecd3a24671a1826940ac60eb75a1391a6a4d30420f4079e4244" args="" -->KDB_BE_DESCRIPTION</em>&nbsp;</td><td>
+Next arg is char * for Description </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940adc638374d367d0a52d9201f782eb9f5c"></a><!-- doxytag: member="KDB_BE_AUTHOR" ref="gge857eadce7085ecd3a24671a1826940adc638374d367d0a52d9201f782eb9f5c" args="" -->KDB_BE_AUTHOR</em>&nbsp;</td><td>
+Next arg is char * for Author </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940ad3348e5ab9428da29ba7b51886b87bbe"></a><!-- doxytag: member="KDB_BE_LICENCE" ref="gge857eadce7085ecd3a24671a1826940ad3348e5ab9428da29ba7b51886b87bbe" args="" -->KDB_BE_LICENCE</em>&nbsp;</td><td>
+Next arg is char * for Licence </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gge857eadce7085ecd3a24671a1826940adab7f08d13e598d0fafa2db1aa707b2f"></a><!-- doxytag: member="KDB_BE_END" ref="gge857eadce7085ecd3a24671a1826940adab7f08d13e598d0fafa2db1aa707b2f" args="" -->KDB_BE_END</em>&nbsp;</td><td>
+End of arguments </td></tr>
+</table>
+</dl>
+
+</div>
+</div><p>
+<hr><h2>Function Documentation</h2>
+<a class="anchor" name="g2b02d413c4bb3c11e0f2560937b82db3"></a><!-- doxytag: member="kdb.c::kdbBackendExport" ref="g2b02d413c4bb3c11e0f2560937b82db3" args="(const char *backendName,...)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KDB* kdbBackendExport           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>backendName</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">&nbsp;</td>
+          <td class="paramname"> <em>...</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+This function must be called by a backend's kdbBackendFactory() to define the backend's methods that will be exported.<p>
+See <a class="el" href="group__backend.html#g40bb01174ff716b57525c4dd7730aac8">KDBEXPORT()</a> how to use it for backends.<p>
+The order and number of arguments are flexible (as in <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> and <a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a>) to let libelektra.so evolve without breaking its ABI compatibility with backends. So for each method a backend must export, there is a flag defined by <a class="el" href="group__backend.html#ge857eadce7085ecd3a24671a1826940a">backend_t</a>. Each flag tells <a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a> which method comes next. A backend can have no implementation for a few methods that have default inefficient high-level implementations and to use these defaults, simply don't pass anything to <a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a> about them.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>backendName</em>&nbsp;</td><td>a simple name for this backend </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>an object that contains all backend informations needed by libelektra.so </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6dd468490ad54c3a52f7e5083b8c721b"></a><!-- doxytag: member="backend.c::kdbClose_backend" ref="g6dd468490ad54c3a52f7e5083b8c721b" args="(KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbClose_backend           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Finalize the backend. Called prior to unloading the backend dynamic module. Should ensure that no functions or static/global variables from the module will ever be accessed again.<p>
+Make sure to free all memory that your backend requested at runtime.<p>
+Specifically make sure to capDel() all capabilites and free your backendData in <a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData()</a>.<p>
+After this call, libelektra.so will unload the backend library, so this is the point to shutdown any affairs with the storage.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success, anything else otherwise. </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g40bb01174ff716b57525c4dd7730aac8"></a><!-- doxytag: member="backend.c::KDBEXPORT" ref="g40bb01174ff716b57525c4dd7730aac8" args="(backend)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KDBEXPORT           </td>
+          <td>(</td>
+          <td class="paramtype">backend&nbsp;</td>
+          <td class="paramname">          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+All KDB methods implemented by the backend can have random names, except kdbBackendFactory(). This is the single symbol that will be looked up when loading the backend, and the first method of the backend implementation that will be called.<p>
+Its purpose is to publish the exported methods for libelektra.so. The implementation inside the provided skeleton is usually enough: simply call <a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a> with all methods that must be exported.<p>
+The first paramter is the name of the backend. Then every backend must have: <code>KDB_BE_OPEN</code>, <code>KDB_BE_CLOSE</code>, <code>KDB_BE_GET</code> and <code>KDB_BE_SET</code> <p>
+You might also give following information by char *: <code>KDB_BE_VERSION</code>, <code>KDB_BE_AUTHOR</code>, <code>KDB_BE_LICENCE</code> and <code>KDB_BE_DESCRIPTION</code> <p>
+You must use static "char arrays" in a read only segment. Don't allocate storage, it won't be freed.<p>
+With capability you can get that information on runtime from any backend with kdbGetCapability().<p>
+The last parameter must be <code>KDB_BE_END</code>.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd><a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a> with the above described parameters. </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backend.html#g2b02d413c4bb3c11e0f2560937b82db3">kdbBackendExport()</a> for an example <p>
+kdbOpenBackend() </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gf3c9c38b7710c435e3caed9e8df2184d"></a><!-- doxytag: member="backend.c::kdbGet_backend" ref="gf3c9c38b7710c435e3caed9e8df2184d" args="(KDB *handle, KeySet *returned, const Key *parentKey)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbGet_backend           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>parentKey</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Retrieve information from a permanent storage to construct a keyset.<h2><a class="anchor" name="intro">
+Introduction</a></h2>
+This function does everything related to get keys out from a backend. There is only one function for that purpose to make implementation and locking much easier.<p>
+The keyset <code>returned</code> needs to be filled with information so that the application using elektra can access it. See the live cycle of a comment to understand: <div class="fragment"><pre class="fragment"><a class="code" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend</a>(KDB *handle, KeySet *returned, Key *parentKey)
+{
+        <span class="comment">// the task of kdbGet_backend is to retrieve the comment out of the permanent storage</span>
+        Key *key = <a class="code" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup</a> (parentKey); <span class="comment">// generate a new key to hold the information</span>
+        <span class="keywordtype">char</span> *comment;
+        loadfromdisc (comment);
+        <a class="code" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment</a> (key, comment, size); <span class="comment">// set the information</span>
+        <a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(returned, key);
+}
+
+<span class="comment">// Now return to kdbGet</span>
+<span class="keywordtype">int</span> <a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+        <a class="code" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend</a> (handle, keyset, 0);
+        <span class="comment">// postprocess the keyset and return it</span>
+}
+
+<span class="comment">// Now return to usercode, waiting for the comment</span>
+<span class="keywordtype">void</span> usercode (Key *key)
+{
+        <a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a> (handle, keyset, parentKey, 0);
+        key = <a class="code" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent</a> (keyset, key); <span class="comment">// lookup the key from the keyset</span>
+        <a class="code" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment</a> (key); <span class="comment">// now the usercode retrieves the comment</span>
+}
+</pre></div> Of course not only the comment, but all information of every key in the keyset <code>returned</code> need to be fetched from permanent storage and stored in the key. So this specification needs to give an exhaustive list of information present in a key.<h2><a class="anchor" name="conditions">
+Conditions</a></h2>
+<dl class="pre" compact><dt><b>Precondition:</b></dt><dd>The caller <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will make sure before you are called that the parentKey:<ul>
+<li>is a valid key (means that it is a system or user key).</li><li>is below (see <a class="el" href="group__keytest.html#g03332b5d97c76a4fd2640aca4762b8df">keyIsBelow()</a>) your mountpoint and that your backend is responsible for it.</li><li>has <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> set when you should only stat keys (see later). and that the returned:</li><li>is a valid keyset.</li><li>has <code>all</code> keys with the flag KEY_FLAG_SYNC set.</li><li><code>may</code> has keys with the flag KEY_FLAG_STAT set.</li><li>contains only valid keys direct below (see <a class="el" href="group__keytest.html#g4f175aafd98948ce6c774f3bd92b72ca">keyIsDirectBelow()</a>) your parentKey. That also means, that the parentKey will not be in that keyset.</li><li>have keyIsStat() set when the value/comment information is not necessary.</li><li>is in a sorted order, see <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>. and that the handle:<ul>
+<li>is a valid KDB for your backend.</li><li>that kdbhGetBackendHandle() contains the same handle for lifetime <a class="el" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend()</a> until <a class="el" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend()</a> was called.</li></ul>
+</li></ul>
+<p>
+The caller <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will make sure that afterwards you were called, whenever the user requested it with the options, that:<ul>
+<li>hidden keys they will be thrown away.</li><li>dirs or only dirs <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will remove the other.</li><li>you will be called again recursively with all subdirectories.</li><li>the keyset will be sorted when needed.</li><li>the keys in returned having KEY_FLAG_SYNC will be sorted out.</li></ul>
+</dd></dl>
+<dl class="invariant" compact><dt><b>Invariant:</b></dt><dd>There are no global variables and <a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData()</a> only stores information which can be regenerated any time. The handle is the same when it is the same backend.</dd></dl>
+<dl class="post" compact><dt><b>Postcondition:</b></dt><dd>The keyset <code>returned</code> has the <code>parentKey</code> and all keys direct below (<a class="el" href="group__keytest.html#g4f175aafd98948ce6c774f3bd92b72ca">keyIsDirectBelow()</a>) with all information from the storage. Make sure to return all keys, all directories and also all hidden keys. If some of them are not wished, the caller <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will drop these keys, see above.</dd></dl>
+<h2><a class="anchor" name="detail">
+Details</a></h2>
+Now lets look at an example how the typical <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a> might be implemented. To explain we introduce some pseudo functions which do all the work with the storage (which is of course 90% of the work for a real backend):<ul>
+<li>find_key() gets an key out from the storage and memorize the position.</li><li>next_key() will find the next key and return it (with the name).</li><li>fetch_key() gets out all information of a key from storage (details see below example). It removes the <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> and <a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a> flag afterwards.</li><li>stat_key() gets all meta information (everything but value and comment). It removes the key <a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a> flag afterwards. returns the next key out from the storage. The typical loop now will be like: <div class="fragment"><pre class="fragment">ssize_t <a class="code" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend</a>(KDB *handle, KeySet *update, <span class="keyword">const</span> Key *parentKey) {
+        Key * current;
+        KeySet *returned = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(<a class="code" href="group__keyset.html#g7474ad6b0a0fa969dbdf267ba5770eee">ksGetSize</a>(update)*2, KS_END);
+
+        find_key (parentKey);
+        current = <a class="code" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup</a> (parentKey);
+        <span class="keywordflow">if</span> (<a class="code" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat</a>(parentKey))
+        {
+                current = stat_key(current);
+        } <span class="keywordflow">else</span> {
+                current = fetch_key(current);
+        }
+        clear_bit (KEY_FLAG_SYNC, current-&gt;flags);
+        <a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(returned, current);
+
+        <span class="keywordflow">while</span> ((current = next_key()) != 0)
+        {
+                <span class="comment">// search if key was passed in update by caller</span>
+                Key * tmp = <a class="code" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup</a> (update, current, KDB_O_WITHOWNER|KDB_O_POP);
+                <span class="keywordflow">if</span> (tmp) current = tmp; <span class="comment">// key was passed, so use it</span>
+                <span class="keywordflow">if</span> (<a class="code" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat</a>(parentKey) || <a class="code" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat</a>(current))
+                {
+                        current = stat_key (current);
+                        set_bit (KEY_FLAG_STAT, current-&gt;flags);
+                } <span class="keywordflow">else</span> {
+                        current = fetch_key(current);
+                }
+                clear_bit (KEY_FLAG_SYNC, current-&gt;flags);
+                <a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(returned, current);
+                <span class="comment">// TODO: delete lookup key</span>
+        }
+
+        <span class="keywordflow">if</span> (error_happened())
+        {
+                errno = restore_errno();
+                <span class="keywordflow">return</span> -1;
+        }
+
+        ksClear (update); <span class="comment">// the rest of update keys is not in storage anymore</span>
+        <a class="code" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend</a>(update, returned); <span class="comment">// append the keys</span>
+        <a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (returned);
+
+        <span class="keywordflow">return</span> nr_keys();
+}
+</pre></div></li></ul>
+<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>- returned and update are separated, for details why see <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a><ul>
+<li>the bit KEY_FLAG_SYNC is always cleared, see postconditions</li></ul>
+</dd></dl>
+So your mission is simple: Search the <code>parentKey</code> and add it and then search all keys below and add them too, of course with all requested information (which is only depended on <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a>).<h2><a class="anchor" name="stat">
+Stat</a></h2>
+Sometimes value and comment are not of interest, but metadata. To avoid a potential time-consuming <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> you can <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> the <code>parentKey</code>. If the backend supports a less time-consuming method to just get names and metadata, implement it, otherwise declare kdbcGetnoStat().<p>
+The implementation works as follows: When the <code>parentKey</code> has <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> set, all keys need to be stated instead of getting them. So the keys you <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a> don't have a value nor a comment and make sure that KEY_FLAG_SYNC is not set, but <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> must be set for all keys which are only stated.<p>
+The keys in <code>returned</code> may already have <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> set. These keys must keep the status <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> and you don't need to get the value and comment. See the example above for code.<h2><a class="anchor" name="updating">
+Updating</a></h2>
+To get all keys out of the storage over and over again can be very inefficient. You might know a more efficient method to know if the key needs update or not, e.g. by stating it or by an external time stamp info. In that case you can make use of <code>returned</code> KeySet. There are following possibilities:<ul>
+<li>The key is in returned and up to date. You just need to remove the KEY_FLAG_SYNC flag.</li><li>The key is in returned but <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> is true. You just need to stat the key and remove the KEY_FLAG_SYNC flag and set the KEY_FLAG_STAT flag.</li><li>The key is in returned, <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> is false (for the key and the <code>parentKey</code>) and you know that the key has changed. You need to fully retrieve the key and remove the KEY_FLAG_SYNC flag.</li><li>The key is not in returned, the <code>parentKey</code> has <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a>. You just need to stat the key. Make sure that KEY_FLAG_SYNC is not set, but KEY_FLAG_STAT needs to be set. Append the key to <code>returned</code>.</li><li>The key is not in returned and the <code>parentKey</code> <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> is false. You need to fully retrieve the key out of storage, clear KEY_FLAG_STAT and KEY_FLAG_SYNC and <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a> it to the <code>returned</code> keyset.</li></ul>
+<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>You must clear the flag KEY_FLAG_SYNC at the very last point where no more modification on the key will take place, because any modification on the key will set the KEY_FLAG_SYNC flag again. With that <a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a> will return true and the caller will sort this key out.</dd></dl>
+<h2><a class="anchor" name="fullget">
+only Full Get</a></h2>
+In some backends it is not useful to get only a part of the configuration, because getting all keys would take as long as getting some. For this situation, you can declare onlyFullGet, see kdbcGetonlyFullGet().<p>
+The only valid call for your backend is then that <code>parentKey</code> equals the <code>mountpoint</code>. For all other <code>parentKey</code> you must, add nothing and just return 0.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordflow">if</span> (strcmp (<a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(<a class="code" href="group__backendhandle.html#g8b5612940fc9bc56e99c15ecc427cbb2">kdbhGetMountpoint</a>(handle)), <a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(parentKey))) <span class="keywordflow">return</span> 0;
+</pre></div><p>
+If the <code>parentKey</code> is your mountpoint you will of course fetch all keys, and not only the keys direct below the <code>parentKey</code>. So <code>returned</code> is valid iff:<ul>
+<li>every key is below ( <a class="el" href="group__keytest.html#g03332b5d97c76a4fd2640aca4762b8df">keyIsBelow()</a>) the parentKey</li><li>every key has a direct parent (<a class="el" href="group__keytest.html#g4f175aafd98948ce6c774f3bd92b72ca">keyIsDirectBelow()</a>) in the keyset</li></ul>
+<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>This statement is only valid for backends with kdbcGetonlyFullGet() set.<p>
+If any calls you use change errno, make sure to restore the old errno.</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> for caller.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>contains a keyset where the function need to append the keys got from the storage. There might be also some keys inside it, see conditions. You may use them to support efficient updating of keys, see <a class="el" href="group__backend.html#updating">Updating</a>. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parentKey</em>&nbsp;</td><td>contains the information below which key the keys should be gotten.</td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>Return how many keys you added.<p>
+-1 on failure, the current key in returned shows the position. In normal execution cases a positive value will be returned. But in some cases you are not able to get keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added in 0.7.1) </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g8ce84f1f6defc40a9239058991e257da"></a><!-- doxytag: member="backend.c::kdbOpen_backend" ref="g8ce84f1f6defc40a9239058991e257da" args="(KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbOpen_backend           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Initialize the backend. This is the first method kdbOpenBackend() calls after dynamically loading the backend library.<p>
+This method is responsible of:<ul>
+<li>backend's specific configuration gathering</li><li>all backend's internal structs initialization</li><li>initial setup of all I/O details such as opening a file, connecting to a database, etc</li></ul>
+<p>
+If your backend does not support all aspects described in <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a> and <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend()</a> you need capabilities to export this information. Per default you declare to be fully compliant to the specification given here, to change it get a pointer to KDBCap structure by using <a class="el" href="group__backendhandle.html#g090cfa7483afbb159b75c975eb1d513c">kdbhGetCapability()</a>.<p>
+You may also read the configuration you can get with <a class="el" href="group__backendhandle.html#gb14dc8708c2ae4ffcba6cfb130019115">kdbhGetConfig()</a> and transform it into other structures used by your backend.<p>
+But be aware that you don't have any global variables. If you do your backend will not be threadsafe. You can use <a class="el" href="group__backendhandle.html#g97fab712e488c7ec3e198492106724ab">kdbhSetBackendData()</a> and <a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData()</a> to store and get any information related to your backend.<p>
+The correct substitute for global variables will be: <div class="fragment"><pre class="fragment"><span class="keyword">struct </span>_GlobalData{ <span class="keywordtype">int</span> global; };
+<span class="keyword">typedef</span> <span class="keyword">struct </span>_GlobalData GlobalData;
+<span class="keywordtype">int</span> <a class="code" href="group__backend.html#g8ce84f1f6defc40a9239058991e257da">kdbOpen_backend</a>(KDB *handle) {
+        PasswdData *data;
+        data=malloc(<span class="keyword">sizeof</span>(PasswdData));
+        data.global = 20;
+        <a class="code" href="group__backendhandle.html#g97fab712e488c7ec3e198492106724ab">kdbhSetBackendData</a>(handle,data);
+}
+</pre></div><p>
+Make sure to free everything in <a class="el" href="group__backend.html#g6dd468490ad54c3a52f7e5083b8c721b">kdbClose_backend()</a>.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g2d86ff43b693d59d4b82b597756e9e23"></a><!-- doxytag: member="backend.c::kdbSet_backend" ref="g2d86ff43b693d59d4b82b597756e9e23" args="(KDB *handle, KeySet *returned, const Key *parentKey)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbSet_backend           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>parentKey</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Store a keyset permanently.<p>
+This function does everything related to set and remove keys in a backend. There is only one function for that purpose to make implementation and locking much easier.<p>
+The keyset <code>returned</code> was filled in with information from the application using elektra and the task of this function is to store it in a permanent way so that a subsequent call of <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a> can rebuild the keyset as it was before. See the live cycle of a comment to understand: <div class="fragment"><pre class="fragment"><span class="keywordtype">void</span> usercode (Key *key)
+{
+        <a class="code" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment</a> (key, <span class="stringliteral">"mycomment"</span>); <span class="comment">// the usercode stores a comment for the key</span>
+        <a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(keyset, key); <span class="comment">// append the key to the keyset</span>
+        <a class="code" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet</a> (handle, keyset, 0, 0);
+}
+
+<span class="comment">// so now kdbSet is called</span>
+<span class="keywordtype">int</span> <a class="code" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet</a>(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+        <span class="comment">// find appropriate backend</span>
+        <a class="code" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend</a> (handle, keyset, 0); <span class="comment">// the keyset with the key will be passed to this function</span>
+}
+
+<span class="comment">// so now kdbSet_backend(), which is the function described here, is called</span>
+<a class="code" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend</a>(KDB *handle, KeySet *keyset, Key *parentKey)
+{
+        <span class="comment">// the task of kdbSet_backend is now to store the comment</span>
+        Key *key = <a class="code" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent</a> (keyset); <span class="comment">// get out the key where the user set the comment before</span>
+        <span class="keywordtype">char</span> *comment = allocate(size);
+        <a class="code" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment</a> (key, comment, size);
+        savetodisc (comment);
+}
+</pre></div> Of course not only the comment, but all information of every key in the keyset <code>returned</code> need to be stored permanetly. So this specification needs to give an exhaustive list of information present in a key.<p>
+<dl class="pre" compact><dt><b>Precondition:</b></dt><dd>The keyset <code>returned</code> holds all keys which must be saved permanently for this keyset. The keyset is sorted and rewinded. All keys having children must be true for <a class="el" href="group__keytest.html#gc0a10c602d52a35f81347e8a32312017">keyIsDir()</a>.<p>
+The <code>parentKey</code> is the key which is the ancestor for all other keys in the keyset. The first key of the keyset <code>returned</code> has the same keyname. The parentKey is below the mountpoint, see <a class="el" href="group__backendhandle.html#g8b5612940fc9bc56e99c15ecc427cbb2">kdbhGetMountpoint()</a>.<p>
+The caller kdbSet will fulfill following parts:<ul>
+<li>If the user does not want hidden keys they will be thrown away. All keys in <code>returned</code> need to be stored permanently.</li><li>If the user does not want dirs or only dirs <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will remove the other.</li><li>Sorting of the keyset. It is not important in which order the keys are appended. So make sure to set all keys, all directories and also all hidden keys. If some of them are not wished, the caller <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> will sort them out.</li></ul>
+</dd></dl>
+<dl class="invariant" compact><dt><b>Invariant:</b></dt><dd>There are no global variables and <a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData()</a> only stores information which can be regenerated any time. The handle is the same when it is the same backend.</dd></dl>
+<dl class="post" compact><dt><b>Postcondition:</b></dt><dd>The information of the keyset <code>returned</code> is stored permanently.</dd></dl>
+When some keys have KEY_FLAG_REMOVE set, that means return true for <a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove()</a>, remove the keys instead of getting them. In this case the sorting order will be the reverse way, first will be the children, then the parentKey when iterating over the KeySet returned.<p>
+Lock your permanent storage in an exclusive way, no access of a concurrent <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend()</a> or <a class="el" href="group__backend.html#gf3c9c38b7710c435e3caed9e8df2184d">kdbGet_backend()</a> is possible and these methods block until the function has finished. Otherwise declare kdbcGetnoLock().<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> for caller.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>contains a keyset with relevant keys </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parentKey</em>&nbsp;</td><td>contains the information where to set the keys</td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>When everything works gracefully return the number of keys you set. The cursor position and the keys remaining in the keyset are not important.<p>
+Return 0 on success with no changed key in database<p>
+Return -1 on failure.</dd></dl>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>If any calls you use change errno, make sure to restore the old errno.</dd></dl>
+<dl compact><dt><b><a class="el" href="err.html#_err000004">Error:</a></b></dt><dd>In normal execution cases a positive value will be returned. But in some cases you are not able to set keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added with 0.7.1)</dd></dl>
+You also have to make sure that <a class="el" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor()</a> shows to the position where the error appeared. 
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__backendhandle.html b/doc/elektra-api/html/group__backendhandle.html
new file mode 100644 (file)
index 0000000..20788d2
--- /dev/null
@@ -0,0 +1,388 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KDB Backends :: KDB access functions</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KDB Backends :: KDB access functions</h1>Methods to access the backend handle.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#g97fab712e488c7ec3e198492106724ab">kdbhSetBackendData</a> (KDB *handle, void *data)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData</a> (const KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KDBCap *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#gd077fae396d9be2b9d5e01b6f9f60318">kdbhSetCapability</a> (KDB *handle, KDBCap *cap)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KDBCap *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#g090cfa7483afbb159b75c975eb1d513c">kdbhGetCapability</a> (const KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Trie *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#g591d15bf10d8f4366ec7e1e9b8c6ddc7">kdbhGetTrie</a> (const KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#g1c5c086715963f34f2a88fac89b62f82">kdbhSetTrie</a> (KDB *handle, Trie *trie)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">const Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#g8b5612940fc9bc56e99c15ecc427cbb2">kdbhGetMountpoint</a> (KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#g65c8878c24776c77716ac9e8abe1a29f">kdbhSetMountpoint</a> (KDB *handle, const Key *mountpoint)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KeySet *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhandle.html#gb14dc8708c2ae4ffcba6cfb130019115">kdbhGetConfig</a> (KDB *handle)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to access the backend handle. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor"> #include &lt;kdb.h&gt;</span>
+</pre></div><p>
+These functions provide access to the information stored in Backend Handles. <hr><h2>Function Documentation</h2>
+<a class="anchor" name="ge463be8651422015fd12811ed66d20f3"></a><!-- doxytag: member="kdbhandle.c::kdbhGetBackendData" ref="ge463be8651422015fd12811ed66d20f3" args="(const KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">void* kdbhGetBackendData           </td>
+          <td>(</td>
+          <td class="paramtype">const KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the previously set backend-specific <code>data</code> from the <code>handle</code>.<p>
+This is useful when your backend have a backend-global context or environment.<p>
+This method will probably be called everytime one of your kdb*() implementations is called. And if you change something inside the data, you don't have to <a class="el" href="group__backendhandle.html#g97fab712e488c7ec3e198492106724ab">kdbhSetBackendData()</a> again, bacause you are manipulating your data, and not a copy of it.<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment"><span class="keyword">struct </span>MyBackendData {
+ <span class="keywordtype">int</span> context1;
+ <span class="keywordtype">int</span> context2;
+};
+
+<span class="keywordtype">int</span> kdbOpen_mybackend(KDB *handle) {
+        <span class="keyword">struct </span>MyBackendData *context;
+
+        context=malloc(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> MyBackendData));
+        <span class="comment">// a random initialization...</span>
+        context-&gt;context1=1;
+        context-&gt;context2=2;
+
+        <a class="code" href="group__backendhandle.html#g97fab712e488c7ec3e198492106724ab">kdbhSetBackendData</a>(*handle,context);
+
+        <span class="keywordflow">return</span> 0;
+}
+
+<span class="keywordtype">int</span> kdbGetKey_maybackend(KDB handle) {
+        <span class="keyword">struct </span>MyBackendData *context;
+
+        context=<a class="code" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData</a>(handle);
+
+        <span class="comment">// No do something with the context</span>
+        . . .
+
+        <span class="keywordflow">return</span> 0;
+}
+</pre></div></dd></dl>
+On the <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a> implementation of your backend, you must remember to free all resources associated to your data.<p>
+<dl class="user" compact><dt><b>Example of kdbClose() implementation that correctly cleans the context:</b></dt><dd><div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> kdbClose_mybackend(KDB &amp;handle) {
+        <span class="keyword">struct </span>MyBackendData *context;
+
+        context=<a class="code" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData</a>(handle);
+        free(context);
+
+        <span class="keywordflow">return</span> 0;
+}
+</pre></div> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to the data previously set be <a class="el" href="group__backendhandle.html#g97fab712e488c7ec3e198492106724ab">kdbhSetBackendData()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g090cfa7483afbb159b75c975eb1d513c"></a><!-- doxytag: member="kdbhandle.c::kdbhGetCapability" ref="g090cfa7483afbb159b75c975eb1d513c" args="(const KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KDBCap* kdbhGetCapability           </td>
+          <td>(</td>
+          <td class="paramtype">const KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Gets capability for handle.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>The backend name set in <code>handle</code>. </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb14dc8708c2ae4ffcba6cfb130019115"></a><!-- doxytag: member="kdbhandle.c::kdbhGetConfig" ref="gb14dc8708c2ae4ffcba6cfb130019115" args="(KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KeySet* kdbhGetConfig           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Returns configuration for handle.<p>
+Every backend may have its own configuration using a Keyset.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the keyset containing configuration for a backend </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g8b5612940fc9bc56e99c15ecc427cbb2"></a><!-- doxytag: member="kdbhandle.c::kdbhGetMountpoint" ref="g8b5612940fc9bc56e99c15ecc427cbb2" args="(KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">const Key* kdbhGetMountpoint           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Gets mountpoint for handle.<p>
+Every mounted backend has a specific mountpoint where it is mounted. You may need to know where you were mounted inside a backend to calculate relative pathes.<p>
+The <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> is where the backend is mounted, keyString() gives the name of which backend is mounted.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhandle.html#g65c8878c24776c77716ac9e8abe1a29f">kdbhSetMountpoint()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>The Key containing the mountpoint. </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g591d15bf10d8f4366ec7e1e9b8c6ddc7"></a><!-- doxytag: member="kdbhandle.c::kdbhGetTrie" ref="g591d15bf10d8f4366ec7e1e9b8c6ddc7" args="(const KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Trie* kdbhGetTrie           </td>
+          <td>(</td>
+          <td class="paramtype">const KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Gets trie for handle.<p>
+The trie is a datastructure containing the mounted backends.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhandle.html#g1c5c086715963f34f2a88fac89b62f82">kdbhSetTrie()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>The backend name set in <code>handle</code>. </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g97fab712e488c7ec3e198492106724ab"></a><!-- doxytag: member="kdbhandle.c::kdbhSetBackendData" ref="g97fab712e488c7ec3e198492106724ab" args="(KDB *handle, void *data)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">void* kdbhSetBackendData           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">void *&nbsp;</td>
+          <td class="paramname"> <em>data</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set some backend-specific <code>data</code> in the <code>handle</code>.<p>
+This is useful when your backend have a backend-global context or environment.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>data</em>&nbsp;</td><td>a pointer to general data specific to a backend implementation. </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhandle.html#ge463be8651422015fd12811ed66d20f3">kdbhGetBackendData()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gd077fae396d9be2b9d5e01b6f9f60318"></a><!-- doxytag: member="kdbhandle.c::kdbhSetCapability" ref="gd077fae396d9be2b9d5e01b6f9f60318" args="(KDB *handle, KDBCap *cap)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KDBCap* kdbhSetCapability           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">KDBCap *&nbsp;</td>
+          <td class="paramname"> <em>cap</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Sets capabilty for handle.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>cap</em>&nbsp;</td><td>a pointer to capability structure </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>The backend name set in <code>handle</code>. </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g65c8878c24776c77716ac9e8abe1a29f"></a><!-- doxytag: member="kdbhandle.c::kdbhSetMountpoint" ref="g65c8878c24776c77716ac9e8abe1a29f" args="(KDB *handle, const Key *mountpoint)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">void kdbhSetMountpoint           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>mountpoint</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Sets mountpoint for handle.<p>
+You must not change the mountpoint inside your backend, it was set correctly already for you.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>mountpoint</em>&nbsp;</td><td>the key containing as name where backend is mounted and as value the backendname </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhandle.html#g8b5612940fc9bc56e99c15ecc427cbb2">kdbhGetMountpoint()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>nothing </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g1c5c086715963f34f2a88fac89b62f82"></a><!-- doxytag: member="kdbhandle.c::kdbhSetTrie" ref="g1c5c086715963f34f2a88fac89b62f82" args="(KDB *handle, Trie *trie)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">void kdbhSetTrie           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Trie *&nbsp;</td>
+          <td class="paramname"> <em>trie</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Sets trie for handle.<p>
+The trie is a datastructure containing the mounted backends. This must not done inside backends, it was set correctly already for you.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>trie</em>&nbsp;</td><td>the datastructure referencing to the other handles of backends </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhandle.html#g591d15bf10d8f4366ec7e1e9b8c6ddc7">kdbhGetTrie()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>nothing </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__backendhelper.html b/doc/elektra-api/html/group__backendhelper.html
new file mode 100644 (file)
index 0000000..68c80b3
--- /dev/null
@@ -0,0 +1,645 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KDB Backends :: Backend Helper for Elektra</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KDB Backends :: Backend Helper for Elektra</h1>Backend helper Methods for Elektra and Backends.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#gcea5819334a71744a54a6b290a8c3bdc">kdbbWriteLock</a> (FILE *f)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g89aa8c310a5720766639c305e643c069">kdbbReadLock</a> (FILE *f)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#gb969d5d25762464c5f719f4f90757fe8">kdbbUnlock</a> (FILE *f)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#gba58d73495f15a309468fd4798539f22">kdbbEncode</a> (void *kdbbDecoded, size_t size, char *returned)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#gfc099de90661f22a048d5fd16fca48f5">kdbbDecode</a> (char *kdbbEncoded, void *returned)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g11b83ea1eac5adb730c3f51660cded9d">kdbbNeedsUTF8Conversion</a> ()</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g5b9a2cb642f2a626037c4c730c790c65">kdbbUTF8Engine</a> (int direction, char **string, size_t *inputOutputByteSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#gac1bda629cb9912eaaaa785b2874cad1">kdbbEncodeChar</a> (char c, char *buffer, size_t bufSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g0ae368114cb42f6f6d56bc5cc96e44cf">kdbbDecodeChar</a> (const char *from, char *into)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#ge3ac53e11feb9d7ff956a4562f1d9bde">kdbbFilenameToKeyName</a> (const char *string, char *buffer, int bufSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g10364b65d79e44f99f9464e50dffa900">kdbbGetFullKeyName</a> (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g93921f5f2169e9d6ba1603807ee5bc2d">kdbbKeyNameToRelativeFilename</a> (const char *string, char *buffer, size_t bufSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g1d09dab69a7d6cee9a1eb2c1c650051e">kdbbKeyCalcRelativeFilename</a> (const Key *key, char *relativeFilename, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__backendhelper.html#g6db2e7738f905ea0ab52b5c9c3981af6">kdbbGetFullFilename</a> (KDB *handle, const Key *forKey, char *returned, size_t maxSize)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Backend helper Methods for Elektra and Backends. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor"> #include &lt;kdbbackend.h&gt;</span>
+</pre></div><p>
+These backend helper methods provide functionality commonly used by backends to make backend development easier and to provide the same behaviour between backends. <hr><h2>Function Documentation</h2>
+<a class="anchor" name="gfc099de90661f22a048d5fd16fca48f5"></a><!-- doxytag: member="helper.c::kdbbDecode" ref="gfc099de90661f22a048d5fd16fca48f5" args="(char *kdbbEncoded, void *returned)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbbDecode           </td>
+          <td>(</td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>kdbbEncoded</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">void *&nbsp;</td>
+          <td class="paramname"> <em>returned</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+UnkdbbEncodes a buffer of ASCII hexadecimal values into a byte stream.<p>
+The allowed format for the hexadecimal values is just a stream of pairs of plain hex-digits, all together or space-separated.<p>
+The <code>returned</code> data won't be bigger than half the size of the source <code>kdbbEncoded</code> data.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>kdbbEncoded</em>&nbsp;</td><td>the source of ASCII hexadecimal digits. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>preallocated destination for the kdbbDecoded data. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the amount of bytes kdbbDecoded <p>
+-1 on failure </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhelper.html#gba58d73495f15a309468fd4798539f22">kdbbEncode()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g0ae368114cb42f6f6d56bc5cc96e44cf"></a><!-- doxytag: member="helper.c::kdbbDecodeChar" ref="g0ae368114cb42f6f6d56bc5cc96e44cf" args="(const char *from, char *into)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbDecodeChar           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>from</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>into</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Char decoding.<p>
+Decode one char from 25, 2B, 2F, 2C following RFC 2396 or copy char untouched if different.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>from</em>&nbsp;</td><td>String containing sequence to decode </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>into</em>&nbsp;</td><td>Decoded char </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>: Positive size of byte read from "from" for decoding the sequence if sucess or -1 if error (into untouched)</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhelper.html#gac1bda629cb9912eaaaa785b2874cad1">kdbbEncodeChar</a></dd></dl>
+NOTE: No '\0' is added at the end of buffer. 
+</div>
+</div><p>
+<a class="anchor" name="gba58d73495f15a309468fd4798539f22"></a><!-- doxytag: member="helper.c::kdbbEncode" ref="gba58d73495f15a309468fd4798539f22" args="(void *kdbbDecoded, size_t size, char *returned)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbbEncode           </td>
+          <td>(</td>
+          <td class="paramtype">void *&nbsp;</td>
+          <td class="paramname"> <em>kdbbDecoded</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>size</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returned</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Encodes a buffer of data onto hexadecimal ASCII.<p>
+The resulting data is made up of pairs of ASCII hex-digits, space- and newline-separated. This is the counterpart of <a class="el" href="group__backendhelper.html#gfc099de90661f22a048d5fd16fca48f5">kdbbDecode()</a>.<p>
+The <code>returned</code> must allocated prior you call this function and won't be bigger than 3 times the size of the source <code>kdbbDecoded</code> + 1 byte.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>kdbbDecoded</em>&nbsp;</td><td>the source buffer. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>size</em>&nbsp;</td><td>the size of the source buffer in bytes. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>the preallocated destination for the ASCII-kdbbEncoded data. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the amount of bytes used in the resulting kdbbEncoded buffer. </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhelper.html#gfc099de90661f22a048d5fd16fca48f5">kdbbDecode()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gac1bda629cb9912eaaaa785b2874cad1"></a><!-- doxytag: member="helper.c::kdbbEncodeChar" ref="gac1bda629cb9912eaaaa785b2874cad1" args="(char c, char *buffer, size_t bufSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbEncodeChar           </td>
+          <td>(</td>
+          <td class="paramtype">char&nbsp;</td>
+          <td class="paramname"> <em>c</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>buffer</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>bufSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Char encoding.<p>
+Encode '/', '\', '', '+', ' ' char following RFC 2396 or copy char untouched if different.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>c</em>&nbsp;</td><td>Char to kdbbEncode </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>buffer</em>&nbsp;</td><td>string wich will contain kdbbEncoded char </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>bufSize</em>&nbsp;</td><td>Size of the buffer </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>: Size of the kdbbEncoded string if success or -1 if error * (then buffer is untouched)</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>kdbiDecodeChar</dd></dl>
+NOTE: No '\0' is added at the end of buffer. 
+</div>
+</div><p>
+<a class="anchor" name="ge3ac53e11feb9d7ff956a4562f1d9bde"></a><!-- doxytag: member="helper.c::kdbbFilenameToKeyName" ref="ge3ac53e11feb9d7ff956a4562f1d9bde" args="(const char *string, char *buffer, int bufSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbFilenameToKeyName           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>string</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>buffer</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&nbsp;</td>
+          <td class="paramname"> <em>bufSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Translate a relative file name to a key name applying decoding.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>string</em>&nbsp;</td><td>Filename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>buffer</em>&nbsp;</td><td>decoded keyName </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>bufSize</em>&nbsp;</td><td>Size of buffer </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhelper.html#g93921f5f2169e9d6ba1603807ee5bc2d">kdbbKeyNameToRelativeFilename</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6db2e7738f905ea0ab52b5c9c3981af6"></a><!-- doxytag: member="helper.c::kdbbGetFullFilename" ref="g6db2e7738f905ea0ab52b5c9c3981af6" args="(KDB *handle, const Key *forKey, char *returned, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbbGetFullFilename           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>forKey</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Calculate the real file name for a key.<p>
+system/ keys will get the prefix KDB_DB_SYSTEM<p>
+For the user/ keys the algorithm works as follow: 1.) When the override environment KDB_HOME exists the configuration will be searched below KDB_HOME/KDB_DB_USER 2.) When the owner of the key exists in the elektra user database steps a.) and b.) will be tested: a.) The specific value for configuration storage of the user below system/users/&lt;owner&gt;/kdb b.) The home variable in system/users/&lt;owner&gt;/home will be merged together with KDB_DB_USER 3.) When the environment HOME exists the configuration will be searched below HOME/KDB_DB_USER 4.) Otherwise the KDB_DB_HOME/&lt;owner&gt;/KDB_DB_USER will be used<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>forKey</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>the kdb handle to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>the buffer to return the calculated filename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>maximum number of bytes that fit the buffer </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>kdbCalcRelativeFilename() </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to the buffer, or 0 on error<p>
+length of returned string on success <p>
+-1 on failure </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g10364b65d79e44f99f9464e50dffa900"></a><!-- doxytag: member="helper.c::kdbbGetFullKeyName" ref="g10364b65d79e44f99f9464e50dffa900" args="(KDB *handle, const char *forFilename, const Key *parentKey, Key *returned)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbbGetFullKeyName           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>forFilename</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>parentKey</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>returned</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Calculates the keyname out of a relative filename.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>The kdb handle to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>forFilename</em>&nbsp;</td><td>needs to be the a null terminated string containing the relative filename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parentKey</em>&nbsp;</td><td>is the key above the key which will be returned </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>The proper keyname and owner will be stored in returned. A valid key must be passed. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to the buffer, or 0 on error<p>
+length of returned string on success <p>
+-1 on failure </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhelper.html#g93921f5f2169e9d6ba1603807ee5bc2d">kdbbKeyNameToRelativeFilename()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g1d09dab69a7d6cee9a1eb2c1c650051e"></a><!-- doxytag: member="helper.c::kdbbKeyCalcRelativeFilename" ref="g1d09dab69a7d6cee9a1eb2c1c650051e" args="(const Key *key, char *relativeFilename, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbbKeyCalcRelativeFilename           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>relativeFilename</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+This is a helper to kdbGetFullFilename()<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>has the relevant name for the relative filename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>relativeFilename</em>&nbsp;</td><td>the buffer to return the calculated filename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>maximum number of bytes that fit the buffer </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>kdbGetFullFilename() </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to the buffer <p>
+-1 on failure </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g93921f5f2169e9d6ba1603807ee5bc2d"></a><!-- doxytag: member="helper.c::kdbbKeyNameToRelativeFilename" ref="g93921f5f2169e9d6ba1603807ee5bc2d" args="(const char *string, char *buffer, size_t bufSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbKeyNameToRelativeFilename           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>string</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>buffer</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>bufSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Translate a key name to a relative file name applying encoding.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>string</em>&nbsp;</td><td>Keyname </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>buffer</em>&nbsp;</td><td>kdbbEncoded filename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>bufSize</em>&nbsp;</td><td>Size of buffer </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>Number of byte written in buffer on success, <p>
+-1 on failure</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__backendhelper.html#g93921f5f2169e9d6ba1603807ee5bc2d">kdbbKeyNameToRelativeFilename</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g11b83ea1eac5adb730c3f51660cded9d"></a><!-- doxytag: member="helper.c::kdbbNeedsUTF8Conversion" ref="g11b83ea1eac5adb730c3f51660cded9d" args="()" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbNeedsUTF8Conversion           </td>
+          <td>(</td>
+          <td class="paramtype">void&nbsp;</td>
+          <td class="paramname">          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Checks if UTF-8 conversion is needed in current context. if nl_langinfo() is not available, no conversion is ever needed. If iconv usage is disabled there is no need to check if we need to convert. Furthermore, some systems have nl_langinfo(), but lacks ability to get CODESET through it. Look at the comments by the <a class="el" href="group__backendhelper.html#g5b9a2cb642f2a626037c4c730c790c65">kdbbUTF8Engine()</a> function for more information.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 if not needed <p>
+anything else if needed </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g89aa8c310a5720766639c305e643c069"></a><!-- doxytag: member="helper.c::kdbbReadLock" ref="g89aa8c310a5720766639c305e643c069" args="(FILE *f)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbReadLock           </td>
+          <td>(</td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>f</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Locks file for read mode.<p>
+Other processes and threads are allowed to read the file too simultaneous.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>f</em>&nbsp;</td><td>is a valid filedescriptor </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure</dd></dl>
+<dl compact><dt><b><a class="el" href="err.html#_err000002">Error:</a></b></dt><dd>sets KDB_ERR_NOLOCK when locking failed </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb969d5d25762464c5f719f4f90757fe8"></a><!-- doxytag: member="helper.c::kdbbUnlock" ref="gb969d5d25762464c5f719f4f90757fe8" args="(FILE *f)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbUnlock           </td>
+          <td>(</td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>f</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Unlocks file.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>f</em>&nbsp;</td><td>is a valid filedescriptor </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure</dd></dl>
+<dl compact><dt><b><a class="el" href="err.html#_err000003">Error:</a></b></dt><dd>sets KDB_ERR_NOLOCK when locking failed </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g5b9a2cb642f2a626037c4c730c790c65"></a><!-- doxytag: member="helper.c::kdbbUTF8Engine" ref="g5b9a2cb642f2a626037c4c730c790c65" args="(int direction, char **string, size_t *inputOutputByteSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbUTF8Engine           </td>
+          <td>(</td>
+          <td class="paramtype">int&nbsp;</td>
+          <td class="paramname"> <em>direction</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char **&nbsp;</td>
+          <td class="paramname"> <em>string</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t *&nbsp;</td>
+          <td class="paramname"> <em>inputOutputByteSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Converts string to (<code>direction</code> = <code>UTF8_TO</code>) and from (<code>direction</code> = <code>UTF8_FROM</code>) UTF-8.<p>
+Since Elektra provides portability for key names and string values between different codesets, you should use this helper in your backend to convert to and from universal UTF-8 strings, when storing key names, values and comments.<p>
+Broken locales in applications can cause problems too. Make sure to load the environment locales in your application using <div class="fragment"><pre class="fragment">setlocale (LC_ALL, <span class="stringliteral">""</span>);
+</pre></div><p>
+Otherwise kdbbUTF8Engine will quit with -1 leading that backends return with error when non-ascii characters appear. Binary values are not effected.<p>
+If iconv() or nl_langinfo() is not available on your system, or if iconv() usage is disabled (--disable-iconv on build time) simply return 0 immediately.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>direction</em>&nbsp;</td><td>must be <code>UTF8_TO</code> (convert from current non-UTF-8 to UTF-8) or <code>UTF8_FROM</code> (convert from UTF-8 to current non-UTF-8) </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>string</em>&nbsp;</td><td>before the call: the string to be converted; after the call: reallocated to carry the converted string </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>inputOutputByteSize</em>&nbsp;</td><td>before the call: the size of the string including leading NULL; after the call: the size of the converted string including leading NULL </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gcea5819334a71744a54a6b290a8c3bdc"></a><!-- doxytag: member="helper.c::kdbbWriteLock" ref="gcea5819334a71744a54a6b290a8c3bdc" args="(FILE *f)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbbWriteLock           </td>
+          <td>(</td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>f</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Locks file for exclusive write mode.<p>
+This function will block until all reader and writer have left the file.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>f</em>&nbsp;</td><td>is a valid filedescriptor </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure</dd></dl>
+<dl compact><dt><b><a class="el" href="err.html#_err000001">Error:</a></b></dt><dd>sets KDB_ERR_NOLOCK when locking failed </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__internal.html b/doc/elektra-api/html/group__internal.html
new file mode 100644 (file)
index 0000000..184ee6d
--- /dev/null
@@ -0,0 +1,217 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KDB Backends :: Internal Helper for Elektra</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KDB Backends :: Internal Helper for Elektra</h1>Internal Methods for Elektra and Backends.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__internal.html#g18471c9f740ea7988a0e757e752834e6">kdbiStrCaseCmp</a> (const char *s1, const char *s2)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__internal.html#gba2b4284a78cad9cb3ca4cc7291caec5">kdbiRealloc</a> (void **buffer, size_t size)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__internal.html#g758aca746b4458fd067165c5c46676be">kdbiFree</a> (void *ptr)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">char *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__internal.html#gc7b7254ccb104b362d3e730da654ea87">kdbiStrDup</a> (const char *s)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">size_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__internal.html#ga13b38e07d4e54738b951c3e0bef2faf">kdbiStrLen</a> (const char *s)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Internal Methods for Elektra and Backends. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor"> #include &lt;kdbbackend.h&gt;</span>
+</pre></div><p>
+There are some areas where libraries have to reimplement some basic functions to archive support for non-standard systems, for testing purposes or to provide a little more convenience. <hr><h2>Function Documentation</h2>
+<a class="anchor" name="g758aca746b4458fd067165c5c46676be"></a><!-- doxytag: member="internal.c::kdbiFree" ref="g758aca746b4458fd067165c5c46676be" args="(void *ptr)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">void kdbiFree           </td>
+          <td>(</td>
+          <td class="paramtype">void *&nbsp;</td>
+          <td class="paramname"> <em>ptr</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Free memory of elektra or its backends.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ptr</em>&nbsp;</td><td>the pointer to free</td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>kdbiMalloc </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gba2b4284a78cad9cb3ca4cc7291caec5"></a><!-- doxytag: member="internal.c::kdbiRealloc" ref="gba2b4284a78cad9cb3ca4cc7291caec5" args="(void **buffer, size_t size)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbiRealloc           </td>
+          <td>(</td>
+          <td class="paramtype">void **&nbsp;</td>
+          <td class="paramname"> <em>buffer</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>size</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Reallocate Storage in a save way.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordflow">if</span> (<a class="code" href="group__internal.html#gba2b4284a78cad9cb3ca4cc7291caec5">kdbiRealloc</a> ((<span class="keywordtype">void</span> **) &amp; buffer, new_length) &lt; 0) {
+        <span class="comment">// here comes the failure handler</span>
+        <span class="comment">// you can still use the old buffer</span>
+<span class="preprocessor">#if DEBUG</span>
+<span class="preprocessor"></span>        fprintf (stderr, <span class="stringliteral">"Reallocation error\n"</span>);
+<span class="preprocessor">#endif</span>
+<span class="preprocessor"></span>        free (buffer);
+        buffer = 0;
+        <span class="comment">// return with error</span>
+}
+ *
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>buffer</em>&nbsp;</td><td>is a pointer to a malloc </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>size</em>&nbsp;</td><td>is the new size for the memory </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>-1 on failure <p>
+0 on success </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g18471c9f740ea7988a0e757e752834e6"></a><!-- doxytag: member="internal.c::kdbiStrCaseCmp" ref="g18471c9f740ea7988a0e757e752834e6" args="(const char *s1, const char *s2)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbiStrCaseCmp           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>s1</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>s2</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Compare Strings ignoring case.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>s1</em>&nbsp;</td><td>The first string to be compared </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>s2</em>&nbsp;</td><td>The second string to be compared</td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a negative number if s1 is less than s2 <p>
+0 if s1 matches s2 <p>
+a positive number if s1 is greater than s2 </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gc7b7254ccb104b362d3e730da654ea87"></a><!-- doxytag: member="internal.c::kdbiStrDup" ref="gc7b7254ccb104b362d3e730da654ea87" args="(const char *s)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">char* kdbiStrDup           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>s</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Copy string into new allocated memory.<p>
+You need to free the memory yourself.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>s</em>&nbsp;</td><td>the null-terminated string to duplicate</td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__internal.html#g758aca746b4458fd067165c5c46676be">kdbiFree</a> <p>
+<a class="el" href="group__internal.html#ga13b38e07d4e54738b951c3e0bef2faf">kdbiStrLen</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga13b38e07d4e54738b951c3e0bef2faf"></a><!-- doxytag: member="internal.c::kdbiStrLen" ref="ga13b38e07d4e54738b951c3e0bef2faf" args="(const char *s)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">size_t kdbiStrLen           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>s</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Calculates the length in bytes of a string.<p>
+This function differs from strlen() because it is Unicode and multibyte chars safe. While strlen() counts characters and ignores the final NULL, <a class="el" href="group__internal.html#ga13b38e07d4e54738b951c3e0bef2faf">kdbiStrLen()</a> count bytes including the ending NULL.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>s</em>&nbsp;</td><td>the string to get the length from </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes used by the string, including the final NULL. </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__kdb.html b/doc/elektra-api/html/group__kdb.html
new file mode 100644 (file)
index 0000000..388a5cd
--- /dev/null
@@ -0,0 +1,485 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KDB :: Low Level Methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KDB :: Low Level Methods</h1>General methods to access the Key database.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#g40e35f26cc69bd43ef1b2207f4fa121b">kdbMount</a> (KDB *handle, const Key *mountpoint, const KeySet *config)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#g400ca66a9bdc04ecadb66d84dc06bd55">kdbUnmount</a> (KDB *handle, const Key *mountpoint)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#ga3717146f45e5a9665377c7f5b71e39b">kdbGetMountpoint</a> (KDB *handle, const Key *where)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KDB *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a> ()</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a> (KDB *handle)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a> (KDB *handle, KeySet *returned, Key *parentKey, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet</a> (KDB *handle, KeySet *ks, Key *parentKey, option_t options)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+General methods to access the Key database. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor"> #include &lt;kdb.h&gt;</span>
+</pre></div><p>
+The kdb*() class of methods are used to access the storage, to get and set <a class="el" href="group__key.html">Keys </a> or <a class="el" href="group__keyset.html">KeySets </a>.<p>
+The most important functions are:<ul>
+<li><a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a></li><li><a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a></li><li><a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a></li><li><a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a></li></ul>
+<p>
+The two essential functions for dynamic information about backends are:<ul>
+<li><a class="el" href="group__kdb.html#ga3717146f45e5a9665377c7f5b71e39b">kdbGetMountpoint()</a></li><li>kdbGetCapability()</li></ul>
+<p>
+They use some backend implementation to know the details about how to access the storage. Currently we have this backends:<ul>
+<li><code>berkeleydb:</code> the keys are stored in a Berkeley DB database, providing very small footprint, speed, and other advantages.</li><li><code>filesys:</code> the key hierarchy and data are saved as plain text files in the filesystem.</li><li><code>ini:</code> the key hierarchy are saved into configuration files. <dl class="see" compact><dt><b>See also:</b></dt><dd><a href="http://www.libelektra.org/Ini">http://www.libelektra.org/Ini</a></dd></dl>
+</li><li><code>fstab:</code> a reference backend used to interpret the <code>/etc/fstab</code> file as a set of keys under <code>system/filesystems</code> .</li><li><code>gconf:</code> makes Elektra use the GConf daemon to access keys. Only the <code>user/</code> tree is available since GConf is not system wide.</li></ul>
+<p>
+Backends are physically a library named <code>/lib/libelektra-{NAME}</code>.so.<p>
+See <a class="el" href="group__backend.html">writing a new backend </a> for information about how to write a backend.<p>
+Language binding writers should follow the same rules:<ul>
+<li>You must relay completely on the backend-dependent methods.</li><li>You may use or reimplement the second set of methods.</li><li>You should completely reimplement in your language the higher lever methods.</li><li>Many methods are just for comfort in C. These methods are marked and need not to be implemented if the binding language has e.g. string operators which can do the operation easily. </li></ul>
+<hr><h2>Function Documentation</h2>
+<a class="anchor" name="gd9bb8bd3f1296bfa77cc9a1b41b7a859"></a><!-- doxytag: member="kdb.c::kdbClose" ref="gd9bb8bd3f1296bfa77cc9a1b41b7a859" args="(KDB *handle)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbClose           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Closes the session with the Key database.<p>
+You should call this method when you finished your affairs with the key database. You can manipulate Key and KeySet objects also after <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a>. You must not use any kdb* call afterwards. You can implement <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a> in the atexit() handler.<p>
+This is the counterpart of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a>.<p>
+The <code>handle</code> parameter will be finalized and all resources associated to it will be freed. After a <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a>, this <code>handle</code> can't be used anymore, unless it gets initialized again with another call to <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a>.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g37b44bda1b83bc0144916bf21a86c1b5"></a><!-- doxytag: member="kdb.c::kdbGet" ref="g37b44bda1b83bc0144916bf21a86c1b5" args="(KDB *handle, KeySet *returned, Key *parentKey, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbGet           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>parentKey</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Retrieve keys in an atomic and universal way, all other kdbGet Functions rely on that one.<p>
+The <code>returned</code> KeySet must be initialized or may already contain some keys. The new retrieved keys will be appended using <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a>.<p>
+In default behaviour (<code>options</code> = 0) it will fully retrieve all keys under the <code>parentKey</code> folder, with all subfolders and their children but not inactive keys or folders.<p>
+The keyset will not be sorted at first place, but will be marked dirty and sorted afterwards when needed. That could be a subsequent <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a>, <a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName()</a> or <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>. See <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> on that issue.<p>
+The behaviour can be fine-tuned with options in various ways to make <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> more comfortable.<h2><a class="anchor" name="kdbgetoption">
+Options</a></h2>
+The <code>option</code> is an array of the following ORed flags:<p>
+<ul>
+<li><code>option_t::KDB_O_DEL</code> <br>
+ Its often useful to <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> the parentKey in the line after <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>. Using this flag, you can just pass a key allocated with <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will free it for you in the end.</li><li><code>option_t::KDB_O_POP</code> <br>
+ The <code>parentKey</code> itself will always be added to <code>returned</code>. If you only want the children of the parentKey in <code>returned</code>, but not the parentKey itself, use this flag. This is only valid for the first parentKey, the one you passed. The other recursive parentKeys will stay in the keyset. To get only the leaves of the tree, without any parentKey, see option_t::KDB_O_NODIR below.</li><li><code>option_t::KDB_O_NODIR</code> <br>
+ Don't include folders in the <code>returned</code> KeySet, so only keys without subkeys. You can picture it best that you only get the leaves of the tree of keys.</li><li><code>option_t::KDB_O_DIRONLY</code> <br>
+ Put in <code>returned</code> only the folder keys. The resulting KeySet will be only the skeleton of the tree. This option must not be ORed together with KDB_O_DIR.</li><li><code>option_t::KDB_O_NOSTAT</code> <br>
+ Don't stat they keys, whatever <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> says. That means that also the key value and comment will be retrieved. The flag will result in that all keys in <code>returned</code> don't have <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> set.</li><li><code>option_t::KDB_O_STATONLY</code> <br>
+ Only stat the keys. It means that key value and comment will not be retrieved. The resulting keys will contain only meta info such as user and group IDs, owner, mode permissions and modification times. You don't need that flag if the keys already have <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> set. The flag will result in that all keys in <code>returned</code> have <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> set.</li><li><code>option_t::KDB_O_INACTIVE</code> <br>
+ Will make it not ignore inactive keys, so <code>returned</code> will contain also inactive keys. Inactive keys are those that have names begining with '.' (dot). Please be sure that you know what you are doing, inactive keys must not have any semantics to the application. This flag should only be set in key browsers after explicit user request. You might also get inactive keys when you plan to remove a whole hierarchy.</li><li><code>option_t::KDB_O_SORT</code> <br>
+ Force <code>returned</code> to be <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>ed. Normally you don't want that the <code>returned</code> is sorted immediately because you might add other keys or go for another <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>. Sorting will take place automatically when needed by <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> or <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>, also without this option set. But you need to sort the keyset for yourself, when you just iterate over it. If you want to do that, pass this flag at the last <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>.</li><li><code>option_t::KDB_O_NORECURSIVE</code> <br>
+ Dont get the keys recursive. Only receive keys from one folder. This might not work if the backend does not support it. Be prepared for more keys and use <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> and avoid static assumptions on how many keys you get.</li></ul>
+<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment">KDB *handle;
+KeySet *myConfig;
+Key *key;
+
+myConfig=<a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+
+handle = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+
+key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"system/sw/MyApp"</span>,KEY_END);
+rc=<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(handle,key, myConfig, 0);
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+
+key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/sw/MyApp"</span>,KEY_END);
+rc=<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(handle,key, myConfig, 0);
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+
+<span class="comment">// will sort keyset here</span>
+key=<a class="code" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName</a>(myConfig,<span class="stringliteral">"/sw/MyApp/key"</span>, 0);
+<span class="comment">// check if key is not 0 and work with it...</span>
+
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (myConfig); <span class="comment">// delete the in-memory configuration</span>
+
+
+<span class="comment">// maybe you want kdbSet() myConfig here</span>
+
+<a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a>(handle); <span class="comment">// no more affairs with the key database.</span>
+</pre></div></dd></dl>
+<h2><a class="anchor" name="kdbgetdetail">
+Details</a></h2>
+When no backend could be found (e.g. no backend mounted) the default backend will be used.<p>
+If you pass a NULL pointer as handle and/or returned <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will return -1 and do nothing but <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> the parentKey when requested and not a NULL pointer.<p>
+If you pass NULL as parentKey the root keys of all namespaces will be appended to returned.<p>
+For every directory key (<a class="el" href="group__keytest.html#gc0a10c602d52a35f81347e8a32312017">keyIsDir()</a>) the appropriate backend will be chosen and keys in it will be requested.<p>
+If any backend reports an failure the recursive getting of keys will be stopped. Backends only report failure when they are not able to get keys for any problems.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parentKey</em>&nbsp;</td><td>parent key or NULL to get the root keys </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>the (pre-initialized) KeySet returned with all keys found </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>ORed options to control approaches </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>option_t <p>
+<a class="el" href="group__kdbhighlevel.html">kdb higher level Methods </a> that rely on <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> <p>
+<a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName()</a>, ksLookupByString() for powerful lookups after the KeySet was retrieved <p>
+commandList() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage example <p>
+commandEdit() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage example <p>
+commandExport() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage example </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of keys contained by <code>returned</code> <p>
+-1 on failure </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga3717146f45e5a9665377c7f5b71e39b"></a><!-- doxytag: member="kdb.c::kdbGetMountpoint" ref="ga3717146f45e5a9665377c7f5b71e39b" args="(KDB *handle, const Key *where)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* kdbGetMountpoint           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>where</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Lookup a mountpoint in a handle for a specific key.<p>
+Will return a key representing the mountpoint or null if there is no appropriate mountpoint e.g. its the root mountpoint.<p>
+Together with kdbGetCapability() the two essential informations about mounted backends.<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment">Key * key = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"system/template"</span>);
+KDB * handle = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+Key *mountpoint=0;
+mountpoint=<a class="code" href="group__kdb.html#ga3717146f45e5a9665377c7f5b71e39b">kdbGetMountpoint</a>(handle, key);
+
+printf(<span class="stringliteral">"The library I am using is %s mounted in %s\n"</span>,
+        <a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(mountpoint),
+        <a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(mountpoint));
+<a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a> (handle);
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (key);
+</pre></div></dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>is the data structure, where the mounted directories are saved. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>where</em>&nbsp;</td><td>the key, that should be looked up. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the mountpoint associated with the key </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g40e35f26cc69bd43ef1b2207f4fa121b"></a><!-- doxytag: member="kdb.c::kdbMount" ref="g40e35f26cc69bd43ef1b2207f4fa121b" args="(KDB *handle, const Key *mountpoint, const KeySet *config)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbMount           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>mountpoint</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>config</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Dynamically mount a single backend.<p>
+Maps the mountpoint, defined through its name and value, into the global elektra hierachy. If successfull, under the mountpoint another backend will reside.<p>
+This only works for a single KDB, that means a single thread in a single process. You may want statically mounting by editing system/elektra/mountpoints.<p>
+If you allocated mountpoint and config first, make sure that you free it! It is ok to free it immediately afterwards.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>handle to the kdb data structure </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>mountpoint</em>&nbsp;</td><td>the <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> of this key is the mountpoint, <a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a> the backend </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>config</em>&nbsp;</td><td>the configuration passed for that backend </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success, -1 if an error occurred </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb7be60c387892d2235907836c5060e1f"></a><!-- doxytag: member="kdb.c::kdbOpen" ref="gb7be60c387892d2235907836c5060e1f" args="()" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KDB* kdbOpen           </td>
+          <td>(</td>
+          <td class="paramtype">void&nbsp;</td>
+          <td class="paramname">          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Opens the session with the Key database.<p>
+The first step is to open the default backend. With it system/elektra/mountpoints will be loaded and all needed libraries and mountpoints will be determined. These libraries for backends will be loaded and with it the <code>KDB</code> datastructure will be initialized.<p>
+You must always call this method before retrieving or commiting any keys to the database. In the end of the program, after using the key database, you must not forget to <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a>. You can use the atexit () handler for it.<p>
+The pointer to the <code>KDB</code> structure returned will be initialized like described above, and it must be passed along on any kdb*() method your application calls.<p>
+Get a <code>KDB</code> handle for every thread using elektra. Don't share the handle across threads, and also not the pointer accessing it: <div class="fragment"><pre class="fragment">thread1 {
+        KDB * h;
+        h = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+        <span class="comment">// fetch keys and work with them</span>
+        <a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a>(h);
+}
+thread2 {
+        KDB * h;
+        h = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+        <span class="comment">// fetch keys and work with them</span>
+        <a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a>(h);
+}
+</pre></div><p>
+You don't need to use the <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a> if you only want to manipulate plain in-memory Key or KeySet objects without any affairs with the backend key database,<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a> to end all affairs to the <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> database. </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a KDB pointer on success <p>
+NULL on failure </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g953cf29721e6000c2516cd6b5d36f571"></a><!-- doxytag: member="kdb.c::kdbSet" ref="g953cf29721e6000c2516cd6b5d36f571" args="(KDB *handle, KeySet *ks, Key *parentKey, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbSet           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>parentKey</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set keys in an atomic and universal way, all other kdbSet Functions rely on that one.<p>
+The given handle and keyset are the objects to work with.<p>
+With parentKey you can only store a part of the given keyset. Otherwise pass a null pointer or a parentKey without a name.<p>
+<div class="fragment"><pre class="fragment">KeySet *ks = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a> (h, ks, <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"system/myapp"</span>,0), KDB_O_DEL);
+<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a> (h, ks, <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/myapp"</span>,0), KDB_O_DEL);
+
+<span class="comment">//now only set everything below user, because you can't write to system</span>
+<a class="code" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet</a> (h, ks, <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/myapp"</span>,0), KDB_O_DEL);
+
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (ks);
+</pre></div><p>
+Each key is checked with <a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a> before being actually committed. So only changed keys are updated. If no key of a backend needs to be synced the <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend()</a> will be omitted.<p>
+If some error occurs, <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> will stop. In this situation the KeySet internal cursor will be set on the key that generated the error. This specific key and all behind it were not set. To be failsafe jump over it and try to set the rest, but report the error to the user.<p>
+<dl class="user" compact><dt><b>Example of how this method can be used:</b></dt><dd><div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> i;
+KeySet *ks;  <span class="comment">// the KeySet I want to set</span>
+<span class="comment">// fill ks with some keys</span>
+<span class="keywordflow">for</span> (i=0; i&lt; 10; i++) <span class="comment">// limit to 10 tries</span>
+{
+        ret=<a class="code" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet</a>(handle,ks, 0, 0);
+        <span class="keywordflow">if</span> (ret == -1)
+        {
+                <span class="comment">// We got an error. Warn user.</span>
+                Key *problem;
+                problem=<a class="code" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent</a>(ks);
+                <span class="keywordflow">if</span> (problem)
+                {
+                        <span class="keywordtype">char</span> keyname[300]=<span class="stringliteral">""</span>;
+                        <a class="code" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName</a>(problem,keyname,<span class="keyword">sizeof</span>(keyname));
+                        fprintf(stderr,<span class="stringliteral">"kdb import: while importing %s"</span>, keyname);
+                } <span class="keywordflow">else</span> <span class="keywordflow">break</span>;
+                <span class="comment">// And try to set keys again starting from the next key,</span>
+                <span class="comment">// unless we reached the end of KeySet</span>
+                <span class="keywordflow">if</span> (<a class="code" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext</a>(ks) == 0) <span class="keywordflow">break</span>;
+        }
+}
+</pre></div></dd></dl>
+<h2><a class="anchor" name="kdbsetoption">
+Options</a></h2>
+There are some options changing the behaviour of <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>:<p>
+<ul>
+<li><code>option_t::KDB_O_DEL</code> <br>
+ Its often useful to <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> the parentKey in the line after <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>. Using this flag, you can just pass a key allocated with <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will free it for you in the end.</li><li><code>option_t::KDB_O_SYNC</code> <br>
+ Will force to save all keys, independent of their sync state.</li><li><code>option_t::KDB_O_NOREMOVE</code> <br>
+ Don't remove any key from disk, even if <a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a> was set. With that flag removing keys can't happen unintentional. The flag will result in that all keys in <code>returned</code> don't have <a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove()</a> set.</li><li><code>option_t::KDB_O_REMOVEONLY</code> <br>
+ Remove all keys instead of setting them. All keys in <code>returned</code> will have <a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove()</a> set, but not <a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a> saying to you that the key was deleted permanently. This option implicit also activates <code>option_t::KDB_O_SYNC</code> because the sync state will be changed when they are marked remove. You might need option_t::KDB_O_INACTIVE set for the previous call of <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> if there are any. Otherwise the recursive remove will fail, because removing directories is only possible when all subkeys are removed.</li></ul>
+<h2><a class="anchor" name="kdbsetdetail">
+Details</a></h2>
+When you dont have a parentKey or its name empty, then all keys will be set.<p>
+You can remove some keys instead of setting them by marking them with <a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a>. The <a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a> flag will be unset after successful removing. But the <a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove()</a> flag will stay, but its safe to delete the key.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>a KeySet which should contain changed keys, otherwise nothing is done </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parentKey</em>&nbsp;</td><td>holds the information below which key keys should be set </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>see in <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> documentation </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a>, <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a>, <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> <p>
+<a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a>, <a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove()</a> <p>
+commandEdit(), commandImport() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage and error handling example </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g400ca66a9bdc04ecadb66d84dc06bd55"></a><!-- doxytag: member="kdb.c::kdbUnmount" ref="g400ca66a9bdc04ecadb66d84dc06bd55" args="(KDB *handle, const Key *mountpoint)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbUnmount           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>mountpoint</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Dynamically unmount a single backend.<p>
+Unmount a backend that was mounted with <a class="el" href="group__kdb.html#g40e35f26cc69bd43ef1b2207f4fa121b">kdbMount()</a> before.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>handle to the kdb data structure </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>mountpoint</em>&nbsp;</td><td>directory where backend is mounted to, that should be unmounted </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success, -1 if an error ocurred. </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__kdbhighlevel.html b/doc/elektra-api/html/group__kdbhighlevel.html
new file mode 100644 (file)
index 0000000..d5425d2
--- /dev/null
@@ -0,0 +1,369 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KDB :: High Level methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KDB :: High Level methods</h1>High level methods to access the Key database.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey</a> (KDB *handle, Key *dest)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey</a> (KDB *handle, const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdbhighlevel.html#g1e1b1a2beace8c9ce93d16259564b51f">kdbGetString</a> (KDB *handle, const char *keyname, char *returned, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdbhighlevel.html#g8e11b3403c9616c7fb3b9b37d1cb849e">kdbSetString</a> (KDB *handle, const char *keyname, const char *value)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdbhighlevel.html#gf9adbbeb3f49c63fb2f89930445c8060">kdbRemove</a> (KDB *handle, const char *keyname)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName</a> (KDB *handle, KeySet *returned, const char *name, option_t options)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+High level methods to access the Key database. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor"> #include &lt;kdb.h&gt;</span>
+</pre></div><p>
+These methods are higher level. They use <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen()</a>, <a class="el" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose()</a>, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> methods to do their job, and don't have to be reimplemented for a different backend.<p>
+These functions avoid limitations through not implemented capabilities. This will of course cost some effort, so read through the description carefully and decide if it is appropriate for your problem.<p>
+Binding writers don't have to implement these functions, use features of the binding language instead. But you can use these functions as ideas what high level methods may be useful.<p>
+Don't use writing single keys in a loop, prefer always writing out a keyset! <hr><h2>Function Documentation</h2>
+<a class="anchor" name="g3013d5768bbcf3c34652f489151940e2"></a><!-- doxytag: member="kdbhighlevel.c::kdbGetByName" ref="g3013d5768bbcf3c34652f489151940e2" args="(KDB *handle, KeySet *returned, const char *name, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t kdbGetByName           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>name</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+This method is similar <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> but the path is given by a string.<p>
+When it is not possible to make a key out of that string -1 is returned .<p>
+When parentName starts with / cascading will be used and both keys from user and system will be fetched.<p>
+A typically app with about 3000 keys may have this line:<p>
+<div class="fragment"><pre class="fragment">KDB *handle = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+KeySet *myConfig = (4096, KS_END);
+ssize_t ret = <a class="code" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName</a> (handle, myConfig, <span class="stringliteral">"/sw/app/current"</span>, 0);
+
+<span class="comment">// check ret and work with keyset myConfig</span>
+
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (myConfig);
+<a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a> (handle);
+ *
+</pre></div><p>
+myConfig will be loaded with keys from system/sw/app/current but also user/sw/app/current.<p>
+When one of these <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> fails -1 will be returned, but the other <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will be tried too.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>name</em>&nbsp;</td><td>the name where to get the keys below </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>the (pre-initialized) KeySet returned with all keys found </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>ORed options to control approaches Unlike to <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> is KDB_O_POP set per default. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of keys contained by <code>returned</code> <p>
+-1 on failure <p>
+-1 when <code>name</code> is no valid key <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga62877888f0cad395898859395e6635f"></a><!-- doxytag: member="kdbhighlevel.c::kdbGetKey" ref="ga62877888f0cad395898859395e6635f" args="(KDB *handle, Key *dest)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbGetKey           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>dest</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Fully retrieves the passed <code>key</code> from the backend storage.<p>
+The backend will try to get the key, identified through its name.<p>
+It uses <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> for retrieving the key and copies the found data to dest.<p>
+While <a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a> is perfect for a simple get of a specific key, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName()</a> gives you more control over the keyset.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>dest</em>&nbsp;</td><td>a pointer to a Key that has a name set </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a> to set a single <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> <p>
+commandGet() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage example <p>
+<a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName()</a> to have more control over <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> and options </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g1e1b1a2beace8c9ce93d16259564b51f"></a><!-- doxytag: member="kdbhighlevel.c::kdbGetString" ref="g1e1b1a2beace8c9ce93d16259564b51f" args="(KDB *handle, const char *keyname, char *returned, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbGetString           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>keyname</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+A high-level method to get a key value, by key name.<p>
+This method gets a backend from any backend with <a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a> and extracts the string and store it into returned. It only works with string keys.<p>
+This method gives you the direct relation between a keyname and the value, without any kdb specific structures. Use it when you just want some values out of the kdb namespace.<p>
+You need to know the maximum string length of the object. That could be the case when you e.g. save a path which is limited with MAX_PATH.<p>
+<div class="fragment"><pre class="fragment">KDB *handle = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+<span class="keywordtype">char</span> buffer [MAX_PATH];
+
+<span class="keywordflow">if</span> (<a class="code" href="group__kdbhighlevel.html#g1e1b1a2beace8c9ce93d16259564b51f">kdbGetString</a>(handle, <span class="stringliteral">"user/key/to/get/pathname"</span>, buffer, <span class="keyword">sizeof</span>(buffer)) == -1)
+{
+        <span class="comment">// handle error cases</span>
+} <span class="keywordflow">else</span> {
+        printf (<span class="stringliteral">"The keys value is %s\n"</span>, buffer);
+}
+<a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a>(handle);
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>keyname</em>&nbsp;</td><td>the name of the key to receive the value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>a buffer to put the key value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>the size of the buffer </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure <p>
+-1 on NULL pointers <p>
+-1 if maxSize is 0 or larger than SSIZE_MAX </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdbhighlevel.html#g8e11b3403c9616c7fb3b9b37d1cb849e">kdbSetString()</a> and <a class="el" href="group__kdbhighlevel.html#gf9adbbeb3f49c63fb2f89930445c8060">kdbRemove()</a> to set and remove a string <p>
+<a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a>, keySetKey() to work with Keys <p>
+<a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName()</a> for full access to <a class="el" href="group__internal.html" title="Internal Methods for Elektra and Backends.">KDB Backends :: Internal Helper for Elektra</a> datastructures </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gf9adbbeb3f49c63fb2f89930445c8060"></a><!-- doxytag: member="kdbhighlevel.c::kdbRemove" ref="gf9adbbeb3f49c63fb2f89930445c8060" args="(KDB *handle, const char *keyname)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbRemove           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>keyname</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Remove a key by its name from the backend storage.<p>
+With <a class="el" href="group__kdbhighlevel.html#g8e11b3403c9616c7fb3b9b37d1cb849e">kdbSetString()</a> its only possible to set a key with an empty string. To really remove a key in a highlevel way you can use this method.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>keyname</em>&nbsp;</td><td>the name of the key to be removed </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure <p>
+-1 on NULL pointers </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>together with <a class="el" href="group__kdbhighlevel.html#g8e11b3403c9616c7fb3b9b37d1cb849e">kdbSetString()</a> and <a class="el" href="group__kdbhighlevel.html#g1e1b1a2beace8c9ce93d16259564b51f">kdbGetString()</a> a highlevel interface for <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> <p>
+commandRemove() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage example </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g23b2f5fead4cddeb5542051a197ddc20"></a><!-- doxytag: member="kdbhighlevel.c::kdbSetKey" ref="g23b2f5fead4cddeb5542051a197ddc20" args="(KDB *handle, const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbSetKey           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Sets <code>key</code> in the backend storage.<p>
+While <a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a> is perfect for a simple get of a specific key, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName()</a> gives you more control over the keyset.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>Key to set </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on failure <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a> to get a single <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> <p>
+<a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> for more control over <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> and options <p>
+commandSet() code in <a class="el" href="group__kdb.html" title="General methods to access the Key database.">KDB :: Low Level Methods</a> command for usage example </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g8e11b3403c9616c7fb3b9b37d1cb849e"></a><!-- doxytag: member="kdbhighlevel.c::kdbSetString" ref="g8e11b3403c9616c7fb3b9b37d1cb849e" args="(KDB *handle, const char *keyname, const char *value)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int kdbSetString           </td>
+          <td>(</td>
+          <td class="paramtype">KDB *&nbsp;</td>
+          <td class="paramname"> <em>handle</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>keyname</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>value</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+A high-level method to set a value to a key, by key name.<p>
+It will check if key exists first, and keep its metadata. So you'll not loose the previous key comment.<p>
+This will set a text key. So if the key was previously a binary it will be retyped as string.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>handle</em>&nbsp;</td><td>contains internal information of <a class="el" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">opened </a> key database </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>keyname</em>&nbsp;</td><td>the name of the key to receive the value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>value</em>&nbsp;</td><td>the value to be set </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL pointers <p>
+-1 on failure </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdbhighlevel.html#g1e1b1a2beace8c9ce93d16259564b51f">kdbGetString()</a>, <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a>, <a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a> </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__key.html b/doc/elektra-api/html/group__key.html
new file mode 100644 (file)
index 0000000..304eda4
--- /dev/null
@@ -0,0 +1,444 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Key :: Basic Methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Key :: Basic Methods</h1>Key construction and initialization methods.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (const char *keyName,...)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup</a> (const Key *source)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#g6a12cbbe656a1ad9f41b8c681d7a2f92">keyCopy</a> (Key *dest, const Key *source)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef</a> (Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#g2c6433ca22109e4e141946057eccb283">keyDecRef</a> (Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__key.html#g4aabc4272506dd63161db2bbb42de8ae">keyGetRef</a> (const Key *key)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Key construction and initialization methods. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdb.h&gt;</span>
+</pre></div><p>
+A Key is the essential class that encapsulates key <a class="el" href="group__keyname.html">name </a>, <a class="el" href="group__keyvalue.html">value </a> and <a class="el" href="group__keymeta.html">metainfo </a>. Key properties are:<ul>
+<li><a class="el" href="group__keyname.html">Key name </a></li><li><a class="el" href="group__keyvalue.html">Key value </a></li><li><a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">Data type </a></li><li><a class="el" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">Key comment </a></li><li><a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">Key owner </a></li><li><a class="el" href="group__keymeta.html">UID, GID and filesystem-like mode permissions </a></li><li><a class="el" href="group__keymeta.html">Mode, change and modification times </a></li></ul>
+<p>
+Described here the methods to allocate and free the key. <hr><h2>Function Documentation</h2>
+<a class="anchor" name="g6a12cbbe656a1ad9f41b8c681d7a2f92"></a><!-- doxytag: member="key.c::keyCopy" ref="g6a12cbbe656a1ad9f41b8c681d7a2f92" args="(Key *dest, const Key *source)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyCopy           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>dest</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>source</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Copy or Clear a key.<p>
+Most often you may prefer <a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a> which allocates a new key and returns a duplication of another key.<p>
+But when you need to copy into an existing key, e.g. because it was passed by a pointer in a function you can do so:<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> h (Key *k)
+{
+        <span class="comment">// receive key c</span>
+        <a class="code" href="group__key.html#g6a12cbbe656a1ad9f41b8c681d7a2f92">keyCopy</a> (k, c);
+        <span class="comment">// the caller will see the changed key k</span>
+}
+</pre></div><p>
+The reference counter will not change for the destination key. Affiliation to keysets are also not affected.<p>
+When you pass a NULL-pointer as source the data of dest will be cleaned completely and you get a fresh dest key.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> g (Key *k)
+{
+        <a class="code" href="group__key.html#g6a12cbbe656a1ad9f41b8c681d7a2f92">keyCopy</a> (k, 0);
+        <span class="comment">// k is now an empty and fresh key</span>
+}
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>dest</em>&nbsp;</td><td>the key which will be written to </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>source</em>&nbsp;</td><td>the key which should be copied or NULL to clean the destination key</td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>-1 on failure when a NULL pointer was passed for dest or a dynamic property could not be written. <p>
+0 when dest was cleaned <p>
+1 when source was successfully copied </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a> to get a duplication of a <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g2c6433ca22109e4e141946057eccb283"></a><!-- doxytag: member="key.c::keyDecRef" ref="g2c6433ca22109e4e141946057eccb283" args="(Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyDecRef           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Decrement the viability of a key object.<p>
+The reference counter can't be decremented once it reached 0. In that situation nothing will happen and 0 will be returned.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the value of the new reference counter <p>
+-1 on null pointer <p>
+0 when the key is ready to be freed </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#g4aabc4272506dd63161db2bbb42de8ae">keyGetRef()</a>, <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a>, <a class="el" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g3df95bbc2494e3e6703ece5639be5bb1"></a><!-- doxytag: member="key.c::keyDel" ref="g3df95bbc2494e3e6703ece5639be5bb1" args="(Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyDel           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+A destructor for Key objects.<p>
+Every key created by <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> must be deleted with <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a>.<p>
+It is save to delete keys which are in a keyset, the number of references will be returned then.<p>
+It is save to delete a nullpointer, -1 will be returned then.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to delete </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>, keyInc(), <a class="el" href="group__key.html#g4aabc4272506dd63161db2bbb42de8ae">keyGetRef()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the value of the reference counter if the key is within keyset(s) <p>
+0 when the key was freed <p>
+-1 on null pointers </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ge6ec6a60cc4b8c1463fa08623d056ce3"></a><!-- doxytag: member="key.c::keyDup" ref="ge6ec6a60cc4b8c1463fa08623d056ce3" args="(const Key *source)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* keyDup           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>source</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return a duplicate of a key.<p>
+Memory will be allocated as needed for dynamic properties.<p>
+The new key will not be member of any KeySet and will start with a new reference counter at 0. A subsequent <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> will delete the key.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> f (<span class="keyword">const</span> Key * source)
+{
+        Key * dup = <a class="code" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup</a> (source);
+        <span class="comment">// work with duplicate</span>
+        <a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (dup);
+        <span class="comment">// everything related to dup is freed</span>
+        <span class="comment">// and source is unchanged</span>
+}
+</pre></div><p>
+Like for a new key after <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> a subsequent <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a> makes a KeySet to take care of the lifecycle of the key.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> g (<span class="keyword">const</span> Key * source, KeySet * ks)
+{
+        Key * dup = <a class="code" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup</a> (source);
+        <span class="comment">// work with duplicate</span>
+        <a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a> (ks, dup);
+        <span class="comment">// ksDel(ks) will also free the duplicate</span>
+        <span class="comment">// source remains unchanged.</span>
+}
+</pre></div><p>
+Duplication of keys should be preferred to <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>, because data like owner can be filled with a copy of the key instead of asking the environment. It can also be optimized in the checks, because the keyname is known to be valid.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>source</em>&nbsp;</td><td>has to be an initializised source Key </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 failure or on NULL pointer <p>
+a fully copy of source on success </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a>, <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> <p>
+keyClear(), <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4aabc4272506dd63161db2bbb42de8ae"></a><!-- doxytag: member="key.c::keyGetRef" ref="g4aabc4272506dd63161db2bbb42de8ae" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetRef           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return how many references the key has.<p>
+The references will be incremented when <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a> or <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a> uses the key and will be decremented when <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a> is used.<p>
+<a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a> will reset the references for dupped key.<p>
+For your own applications you can use <a class="el" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef()</a> and keyDelRef() for reference counting. Keys with zero references will be deleted when using <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a>.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of references <p>
+-1 on null pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef()</a> and <a class="el" href="group__key.html#g2c6433ca22109e4e141946057eccb283">keyDecRef()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6970a6f254d67af7e39f8e469bb162f1"></a><!-- doxytag: member="key.c::keyIncRef" ref="g6970a6f254d67af7e39f8e469bb162f1" args="(Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyIncRef           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Increment the viability of a key object.<p>
+This function is intended for applications using their own reference counter for key objects. With it you can increment the reference and thus avoid destruction of the object in a subsequent <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a>.<p>
+<div class="fragment"><pre class="fragment">Key *k;
+keyInc (k);
+function_that_keyDec(k);
+<span class="comment">// work with k</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (k); <span class="comment">// now really free it</span>
+</pre></div><p>
+The reference counter can't be incremented once it reached SSIZE_MAX. In that situation nothing will happen and SSIZE_MAX will be returned.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the value of the new reference counter <p>
+-1 on null pointer <p>
+SSIZE_MAX when maximum exceeded </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#g4aabc4272506dd63161db2bbb42de8ae">keyGetRef()</a>, <a class="el" href="group__key.html#g2c6433ca22109e4e141946057eccb283">keyDecRef()</a>, <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gf6893c038b3ebee90c73a9ea8356bebf"></a><!-- doxytag: member="key.c::keyNew" ref="gf6893c038b3ebee90c73a9ea8356bebf" args="(const char *keyName,...)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* keyNew           </td>
+          <td>(</td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>keyName</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">&nbsp;</td>
+          <td class="paramname"> <em>...</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+A practical way to fully create a Key object in one step.<p>
+This function tries to mimic the C++ way for constructors.<p>
+To just get a key object, simple do: <div class="fragment"><pre class="fragment">Key *k = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<span class="comment">// work with it</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (k);
+</pre></div><p>
+If you want the key object to contain a name, value, comment and other meta info read on.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>When you already have a key with similar properties its easier and cheaper to <a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a> the key.</dd></dl>
+Due to ABI compatibility, the <code>Key</code> structure is not defined in kdb.h, only declared. So you can only declare <code>pointers</code> to <code>Keys</code> in your program, and allocate and free memory for them with <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> and <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> respectively. See <a href="http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135">http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135</a><p>
+You can call it in many different ways depending on the attribute tags you pass as parameters. Tags are represented as the keyswitch_t values, and tell <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> which Key attribute comes next.<p>
+The simplest and minimum way to use it is with no tags, only a key name: <div class="fragment"><pre class="fragment">Key *nullKey,*emptyNamedKey;
+
+<span class="comment">// Create a key that has no name, is completely empty, but is initialized</span>
+nullKey=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (nullKey);
+
+<span class="comment">// Is the same as above</span>
+nullKey=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">""</span>, KEY_END);
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (nullKey);
+
+<span class="comment">// Create and initialize a key with a name and nothing else</span>
+emptyNamedKey=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/some/example"</span>,KEY_END);
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (emptyNamedKey);
+</pre></div><p>
+<a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> allocates memory for a key object and cleans everything up. After that, it processes the given argument list.<p>
+The Key attribute tags are the following:<ul>
+<li>keyswitch_t::KEY_TYPE <br>
+ Next parameter is a type of the value. Default assumed is KEY_TYPE_UNDEFINED. Set this attribute so that a subsequent KEY_VALUE can toggle to <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> or <a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary()</a> regarding to <a class="el" href="group__keytest.html#gea7670778abd07fee0fe8ac12a149190">keyIsString()</a> or <a class="el" href="group__keytest.html#g9526b371087564e43e3dff8ad0dac949">keyIsBinary()</a>. If you don't use KEY_TYPE but a KEY_VALUE follows afterwards, KEY_TYPE_STRING will be used.</li><li>keyswitch_t::KEY_SIZE <br>
+ Define a maximum length of the value. This is especially useful for setting a binary key. So make sure you use that before you KEY_VALUE for binary keys.</li><li>keyswitch_t::KEY_VALUE <br>
+ Next parameter is a pointer to the value that will be set to the key If no keyswitch_t::KEY_TYPE was used before, keyswitch_t::KEY_TYPE_STRING is assumed. If KEY_TYPE was previously passed with a KEY_TYPE_BINARY, you should have passed KEY_SIZE before! Otherwise it will be cut of with first \0 in string!</li><li>keyswitch_t::KEY_UID, <code>keyswitch_t::KEY_GID</code> <br>
+ Next parameter is taken as the UID (uid_t) or GID (gid_t) that will be defined on the key. See <a class="el" href="group__keymeta.html#gb5f284f5ecd261e0a290095f50ba1af7">keySetUID()</a> and <a class="el" href="group__keymeta.html#g9e3d0fb3f7ba906e067727b9155d22e3">keySetGID()</a>.</li><li>keyswitch_t::KEY_MODE <br>
+ Next parameter is taken as mode permissions (mode_t) to the key. See <a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a>.</li><li>keyswitch_t::KEY_DIR <br>
+ Define that the key is a directory rather than a ordinary key. This means its executable bits in its mode are set. This option allows the key to have subkeys. See <a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a>.</li><li>keyswitch_t::KEY_OWNER <br>
+ Next parameter is the owner. See <a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner()</a>.</li><li>keyswitch_t::KEY_COMMENT <br>
+ Next parameter is a comment. See <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a>.</li><li>keyswitch_t::KEY_REMOVE <br>
+ Mark the key to be removed instead of set it. See <a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a>.</li><li>keyswitch_t::KEY_STAT <br>
+ Mark the key to be stated instead of get it. See <a class="el" href="group__keymeta.html#gb8189add5e562bdb148675ee595bd95b">keyStat()</a>.</li><li>keyswitch_t::KEY_END <br>
+ Must be the last parameter passed to <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>. It is always required, unless the <code>keyName</code> is 0.</li></ul>
+<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment">KeySet *ks=<a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks,<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0));       <span class="comment">// an empty key</span>
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks,<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/sw"</span>,              <span class="comment">// the name of the key</span>
+        KEY_END));                      <span class="comment">// no more args</span>
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks,<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/tmp/ex1"</span>,
+        KEY_VALUE,<span class="stringliteral">"some data"</span>,          <span class="comment">// set a string value</span>
+        KEY_END));                      <span class="comment">// end of args</span>
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks,<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/tmp/ex2"</span>,
+        KEY_VALUE,<span class="stringliteral">"some data"</span>,          <span class="comment">// with a simple value</span>
+        KEY_MODE,0777,                  <span class="comment">// permissions</span>
+        KEY_END));                      <span class="comment">// end of args</span>
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks,<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/tmp/ex4"</span>,
+        KEY_TYPE,KEY_TYPE_BINARY,       <span class="comment">// key type</span>
+        KEY_SIZE,7,                     <span class="comment">// assume binary length 7</span>
+        KEY_VALUE,<span class="stringliteral">"some data"</span>,          <span class="comment">// value that will be truncated in 7 bytes</span>
+        KEY_COMMENT,<span class="stringliteral">"value is truncated"</span>,
+        KEY_OWNER,<span class="stringliteral">"root"</span>,               <span class="comment">// owner (not uid) is root</span>
+        KEY_UID,0,                      <span class="comment">// root uid</span>
+        KEY_END));                      <span class="comment">// end of args</span>
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks,<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/tmp/ex5"</span>,
+        KEY_TYPE,
+                KEY_TYPE_DIR | KEY_TYPE_BINARY,<span class="comment">// dir key with a binary value</span>
+        KEY_SIZE,7,
+        KEY_VALUE,<span class="stringliteral">"some data"</span>,          <span class="comment">// value that will be truncated in 7 bytes</span>
+        KEY_COMMENT,<span class="stringliteral">"value is truncated"</span>,
+        KEY_OWNER,<span class="stringliteral">"root"</span>,               <span class="comment">// owner (not uid) is root</span>
+        KEY_UID,0,                      <span class="comment">// root uid</span>
+        KEY_END));                      <span class="comment">// end of args</span>
+
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a>(ks);
+</pre></div></dd></dl>
+The reference counter (see <a class="el" href="group__key.html#g4aabc4272506dd63161db2bbb42de8ae">keyGetRef()</a>) will be initialized with 0, that means a subsequent call of <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> will delete the key. If you append the key to a keyset the reference counter will be incremented by one (see keyInc()) and the key can't be be deleted by a <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a>.<p>
+<div class="fragment"><pre class="fragment">Key *k = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0); <span class="comment">// ref counter 0</span>
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks, k); <span class="comment">// ref counter of key 1</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a>(ks); <span class="comment">// key will be deleted with keyset</span>
+ *
+</pre></div><p>
+If you increment only by one with keyInc() the same as said above is valid:<p>
+<div class="fragment"><pre class="fragment">Key *k = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0); <span class="comment">// ref counter 0</span>
+<a class="code" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef</a>(k); <span class="comment">// ref counter of key 1</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(k);    <span class="comment">// has no effect</span>
+<a class="code" href="group__key.html#g2c6433ca22109e4e141946057eccb283">keyDecRef</a>(k); <span class="comment">// ref counter back to 0</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(k);    <span class="comment">// key is now deleted</span>
+ *
+</pre></div><p>
+If you add the key to more keySets:<p>
+<div class="fragment"><pre class="fragment">Key *k = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0); <span class="comment">// ref counter 0</span>
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks1, k); <span class="comment">// ref counter of key 1</span>
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks2, k); <span class="comment">// ref counter of key 2</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a>(ks1); <span class="comment">// ref counter of key 1</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a>(ks2); <span class="comment">// k is now deleted</span>
+ *
+</pre></div><p>
+or use keyInc() more than once:<p>
+<div class="fragment"><pre class="fragment">Key *k = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0); <span class="comment">// ref counter 0</span>
+<a class="code" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef</a>(k); <span class="comment">// ref counter of key 1</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (k);   <span class="comment">// has no effect</span>
+<a class="code" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef</a>(k); <span class="comment">// ref counter of key 2</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (k);   <span class="comment">// has no effect</span>
+<a class="code" href="group__key.html#g2c6433ca22109e4e141946057eccb283">keyDecRef</a>(k); <span class="comment">// ref counter of key 1</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (k);   <span class="comment">// has no effect</span>
+<a class="code" href="group__key.html#g2c6433ca22109e4e141946057eccb283">keyDecRef</a>(k); <span class="comment">// ref counter is now 0</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (k); <span class="comment">// k is now deleted</span>
+ *
+</pre></div><p>
+they key won't be deleted by a <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> as long refcounter is not 0.<p>
+The key's sync bit will always be set for any call, except: <div class="fragment"><pre class="fragment">Key *k = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<span class="comment">// keyNeedSync() will be false</span>
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>keyName</em>&nbsp;</td><td>a valid name to the key, or NULL to get a simple initialized, but really empty, object </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to a new allocated and initialized Key object, or NULL if an invalid <code>keyName</code> was passed (see <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>). </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__keymeta.html b/doc/elektra-api/html/group__keymeta.html
new file mode 100644 (file)
index 0000000..0275b2d
--- /dev/null
@@ -0,0 +1,722 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Key :: Meta Info Manipulation Methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Key :: Meta Info Manipulation Methods</h1>Methods to do various operations on Key metainfo.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#gb8189add5e562bdb148675ee595bd95b">keyStat</a> (Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove</a> (Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">uid_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#gcaa5060e67b03f50ae49a3620c54bc46">keyGetUID</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#gb5f284f5ecd261e0a290095f50ba1af7">keySetUID</a> (Key *key, uid_t uid)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">gid_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g46a95e81d7d7f4e3eb59e60e5f3738c0">keyGetGID</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g9e3d0fb3f7ba906e067727b9155d22e3">keySetGID</a> (Key *key, gid_t gid)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir</a> (Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">mode_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#gbc0cec592ce3b77e9bc33dbc8e8f6bdc">keyGetMode</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode</a> (Key *key, mode_t mode)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">type_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g4fbbf04b1a2c5928dfcec939972e7347">keyGetType</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">keySetType</a> (Key *key, type_t newType)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">time_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g6b05da399c3c78904969ef39f191b0eb">keyGetATime</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g995d8b84731673c88c7c01f3fed538b9">keySetATime</a> (Key *key, time_t atime)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">time_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g57689eb5691679071463b777ae786ae9">keyGetMTime</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g481d8997187992fe4bbf288bc8ef4db7">keySetMTime</a> (Key *key, time_t mtime)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">time_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g2c213c120cbe02201278ef7fb8cd94be">keyGetCTime</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keymeta.html#g9f502ecab8ab43f0b17220fcc95f3fa5">keySetCTime</a> (Key *key, time_t ctime)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to do various operations on Key metainfo. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdb.h&gt;</span>
+</pre></div><p>
+Next to <a class="el" href="group__keyname.html">Name (key and owner) </a> and <a class="el" href="group__keyvalue.html">Value (data and comment) </a> there is the so called metainfo inside every key.<p>
+Key metainfo insists of:<ul>
+<li>UID, the user id</li><li>GID, the group id</li><li>filesystem-like mode permissions (rwx)</li><li>Mode, change and modification times</li></ul>
+<p>
+The comment can contain userdata which directly belong to that key.<p>
+Owner is the user that owns the key. It only works for the user/ hierachy.<p>
+Every user and group of your System has a uniqe ID. These values are used in the keys too. They are very important for the mode. See man 2 chown.<p>
+With the mode mode you can choose if a user, group or the world can mode your key. See man 2 chmod. <hr><h2>Function Documentation</h2>
+<a class="anchor" name="g6b05da399c3c78904969ef39f191b0eb"></a><!-- doxytag: member="keymeta.c::keyGetATime" ref="g6b05da399c3c78904969ef39f191b0eb" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">time_t keyGetATime           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get last time the key data was read from disk.<p>
+Every <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> might update the access time of a key. You get information when the key was read the last time from the database.<p>
+You will get 0 when the key was not read already.<p>
+Beware that multiple copies of keys with <a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a> might have different atimes because you <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> one, but not the other. You can use this information to decide which key is the latest.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>Key to get information from. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the time you got the key with <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> <p>
+0 on key that was never <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> <p>
+(time_t)-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g995d8b84731673c88c7c01f3fed538b9">keySetATime()</a> <p>
+<a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g2c213c120cbe02201278ef7fb8cd94be"></a><!-- doxytag: member="keymeta.c::keyGetCTime" ref="g2c213c120cbe02201278ef7fb8cd94be" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">time_t keyGetCTime           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get last time the key metadata was changed from disk.<p>
+You will get 0 when the key was not read already.<p>
+Any changed field in metadata will influence the ctime of a key.<p>
+This time is not updated if only value or comment are changed.<p>
+Not changed keys will not update this time, even after <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>.<p>
+It is possible that other keys written to disc influence this time if the backend is not grained enough.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>Key to get information from. </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g9f502ecab8ab43f0b17220fcc95f3fa5">keySetCTime()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>(time_t)-1 on NULL pointer <p>
+the metadata change time </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g46a95e81d7d7f4e3eb59e60e5f3738c0"></a><!-- doxytag: member="keymeta.c::keyGetGID" ref="g46a95e81d7d7f4e3eb59e60e5f3738c0" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">gid_t keyGetGID           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the group ID of a key.<h2><a class="anchor" name="GID">
+GID</a></h2>
+The group ID is a unique identification for every group present on a system. Keys will belong to root (0) as long as you did not get their real GID with <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>.<p>
+Unlike UID users might change their group. This makes it possible to share configuration between some users.<p>
+A fresh key will have (gid_t)-1 also known as the group nogroup. It means that the key is not related to a group ID at the moment.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the system's GID of the key <p>
+(gid_t)-1 on NULL key or currently unknown ID </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g9e3d0fb3f7ba906e067727b9155d22e3">keySetGID()</a>, <a class="el" href="group__keymeta.html#gcaa5060e67b03f50ae49a3620c54bc46">keyGetUID()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gbc0cec592ce3b77e9bc33dbc8e8f6bdc"></a><!-- doxytag: member="keymeta.c::keyGetMode" ref="gbc0cec592ce3b77e9bc33dbc8e8f6bdc" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">mode_t keyGetMode           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the key mode permissions.<p>
+Default is 0664 (octal) for keys and 0775 for directory keys which used <a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a>.<p>
+The defaults are defined with the macros KEY_DEF_MODE and KEY_DEF_DIR.<p>
+For more information about the mode permissions see <a class="el" href="group__backend.html#mode">Mode</a>.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>mode permissions of the key <p>
+(mode_t)-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g57689eb5691679071463b777ae786ae9"></a><!-- doxytag: member="keymeta.c::keyGetMTime" ref="g57689eb5691679071463b777ae786ae9" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">time_t keyGetMTime           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get last modification time of the key on disk.<p>
+You will get 0 when the key was not read already.<p>
+Everytime you change value or comment and <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> the key the mtime will be updated. When you <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> the key, the atime is set appropriate.<p>
+Not changed keys may not even passed to <a class="el" href="group__backend.html#g2d86ff43b693d59d4b82b597756e9e23">kdbSet_backend()</a> so it will not update this time, even after <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>.<p>
+It is possible that other keys written to disc influence this time if the backend is not grained enough.<p>
+If you add or remove a key the key thereunder in the hierarchy will update the mtime if written with <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> to disc.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>Key to get information from. </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g481d8997187992fe4bbf288bc8ef4db7">keySetMTime()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the last modification time <p>
+(time_t)-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4fbbf04b1a2c5928dfcec939972e7347"></a><!-- doxytag: member="keymeta.c::keyGetType" ref="g4fbbf04b1a2c5928dfcec939972e7347" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">type_t keyGetType           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Returns the key data type.<p>
+See type_t for the type definition.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">keySetType()</a> <p>
+<a class="el" href="group__keytest.html#g9526b371087564e43e3dff8ad0dac949">keyIsBinary()</a> and <a class="el" href="group__keytest.html#gea7670778abd07fee0fe8ac12a149190">keyIsString()</a> <p>
+<a class="el" href="group__keytest.html#gc0a10c602d52a35f81347e8a32312017">keyIsDir()</a> is not related to the type system </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>key where to get the type. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the key type <p>
+KEY_TYPE_UNDEFINED on keys without type <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gcaa5060e67b03f50ae49a3620c54bc46"></a><!-- doxytag: member="keymeta.c::keyGetUID" ref="gcaa5060e67b03f50ae49a3620c54bc46" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">uid_t keyGetUID           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the user ID of a key.<h2><a class="anchor" name="UID">
+UID</a></h2>
+The user ID is a unique identification for every user present on a system. Keys will belong to root (0) as long as you did not get their real UID with <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>.<p>
+Although usually the same, the UID of a key is not related to its owner.<p>
+A fresh key will have (uid_t)-1 also known as the user nobody. It means that the key is not related to a user ID at the moment.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the system's UID of the key <p>
+(uid_t)-1 on NULL key or currently unknown ID </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g46a95e81d7d7f4e3eb59e60e5f3738c0">keyGetGID()</a>, <a class="el" href="group__keymeta.html#gb5f284f5ecd261e0a290095f50ba1af7">keySetUID()</a>, <a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6e14e5f1de26e1318100631a149f2984"></a><!-- doxytag: member="keymeta.c::keyRemove" ref="g6e14e5f1de26e1318100631a149f2984" args="(Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyRemove           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Permanently remove a key after committing to database.<p>
+This functions sets a flag that the key needs to be removed. It also sets a flag that it is not synced.<p>
+Remove the key instead of writing it in the key database when doing <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> and related functions.<p>
+This key will be ignored and it is save to delete it afterwards. To be sure that it was removed, check if it needs sync with <a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync()</a>.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Delete in elektra terminology means to free memory, remove means to free permanent storage.</dd></dl>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>You should not change a key's remove status once it belongs to a keyset. See <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> for more information.</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove()</a>, <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>, <a class="el" href="group__kdbhighlevel.html#gf9adbbeb3f49c63fb2f89930445c8060">kdbRemove()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on success <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g995d8b84731673c88c7c01f3fed538b9"></a><!-- doxytag: member="keymeta.c::keySetATime" ref="g995d8b84731673c88c7c01f3fed538b9" args="(Key *key, time_t atime)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetATime           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">time_t&nbsp;</td>
+          <td class="paramname"> <em>atime</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Update the atime information for a key.<p>
+When you do manual sync of keys you might also update the atime to make them indistinguishable.<p>
+It can also be useful if you work with keys not using a keydatabase.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>The Key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>atime</em>&nbsp;</td><td>The new access time for the key </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g6b05da399c3c78904969ef39f191b0eb">keyGetATime()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g9f502ecab8ab43f0b17220fcc95f3fa5"></a><!-- doxytag: member="keymeta.c::keySetCTime" ref="g9f502ecab8ab43f0b17220fcc95f3fa5" args="(Key *key, time_t ctime)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetCTime           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">time_t&nbsp;</td>
+          <td class="paramname"> <em>ctime</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Update the ctime information for a key.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>The Key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>ctime</em>&nbsp;</td><td>The new change metadata time for the key </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g2c213c120cbe02201278ef7fb8cd94be">keyGetCTime()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gae575bd86a628a15ee45baa860522e75"></a><!-- doxytag: member="keymeta.c::keySetDir" ref="gae575bd86a628a15ee45baa860522e75" args="(Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetDir           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set mode so that key will be recognized as directory.<p>
+The function will add all executable bits.<p>
+<ul>
+<li>Mode 0200 will be translated to 0311</li><li>Mode 0400 will be translated to 0711</li><li>Mode 0664 will be translated to 0775</li></ul>
+<p>
+The macro KEY_DEF_DIR (defined to 0111) will be used for that.<p>
+The executable bits show that child keys are allowed and listable. There is no way to have child keys which are not listable for anyone, but it is possible to restrict listing the keys to the owner only.<p>
+<ul>
+<li>Mode 0000 means that it is a key not read or writable to anyone.</li><li>Mode 0111 means that it is a directory not read or writable to anyone. But it is recognized as directory to anyone.</li></ul>
+<p>
+For more about mode see <a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a>.<p>
+It is not possible to access keys below a not executable key. If a key is not writeable and executable <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> will fail to access the keys below. If a key is not readable and executable <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> will fail to access the keys below.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key to set permissions to be recognized as directory. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g9e3d0fb3f7ba906e067727b9155d22e3"></a><!-- doxytag: member="keymeta.c::keySetGID" ref="g9e3d0fb3f7ba906e067727b9155d22e3" args="(Key *key, gid_t gid)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetGID           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">gid_t&nbsp;</td>
+          <td class="paramname"> <em>gid</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the group ID of a key.<p>
+See <a class="el" href="group__keymeta.html#GID">GID</a> for more information about group IDs.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>gid</em>&nbsp;</td><td>is the group ID </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL key </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g46a95e81d7d7f4e3eb59e60e5f3738c0">keyGetGID()</a>, <a class="el" href="group__keymeta.html#gb5f284f5ecd261e0a290095f50ba1af7">keySetUID()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g8803037e35b9da1ce492323a88ff6bc3"></a><!-- doxytag: member="keymeta.c::keySetMode" ref="g8803037e35b9da1ce492323a88ff6bc3" args="(Key *key, mode_t mode)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetMode           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">mode_t&nbsp;</td>
+          <td class="paramname"> <em>mode</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the key mode permissions.<p>
+The mode consists of 9 individual bits for mode permissions. In the following explanation the octal notation with leading zero will be used.<p>
+Default is 0664 (octal) for keys and 0775 for directory keys which used <a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a>.<p>
+The defaults are defined with the macros KEY_DEF_MODE and KEY_DEF_DIR.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>libelektra 0.7.0 only allows 0775 (directory keys) and 0664 (other keys). More will be added later in a sense of the description below.</dd></dl>
+<h2><a class="anchor" name="mode">
+Mode</a></h2>
+0000 is the most restrictive mode. No user might read, write or execute the key.<p>
+Reading the key means to get the value and comment by <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> or all highlevel methods.<p>
+Writing the key means to set the value and comment by <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> or all highlevel methods.<p>
+Execute the key means to make a step deeper in the hierarchy. But you must be able to read the key to be able to list the keys below. See also <a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a> in that context. But you must be able to write the key to be able to add or remove keys below.<p>
+0777 is the most relaxing mode. Every user is allowed to read, write and execute the key, if he is allowed to execute and read all keys below.<p>
+0700 allows every action for the current user, identified by the uid. See <a class="el" href="group__keymeta.html#gcaa5060e67b03f50ae49a3620c54bc46">keyGetUID()</a> and <a class="el" href="group__keymeta.html#gb5f284f5ecd261e0a290095f50ba1af7">keySetUID()</a>.<p>
+To be more specific for the user the single bits can elect the mode for read, write and execute. 0100 only allows executing which gives the information that it is a directory for that user, but not accessable. 0200 only allows reading. This information may be combined to 0300, which allows execute and reading of the directory. Last 0400 decides about the writing permissions.<p>
+The same as above is also valid for the 2 other octal digits. 0070 decides about the group permissions, in that case full access. Groups are identified by the gid. See <a class="el" href="group__keymeta.html#g46a95e81d7d7f4e3eb59e60e5f3738c0">keyGetGID()</a> and <a class="el" href="group__keymeta.html#g9e3d0fb3f7ba906e067727b9155d22e3">keySetGID()</a>. In that example everyone with a different uid, but the gid of the the key, has full access.<p>
+0007 decides about the world permissions. This is taken into account when neighter the uid nor the gid matches. So that example would allow everyone with a different uid and gid of that key gains full access.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key to set mode permissions </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>mode</em>&nbsp;</td><td>the mode permissions </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL key </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#gbc0cec592ce3b77e9bc33dbc8e8f6bdc">keyGetMode()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g481d8997187992fe4bbf288bc8ef4db7"></a><!-- doxytag: member="keymeta.c::keySetMTime" ref="g481d8997187992fe4bbf288bc8ef4db7" args="(Key *key, time_t mtime)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetMTime           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">time_t&nbsp;</td>
+          <td class="paramname"> <em>mtime</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Update the mtime information for a key.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>The Key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>mtime</em>&nbsp;</td><td>The new modification time for the key </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g57689eb5691679071463b777ae786ae9">keyGetMTime()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb92003db4b938594df48807c16766bf7"></a><!-- doxytag: member="keymeta.c::keySetType" ref="gb92003db4b938594df48807c16766bf7" args="(Key *key, type_t newType)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetType           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">type_t&nbsp;</td>
+          <td class="paramname"> <em>newType</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set a new key type.<p>
+This method is usually not needed, unless you are working with more semantic value types, or want to force a specific value type for a key. It is not usually needed because the data type is automatically set when setting the key value.<p>
+See type_t for the type defintion.<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment"><span class="comment">// here we define the new type</span>
+<span class="keyword">enum</span>
+{
+        KEY_TYPE_COLOR=KEY_TYPE_STRING+4
+};
+<span class="comment">// here we make a new key with the type</span>
+Key *k1 = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/sw/oyranos/current/color1"</span>,
+        KEY_VALUE, <span class="stringliteral">"#4B52CA"</span>,
+        KEY_COMMENT, <span class="stringliteral">"a custom color"</span>,
+        KEY_TYPE, KEY_TYPE_COLOR,
+        KEY_END);
+<span class="comment">// lets check if it is really correct type</span>
+<span class="keywordflow">if</span> (<a class="code" href="group__keymeta.html#g4fbbf04b1a2c5928dfcec939972e7347">keyGetType</a>(k1) == KEY_TYPE_COLOR) printf (<span class="stringliteral">"correct type"</span>);
+</pre></div></dd></dl>
+When using type_t::KEY_TYPE_DIR, this method will not set mode permissions to the key. You'll have to set it manually after <a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">keySetType()</a>, calling <a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a> with appropriate permissions. Or use the <a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a>.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g4fbbf04b1a2c5928dfcec939972e7347">keyGetType()</a> <p>
+<a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a> to see that the directory concept is independent of types </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>newType</em>&nbsp;</td><td>contains the new type </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on sucess <p>
+-1 on NULL pointer and when newType &gt;= KEY_TYPE_MAX </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb5f284f5ecd261e0a290095f50ba1af7"></a><!-- doxytag: member="keymeta.c::keySetUID" ref="gb5f284f5ecd261e0a290095f50ba1af7" args="(Key *key, uid_t uid)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keySetUID           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">uid_t&nbsp;</td>
+          <td class="paramname"> <em>uid</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the user ID of a key.<p>
+See <a class="el" href="group__keymeta.html#UID">UID</a> for more information about user IDs.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>uid</em>&nbsp;</td><td>the user ID to set </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL key </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g9e3d0fb3f7ba906e067727b9155d22e3">keySetGID()</a>, <a class="el" href="group__keymeta.html#gcaa5060e67b03f50ae49a3620c54bc46">keyGetUID()</a>, <a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb8189add5e562bdb148675ee595bd95b"></a><!-- doxytag: member="keymeta.c::keyStat" ref="gb8189add5e562bdb148675ee595bd95b" args="(Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyStat           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Only stat a key instead of receiving value, comment and key type.<p>
+Only stat the key in the database when doing <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>. The key may not have any value, comment or key type set.<p>
+It is not possible to revert the action on per-key basis. When you want to remove the flag you have to pass option_t::KDB_O_NOSTAT to the next <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat()</a>, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on succuess <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__keyname.html b/doc/elektra-api/html/group__keyname.html
new file mode 100644 (file)
index 0000000..fde1bf3
--- /dev/null
@@ -0,0 +1,701 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Key :: Name Manipulation Methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Key :: Name Manipulation Methods</h1>Methods to do various operations on Key names.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">const char *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#gbdbcfa51ed8a387e47ead207affa2d2e">keyGetNameSize</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName</a> (const Key *key, char *returnedName, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName</a> (Key *key, const char *newName)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#gb65dc9d43d3ee08d5e936a20ebbddd23">keyGetFullNameSize</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName</a> (const Key *key, char *returnedName, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">const char *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#gaff35e7ca8af5560c47e662ceb9465f5">keyBaseName</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g1a0b76c5d9e5367c7e72211e6c63d43a">keyGetBaseNameSize</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g0992d26bcfca767cb8e77053a483eb64">keyGetBaseName</a> (const Key *key, char *returned, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#ga942091fc4bd5c2699e49ddc50829524">keyAddBaseName</a> (Key *key, const char *baseName)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g6e804bd453f98c28b0ff51430d1df407">keySetBaseName</a> (Key *key, const char *baseName)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">const char *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g4a4561895741ba2ad10acf007c188593">keyGetOwnerSize</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner</a> (const Key *key, char *returned, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner</a> (Key *key, const char *owner)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to do various operations on Key names. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdb.h&gt;</span>
+</pre></div><p>
+These functions make it easier for c programmers to work with key names. Everything here can also be done with keySetName, described in key.<p>
+<dl class="user" compact><dt><b>Rules for Key Names</b></dt><dd></dd></dl>
+When using Elektra to store your application's configuration and state, please keep in mind the following rules:<ul>
+<li>You are not allowed to create keys right under <code>system</code> or <code>user</code>.</li><li>You are not allowed to create folder keys right under <code>system</code> or <code>user</code>. They are reserved for very essential OS subsystems.</li><li>The keys for your application, called say <em>MyApp</em>, should be created under <code>system/sw/MyApp</code> and/or <code>user/sw/MyApp</code>.</li><li>It is suggested to make your application look for default keys under <code>system/sw/MyApp/current</code> and/or <code>user/sw/MyApp/current</code>. This way, from a sysadmin perspective, it will be possible to copy the <code>system/sw/MyApp/current</code> tree to something like <code>system/sw/MyApp/old</code>, and keep system clean and organized.</li><li>\0 must not occur in names.</li><li>/ is the seperator. </li></ul>
+<hr><h2>Function Documentation</h2>
+<a class="anchor" name="ga942091fc4bd5c2699e49ddc50829524"></a><!-- doxytag: member="keyname.c::keyAddBaseName" ref="ga942091fc4bd5c2699e49ddc50829524" args="(Key *key, const char *baseName)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyAddBaseName           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>baseName</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Adds <code>baseName</code> to the current key name.<p>
+Assumes that <code>key</code> is a directory. <code>baseName</code> is appended to it. The function adds <code>'/'</code> if needed while concatenating.<p>
+So if <code>key</code> has name <code>"system/dir1/dir2"</code> and this method is called with <code>baseName</code> <code>"mykey"</code>, the resulting key will have name <code>"system/dir1/dir2/mykey"</code>.<p>
+When baseName is 0 or "" nothing will happen and the size of the name is returned.<p>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>You should not change a keys name once it belongs to a keyset. See <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> for more information.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>baseName</em>&nbsp;</td><td>the string to append to the name </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the size in bytes of the new key name including the ending NULL <p>
+-1 if the key had no name <p>
+-1 on NULL pointers </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g6e804bd453f98c28b0ff51430d1df407">keySetBaseName()</a> <p>
+<a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> to set a new name. </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gaff35e7ca8af5560c47e662ceb9465f5"></a><!-- doxytag: member="keyname.c::keyBaseName" ref="gaff35e7ca8af5560c47e662ceb9465f5" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">const char* keyBaseName           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Returns a pointer to the real internal key name where the <code>basename</code> starts.<p>
+This is a much more efficient version of <a class="el" href="group__keyname.html#g0992d26bcfca767cb8e77053a483eb64">keyGetBaseName()</a> and you should use it if you are responsible enough to not mess up things. The name might change or even point to a wrong place after a <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>. If you need a copy of the basename consider to use <a class="el" href="group__keyname.html#g0992d26bcfca767cb8e77053a483eb64">keyGetBaseName()</a>.<p>
+<a class="el" href="group__keyname.html#gaff35e7ca8af5560c47e662ceb9465f5">keyBaseName()</a> returns "" when there is no keyBaseName. The reason is <div class="fragment"><pre class="fragment">key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName</a>(key,<span class="stringliteral">""</span>);
+<a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(key); <span class="comment">// you would expect "" here</span>
+<a class="code" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName</a>(key,<span class="stringliteral">"user"</span>);
+<a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(key); <span class="comment">// you would expect "" here</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+</pre></div><p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by <a class="el" href="group__keyname.html#gaff35e7ca8af5560c47e662ceb9465f5">keyBaseName()</a> method to set a new value. Use <a class="el" href="group__keyname.html#g6e804bd453f98c28b0ff51430d1df407">keySetBaseName()</a> instead.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the object to obtain the basename from </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to the basename <p>
+"" on null pointer or when the key has no name <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g0992d26bcfca767cb8e77053a483eb64">keyGetBaseName()</a>, <a class="el" href="group__keyname.html#g1a0b76c5d9e5367c7e72211e6c63d43a">keyGetBaseNameSize()</a> <p>
+<a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> to get a pointer to the name <p>
+<a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner()</a> to get a pointer to the owner </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g0992d26bcfca767cb8e77053a483eb64"></a><!-- doxytag: member="keyname.c::keyGetBaseName" ref="g0992d26bcfca767cb8e77053a483eb64" args="(const Key *key, char *returned, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetBaseName           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Calculate the basename of a key name and put it in <code>returned</code> finalizing the string with NULL.<p>
+Some examples:<ul>
+<li>basename of <code>system/some/keyname</code> is <code>keyname</code> </li><li>basename of <code>"user/tmp/some key"</code> is <code>"some key"</code> </li></ul>
+<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key to extract basename from </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>a pre-allocated buffer to store the basename </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>size of the <code>returned</code> buffer </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes copied to <code>returned</code> <p>
+1 on empty name <p>
+-1 on NULL pointers <p>
+-1 when maxSize is 0 or larger than SSIZE_MAX </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#gaff35e7ca8af5560c47e662ceb9465f5">keyBaseName()</a>, <a class="el" href="group__keyname.html#g1a0b76c5d9e5367c7e72211e6c63d43a">keyGetBaseNameSize()</a> <p>
+<a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a>, <a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a>, <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g1a0b76c5d9e5367c7e72211e6c63d43a"></a><!-- doxytag: member="keyname.c::keyGetBaseNameSize" ref="g1a0b76c5d9e5367c7e72211e6c63d43a" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetBaseNameSize           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Calculates number of bytes needed to store basename of <code>key</code>.<p>
+Key names that have only root names (e.g. <code>"system"</code> or <code>"user"</code> or <code>"user:domain"</code> ) does not have basenames, thus the function will return 1 bytes to store "".<p>
+Basenames are denoted as:<ul>
+<li><code>system/some/thing/basename</code> -&gt; <code>basename</code> </li><li><code>user:domain/some/thing/base\/name</code> &gt; <code>base\/name</code> </li></ul>
+<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>size in bytes of <code>key's</code> basename including ending NULL </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#gaff35e7ca8af5560c47e662ceb9465f5">keyBaseName()</a>, <a class="el" href="group__keyname.html#g0992d26bcfca767cb8e77053a483eb64">keyGetBaseName()</a> <p>
+<a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a>, <a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a>, <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gaba1494a5ffc976e0e56c43f4334a23c"></a><!-- doxytag: member="keyname.c::keyGetFullName" ref="gaba1494a5ffc976e0e56c43f4334a23c" args="(const Key *key, char *returnedName, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetFullName           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returnedName</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get key full name, including the user domain name.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written <p>
+1 on empty name <p>
+-1 on NULL pointers <p>
+-1 if maxSize is 0 or larger than SSIZE_MAX </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returnedName</em>&nbsp;</td><td>pre-allocated memory to write the key name </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>maximum number of bytes that will fit in returnedName, including the final NULL </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb65dc9d43d3ee08d5e936a20ebbddd23"></a><!-- doxytag: member="keyname.c::keyGetFullNameSize" ref="gb65dc9d43d3ee08d5e936a20ebbddd23" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetFullNameSize           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Bytes needed to store the key name including user domain and ending NULL.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes needed to store key name including user domain <p>
+1 on empty name <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a>, <a class="el" href="group__keyname.html#gbdbcfa51ed8a387e47ead207affa2d2e">keyGetNameSize()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gb29a850168d9b31c9529e90cf9ab68be"></a><!-- doxytag: member="keyname.c::keyGetName" ref="gb29a850168d9b31c9529e90cf9ab68be" args="(const Key *key, char *returnedName, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetName           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returnedName</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get abbreviated key name (without owner name).<p>
+When there is not enough space to write the name, nothing will be written and -1 will be returned.<p>
+maxSize is limited to SSIZE_MAX. When this value is exceeded -1 will be returned. The reason for that is that any value higher is just a negative return value passed by accident. Of course malloc is not as failure tolerant and will try to allocate.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to <code>returnedName</code> <p>
+1 when only a null was written <p>
+-1 when keyname is longer then maxSize or 0 or any NULL pointer </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returnedName</em>&nbsp;</td><td>pre-allocated memory to write the key name </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>maximum number of bytes that will fit in returnedName, including the final NULL </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#gbdbcfa51ed8a387e47ead207affa2d2e">keyGetNameSize()</a>, <a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a>, <a class="el" href="group__keyname.html#gb65dc9d43d3ee08d5e936a20ebbddd23">keyGetFullNameSize()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gbdbcfa51ed8a387e47ead207affa2d2e"></a><!-- doxytag: member="keyname.c::keyGetNameSize" ref="gbdbcfa51ed8a387e47ead207affa2d2e" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetNameSize           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Bytes needed to store the key name without owner.<p>
+For an empty key name you need one byte to store the ending NULL. For that reason 1 is returned.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes needed, including ending NULL, to store key name without owner <p>
+1 if there is is no key Name <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a>, <a class="el" href="group__keyname.html#gb65dc9d43d3ee08d5e936a20ebbddd23">keyGetFullNameSize()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6d612841c829a638a8fbbbd4a31cc54a"></a><!-- doxytag: member="keyname.c::keyGetOwner" ref="g6d612841c829a638a8fbbbd4a31cc54a" args="(const Key *key, char *returned, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetOwner           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returned</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the owner of the key.<ul>
+<li>Given <code>user:someuser/</code>..... return <code>someuser</code> </li><li>Given <code>user:some.user/</code>.... return <code>some.user</code> </li><li>Given <code>user/</code>.... return the current user</li></ul>
+<p>
+Only <code>user/</code>... keys have a owner. For <code>system/</code>... keys (that doesn't have a key owner) an empty string ("") is returned.<p>
+Although usually the same, the owner of a key is not related to its UID. Owner are related to WHERE the key is stored on disk, while UIDs are related to mode controls of a key.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returned</em>&nbsp;</td><td>a pre-allocated space to store the owner </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>maximum number of bytes that fit returned </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to buffer <p>
+1 if there is no owner <p>
+-1 on NULL pointers <p>
+-1 when maxSize is 0, larger than SSIZE_MAX or too small for ownername </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner()</a>, <a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner()</a>, <a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4a4561895741ba2ad10acf007c188593"></a><!-- doxytag: member="keyname.c::keyGetOwnerSize" ref="g4a4561895741ba2ad10acf007c188593" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetOwnerSize           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the size of the owner of the Key with concluding 0.<p>
+The returned number can be used to allocate a string. 1 will returned on an empty owner to store the concluding 0 on using <a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a>.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">char</span> * buffer;
+buffer = malloc (<a class="code" href="group__keyname.html#g4a4561895741ba2ad10acf007c188593">keyGetOwnerSize</a> (key));
+<span class="comment">// use buffer and keyGetOwnerSize (key) for maxSize</span>
+</pre></div><p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>that -1 might be returned on null pointer, so when you directly allocate afterwards its best to check if you will pass a null pointer before.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes <p>
+1 if there is no owner <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g8e805c726a60da921d3736cda7813513"></a><!-- doxytag: member="keyname.c::keyName" ref="g8e805c726a60da921d3736cda7813513" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">const char* keyName           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Returns a pointer to the abbreviated real internal <code>key</code> name.<p>
+This is a much more efficient version of <a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a> and can use it if you are responsible enough to not mess up things. You are not allowed to change anything in the returned array. The content of that string may change after <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> and similar functions. If you need a copy of the name, consider using <a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a>.<p>
+The name will be without owner, see <a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a> if you need the name with its owner.<p>
+<a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> returns "" when there is no keyName. The reason is <div class="fragment"><pre class="fragment">key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName</a>(key,<span class="stringliteral">""</span>);
+<a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(key); <span class="comment">// you would expect "" here</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+</pre></div><p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> method to set a new value. Use <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> instead.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to the keyname which must not be changed. <p>
+"" when there is no (a empty) keyname <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#gbdbcfa51ed8a387e47ead207affa2d2e">keyGetNameSize()</a> for the string length <p>
+<a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a>, <a class="el" href="group__keyname.html#gb65dc9d43d3ee08d5e936a20ebbddd23">keyGetFullNameSize()</a> to get the full name <p>
+<a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a> as alternative to get a copy <p>
+<a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner()</a> to get a pointer to owner </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gf6485fb8599714b6bbd830cf915ffea5"></a><!-- doxytag: member="keyname.c::keyOwner" ref="gf6485fb8599714b6bbd830cf915ffea5" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">const char* keyOwner           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return a pointer to the real internal <code>key</code> owner.<p>
+This is a much more efficient version of <a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a> and you should use it if you are responsible enough to not mess up things. You are not allowed to modify the returned string in any way. If you need a copy of the string, consider to use <a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a> instead.<p>
+<a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner()</a> returns "" when there is no keyOwner. The reason is <div class="fragment"><pre class="fragment">key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner</a>(key,<span class="stringliteral">""</span>);
+<a class="code" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner</a>(key); <span class="comment">// you would expect "" here</span>
+<a class="code" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner</a>(key,<span class="stringliteral">"system"</span>);
+<a class="code" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner</a>(key); <span class="comment">// you would expect "" here</span>
+</pre></div><p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by <a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner()</a> method to set a new value. Use <a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner()</a> instead.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to internal owner <p>
+"" when there is no (a empty) owner <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g4a4561895741ba2ad10acf007c188593">keyGetOwnerSize()</a> for the size of the string with concluding 0 <p>
+<a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a>, <a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner()</a> <p>
+<a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> for name without owner <p>
+<a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a> for name with owner </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6e804bd453f98c28b0ff51430d1df407"></a><!-- doxytag: member="keyname.c::keySetBaseName" ref="g6e804bd453f98c28b0ff51430d1df407" args="(Key *key, const char *baseName)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keySetBaseName           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>baseName</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Sets <code>baseName</code> as the new basename for <code>key</code>.<p>
+All text after the last <code>'/'</code> in the <code>key</code> keyname is erased and <code>baseName</code> is appended.<p>
+So lets suppose <code>key</code> has name <code>"system/dir1/dir2/key1"</code>. If <code>baseName</code> is <code>"key2"</code>, the resulting key name will be <code>"system/dir1/dir2/key2"</code>. If <code>baseName</code> is empty or NULL, the resulting key name will be <code>"system/dir1/dir2"</code>.<p>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>You should not change a keys name once it belongs to a keyset. See <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> for more information.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>baseName</em>&nbsp;</td><td>the string used to overwrite the basename of the key </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the size in bytes of the new key name <p>
+-1 on NULL pointers </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#ga942091fc4bd5c2699e49ddc50829524">keyAddBaseName()</a> <p>
+<a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> to set a new name </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g7699091610e7f3f43d2949514a4b35d9"></a><!-- doxytag: member="keyname.c::keySetName" ref="g7699091610e7f3f43d2949514a4b35d9" args="(Key *key, const char *newName)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keySetName           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>newName</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set a new name to a key.<p>
+A valid name is of the forms:<ul>
+<li><code>system/something</code> </li><li><code>user/something</code> </li><li><code>user:username/something</code> </li></ul>
+<p>
+The last form has explicitly set the owner, to let the library know in which user folder to save the key. A owner is a user name. If not defined (the second form) current user is calculated and used as default.<p>
+You should always follow the guidelines for key tree structure creation.<p>
+A private copy of the key name will be stored, and the <code>newName</code> parameter can be freed after this call.<p>
+.., . and / will be handled correctly. A valid name will be build out of the (valid) name what you pass, e.g. user///sw/../sw//././MyApp -&gt; user/sw/MyApp<p>
+On invalid names, NULL or "" the name will be "" afterwards.<p>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>You should not change a keys name once it belongs to a keyset. See <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> for more information.</dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>size in bytes of this new key name including ending NULL <p>
+-1 if <code>newName</code> is empty or invalid or any NULL pointer </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>newName</em>&nbsp;</td><td>the new key name </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>, <a class="el" href="group__keyname.html#ga899d9f0251cb98a89761ef112910eca">keySetOwner()</a> <p>
+<a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a>, <a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a>, <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> <p>
+<a class="el" href="group__keyname.html#g6e804bd453f98c28b0ff51430d1df407">keySetBaseName()</a>, <a class="el" href="group__keyname.html#ga942091fc4bd5c2699e49ddc50829524">keyAddBaseName()</a> to manipulate a name </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga899d9f0251cb98a89761ef112910eca"></a><!-- doxytag: member="keyname.c::keySetOwner" ref="ga899d9f0251cb98a89761ef112910eca" args="(Key *key, const char *owner)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keySetOwner           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>owner</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the owner of a key.<p>
+A owner is a name of a system user related to a UID. The owner decides on which location on the disc the key goes.<p>
+A private copy is stored, so the passed parameter can be freed after the call.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>owner</em>&nbsp;</td><td>the owner (or user name) </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually saved including final NULL <p>
+1 when owner is freed (by setting 0 or "") <p>
+-1 on null pointer or memory problems </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#g6d612841c829a638a8fbbbd4a31cc54a">keyGetOwner()</a>, <a class="el" href="group__keyname.html#gaba1494a5ffc976e0e56c43f4334a23c">keyGetFullName()</a> </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__keyset.html b/doc/elektra-api/html/group__keyset.html
new file mode 100644 (file)
index 0000000..c4fa171
--- /dev/null
@@ -0,0 +1,937 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: KeySet :: Class Methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>KeySet :: Class Methods</h1>Methods to manipulate KeySets.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KeySet *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a> (size_t alloc,...)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">KeySet *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gc59e4b328245463f1451f68d5106151c">ksDup</a> (const KeySet *source)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gba1f1dbea191f4d7e7eb3e4296ae7d5e">ksCopy</a> (KeySet *dest, const KeySet *source)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">void&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort</a> (KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g7474ad6b0a0fa969dbdf267ba5770eee">ksGetSize</a> (const KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a> (KeySet *ks, Key *toAppend)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend</a> (KeySet *ks, const KeySet *toAppend)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop</a> (KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a> (KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext</a> (KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent</a> (const KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#ge7dbf3aef70e67b5328475eb3d1f92f5">ksHead</a> (const KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gdca442c4ab43cf532b15091d7711559e">ksTail</a> (const KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">cursor_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor</a> (const KeySet *ks)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gd94c9ffaa3e8034564c0712fd407c345">ksSetCursor</a> (KeySet *ks, cursor_t cursor)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup</a> (KeySet *ks, Key *key, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">Key *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName</a> (KeySet *ks, const char *name, option_t options)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to manipulate KeySets. 
+<p>
+A KeySet is a unsorted set of keys.<p>
+Terminate with ksNew(0) or ksNew(20, ..., KS_END) This is because there is a list of Key* required and KS_END has the length of (Key*).<p>
+It can be implemented in various ways like a linked list or with a dynamically allocated array.<p>
+With <a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a> you can create a new KeySet.<p>
+You can add keys with <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a> in the keyset. <a class="el" href="group__keyset.html#g7474ad6b0a0fa969dbdf267ba5770eee">ksGetSize()</a> tells you the current size of the keyset.<p>
+With <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a> and <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> you can navigate through the keyset. Don't expect any particular order, but it is assured that you will get every key of the set.<p>
+KeySets have an <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">internal cursor </a>. This is used for <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> and <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>.<p>
+KeySet has a fundamental meaning inside elektra. It makes it possible to get and store many keys at once inside the database. In addition to that the class can be used as high level datastructure in applications. With <a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName()</a> it is possible to fetch easily specific keys out of the list of keys.<p>
+You can easily create and iterate keys: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdb.h&gt;</span>
+
+<span class="comment">// create a new keyset with 3 keys</span>
+<span class="comment">// with a hint that about 20 keys will be inside</span>
+KeySet *myConfig = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(20,
+        <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/name1"</span>, 0),
+        <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/name2"</span>, 0),
+        <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/name3"</span>, 0),
+        KS_END);
+<span class="comment">// append a key in the keyset</span>
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(myConfig, <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(<span class="stringliteral">"user/name4"</span>, 0));
+
+Key *current;
+<a class="code" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a>(myConfig);
+<span class="keywordflow">while</span> ((current=<a class="code" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext</a>(myConfig))!=0) {
+        printf(<span class="stringliteral">"Key name is %s.\n"</span>, <a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a> (current));
+}
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (myConfig); <span class="comment">// delete keyset and all keys appended</span>
+</pre></div> <hr><h2>Function Documentation</h2>
+<a class="anchor" name="g21eb9c3a14a604ee3a8bdc779232e7b7"></a><!-- doxytag: member="keyset.c::ksAppend" ref="g21eb9c3a14a604ee3a8bdc779232e7b7" args="(KeySet *ks, const KeySet *toAppend)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t ksAppend           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>toAppend</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Append all <code>toAppend</code> contained keys to the end of the <code>ks</code>.<p>
+<code>toAppend</code> KeySet will be left unchanged.<p>
+Makes the keyset dirty, see <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the size of the KeySet after transfer <p>
+-1 on NULL pointers </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the KeySet that will receive the keys </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>toAppend</em>&nbsp;</td><td>the KeySet that provides the keys that will be transfered </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a>, ksInsert(), ksInsertKeys() </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga5a1d467a4d71041edce68ea7748ce45"></a><!-- doxytag: member="keyset.c::ksAppendKey" ref="ga5a1d467a4d71041edce68ea7748ce45" args="(KeySet *ks, Key *toAppend)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t ksAppendKey           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>toAppend</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Appends a Key to the end of <code>ks</code>.<p>
+A pointer to the key will be stored, and not a private copy. So a future <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a> on <code>ks</code> may <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> the <code>toAppend</code> object, see <a class="el" href="group__key.html#g4aabc4272506dd63161db2bbb42de8ae">keyGetRef()</a>.<p>
+The reference counter of the key will be incremented, and thus toAppend is not const.<p>
+The KeySet internal cursor is not moved.<p>
+Makes the keyset dirty, see <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the size of the KeySet after insertion <p>
+-1 on NULL pointers </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>KeySet that will receive the key </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>toAppend</em>&nbsp;</td><td>Key that will be appended to ks </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>ksInsert(), ksInsertKeys(), <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a>, <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a>, <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a> <p>
+<a class="el" href="group__key.html#g6970a6f254d67af7e39f8e469bb162f1">keyIncRef()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gba1f1dbea191f4d7e7eb3e4296ae7d5e"></a><!-- doxytag: member="keyset.c::ksCopy" ref="gba1f1dbea191f4d7e7eb3e4296ae7d5e" args="(KeySet *dest, const KeySet *source)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksCopy           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>dest</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>source</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Copy a keyset.<p>
+Most often you may want a duplicate of a keyset, see <a class="el" href="group__keyset.html#gc59e4b328245463f1451f68d5106151c">ksDup()</a> or append keys, see <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a>. But in some situations you need to copy a keyset to a existing keyset, for that this function exists.<p>
+You can also use it to clear a keyset when you pass a NULL pointer as <code>source</code>.<p>
+Note that all keys in <code>dest</code> will be deleted. Afterwards the content of the source will be added to the destination and the <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> is set properly in <code>dest</code>.<p>
+A flat copy is made, so the keys will not be duplicated, but there reference counter is updated, so both keysets need to be <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a>.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> f (KeySet *ks)
+{
+        KeySet *c = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a> (20, ..., KS_END);
+        <span class="comment">// c receives keys</span>
+        <a class="code" href="group__keyset.html#gba1f1dbea191f4d7e7eb3e4296ae7d5e">ksCopy</a> (ks, c); <span class="comment">// pass the keyset to the caller</span>
+
+        <a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (c);
+}       <span class="comment">// caller needs to ksDel (ks)</span>
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>source</em>&nbsp;</td><td>has to be an initialized source KeySet or NULL </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>dest</em>&nbsp;</td><td>has to be an initialized KeySet where to write the keys </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on success <p>
+0 if dest was cleared successfully (source is NULL) <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a>, <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a>, <a class="el" href="group__keyset.html#gc59e4b328245463f1451f68d5106151c">ksDup()</a> <p>
+<a class="el" href="group__key.html#g6a12cbbe656a1ad9f41b8c681d7a2f92">keyCopy()</a> for copying keys </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4287b9416912c5f2ab9c195cb74fb094"></a><!-- doxytag: member="keyset.c::ksCurrent" ref="g4287b9416912c5f2ab9c195cb74fb094" args="(const KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksCurrent           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the current Key.<p>
+The pointer is NULL if you reached the end or after <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>You must not delete the key or change the key, use <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a> if you want to delete it.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>pointer to the Key pointed by <code>ks's</code> cursor <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a>, <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a> <p>
+kdbMonitorKeys() for a usage example </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g27e5c16473b02a422238c8d970db7ac8"></a><!-- doxytag: member="keyset.c::ksDel" ref="g27e5c16473b02a422238c8d970db7ac8" args="(KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksDel           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+A destructor for KeySet objects.<p>
+Cleans all internal dynamic attributes, decrement all reference pointers to all keys and then <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> all contained Keys, and free()s the release the KeySet object memory (that was previously allocated by <a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a>).<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 when the keyset was freed <p>
+-1 on null pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gc59e4b328245463f1451f68d5106151c"></a><!-- doxytag: member="keyset.c::ksDup" ref="gc59e4b328245463f1451f68d5106151c" args="(const KeySet *source)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KeySet* ksDup           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>source</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return a duplicate of a keyset.<p>
+Objects created with <a class="el" href="group__keyset.html#gc59e4b328245463f1451f68d5106151c">ksDup()</a> must be destroyed with <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a>.<p>
+Memory will be allocated as needed for dynamic properties, so you need to <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a> the returned pointer.<p>
+A flat copy is made, so the keys will not be duplicated, but there reference counter is updated, so both keysets need <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a>.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>source</em>&nbsp;</td><td>has to be an initializised source KeySet </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a flat copy of source on success <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a>, <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a> <p>
+<a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a> for <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> duplication </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gffe507ab9281c322eb16c3e992075d29"></a><!-- doxytag: member="keyset.c::ksGetCursor" ref="gffe507ab9281c322eb16c3e992075d29" args="(const KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">cursor_t ksGetCursor           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the KeySet internal cursor.<p>
+Use it to get the cursor of the actual position.<p>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>Cursors are getting invalid when the key was <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a>ed or <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> with KDB_O_POP was used.</dd></dl>
+<h2><a class="anchor" name="readahead">
+Read ahead</a></h2>
+With the cursors it is possible to read ahead in a keyset:<p>
+<div class="fragment"><pre class="fragment">cursor_t jump;
+<a class="code" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a> (ks);
+<span class="keywordflow">while</span> ((key = keyNext (ks))!=0)
+{
+        <span class="comment">// now mark this key</span>
+        jump = <a class="code" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor</a>(ks);
+
+        <span class="comment">//code..</span>
+        keyNext (ks); <span class="comment">// now browse on</span>
+        <span class="comment">// use ksCurrent(ks) to check the keys</span>
+        <span class="comment">//code..</span>
+
+        <span class="comment">// jump back to the position marked before</span>
+        <a class="code" href="group__keyset.html#gd94c9ffaa3e8034564c0712fd407c345">ksSetCursor</a>(ks, jump);
+}
+</pre></div><h2><a class="anchor" name="restore">
+Restoring state</a></h2>
+It can also be used to restore the state of a keyset in a function<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> f (KeySet *ks)
+{
+        cursor_t state = <a class="code" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor</a>(ks);
+
+        <span class="comment">// work with keyset</span>
+
+        <span class="comment">// now bring the keyset to the state before</span>
+        <a class="code" href="group__keyset.html#gd94c9ffaa3e8034564c0712fd407c345">ksSetCursor</a> (ks, state);
+}
+</pre></div><p>
+It is of course possible to make the KeySet const and cast its const away to set the cursor. Another way to achieve the same is to <a class="el" href="group__keyset.html#gc59e4b328245463f1451f68d5106151c">ksDup()</a> the keyset, but it is not as efficient.<p>
+An invalid cursor will be returned directly after <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>. When you set an invalid cursor <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> is 0 and <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> == <a class="el" href="group__keyset.html#ge7dbf3aef70e67b5328475eb3d1f92f5">ksHead()</a>.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Only use a cursor for the same keyset which it was made for.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a valid cursor on success <p>
+an invalid cursor on NULL pointer or after <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a> </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a>, <a class="el" href="group__keyset.html#gd94c9ffaa3e8034564c0712fd407c345">ksSetCursor()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g7474ad6b0a0fa969dbdf267ba5770eee"></a><!-- doxytag: member="keyset.c::ksGetSize" ref="g7474ad6b0a0fa969dbdf267ba5770eee" args="(const KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t ksGetSize           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the number of keys that <code>ks</code> contains.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of keys that <code>ks</code> contains. <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>ksNew(0), <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ge7dbf3aef70e67b5328475eb3d1f92f5"></a><!-- doxytag: member="keyset.c::ksHead" ref="ge7dbf3aef70e67b5328475eb3d1f92f5" args="(const KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksHead           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the first key in the KeySet.<p>
+The KeySets cursor will not be effected.<p>
+If <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a>==ksHead() you know you are on the first key.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the first Key of a keyset <p>
+0 on NULL pointer or empty keyset </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#gdca442c4ab43cf532b15091d7711559e">ksTail()</a> for the last <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> <p>
+<a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>, <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> and <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> for iterating over the <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga34fc43a081e6b01e4120daa6c112004"></a><!-- doxytag: member="keyset.c::ksLookup" ref="ga34fc43a081e6b01e4120daa6c112004" args="(KeySet *ks, Key *key, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksLookup           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Look for a Key contained in <code>ks</code> that matches the name of the <code>key</code>.<h2><a class="anchor" name="Introduction">
+Introduction</a></h2>
+<code><a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a></code> is designed to let you work with entirely pre-loaded KeySets, so instead of <a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a>, key by key, the idea is to fully <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> for your application root key and process it all at once with <code><a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a></code>.<p>
+This function is very efficient by using binary search. Together with <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> which can you load the whole configuration with only some communication to backends you can write very effective but short code for configuration.<h2><a class="anchor" name="Usage">
+Usage</a></h2>
+If found, <code>ks</code> internal cursor will be positioned in the matched key (also accessible by <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a>), and a pointer to the Key is returned. If not found, <code>ks</code> internal cursor will not move, and a NULL pointer is returned.<p>
+Cascading is done if the first character is a /. This leads to ignoring the prefix like system/ and user/. <div class="fragment"><pre class="fragment">        <span class="keywordflow">if</span> (<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(handle, <span class="stringliteral">"user/myapp"</span>, myConfig, 0 ) == -1)
+                ErrorHandler (<span class="stringliteral">"Could not get Keys"</span>);
+
+        <span class="keywordflow">if</span> (<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(handle, <span class="stringliteral">"system/myapp"</span>, myConfig, 0 ) == -1)
+                ErrorHandler (<span class="stringliteral">"Could not get Keys"</span>);
+
+        <span class="keywordflow">if</span> ((myKey = <a class="code" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup</a>(myConfig, key, 0)) == NULL)
+                ErrorHandler (<span class="stringliteral">"Could not Lookup Key"</span>);
+</pre></div><p>
+This is the way multi user Programs should get there configuration and search after the values. It is guaranteed that more namespaces can be added easily and that all values can be set by admin and user.<h3><a class="anchor" name="KDB_O_NOALL">
+KDB_O_NOALL</a></h3>
+When KDB_O_NOALL is set the keyset will be only searched from <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> to <a class="el" href="group__keyset.html#gdca442c4ab43cf532b15091d7711559e">ksTail()</a>. You need to <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a> the keyset yourself. <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> is always set properly after searching a key, so you can go on searching another key after the found key.<p>
+When KDB_O_NOALL is not set the cursor will stay untouched and all keys are considered. A much more efficient binary search will be used then.<p>
+So if you change keys, e.g. rename (<a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>) or remove (<a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a>) them make sure to sort the keyset with <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>. When the keyset is dirty, see ksNeedSort() it will be sorted automatically when needed.<h3><a class="anchor" name="KDB_O_POP">
+KDB_O_POP</a></h3>
+When KDB_O_POP is set the key which was found will be <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a>ed. <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> will not be changed, only iff <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> is the searched key, then the keyset will be <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>ed.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that <a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a> keys won't be found after the first time the keyset is resorted. Lookup automatically sorts the keyset, if needed, but it can't find it out when only keys are changed, not the keyset.<p>
+Like in <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a> the popped key always needs to be <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> afterwards, even if it is appended to another keyset.</dd></dl>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>All cursors on the keyset will be invalid iff you use KDB_O_POP, so don't use this if you rely on a cursor, see <a class="el" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor()</a>.</dd></dl>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Never use <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> with KDB_O_POP and <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a> or <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a> together in a loop. Otherwise <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> will need to resort the keyset every iteration and spend 99.96% of the time in <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> (benchmarked with above 500k iterations).</dd></dl>
+You can solve this problem by using KDB_O_NOALL, risking you have to iterate n^2 instead of n.<p>
+The more elegant way is to separate the keyset you use for <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> and <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a>: <div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> f(KeySet *iterator, KeySet *lookup)
+{
+        KeySet *append = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a> (<a class="code" href="group__keyset.html#g7474ad6b0a0fa969dbdf267ba5770eee">ksGetSize</a>(lookup), KS_END);
+        Key *key;
+        Key *current;
+
+        <a class="code" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a>(iterator);
+        <span class="keywordflow">while</span> (current=<a class="code" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext</a>(iterator))
+        {
+                key = <a class="code" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup</a> (lookup, current, KDB_O_POP);
+                <span class="comment">// do something...</span>
+                <a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(append, key); <span class="comment">// now append it to append, not lookup!</span>
+                <a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a> (key); <span class="comment">// make sure to ALWAYS delete poped keys.</span>
+        }
+        <a class="code" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend</a>(lookup, append);
+        <span class="comment">// now lookup needs to be sorted only once, append never</span>
+        <a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (append);
+}
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>where to look for </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object you are looking for </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>some <code>KDB_O_*</code> option bits:<ul>
+<li><code>KDB_O_NOCASE</code> <br>
+ Lookup ignoring case.</li><li><code>KDB_O_WITHOWNER</code> <br>
+ Also consider correct owner.</li><li><code>KDB_O_NOALL</code> <br>
+ Only search from <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> to end of keyset, see above text.</li><li><code>KDB_O_POP</code> <br>
+ Pop the key which was found.</li><li><code>KDB_O_SORT</code> <br>
+ Force sorting before searching, see <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>. Together with KDB_O_NOALL the search will start from beginning. </li></ul>
+</td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>pointer to the Key found, 0 otherwise <p>
+0 on NULL pointers </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName()</a> to search by a name given by a string <p>
+<a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a>, <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>, <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> for iterating over a <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> <p>
+<a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> to understand how <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> sort themself </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gd2e30fb6d4739d917c5abb2ac2f9c1a1"></a><!-- doxytag: member="keyset.c::ksLookupByName" ref="gd2e30fb6d4739d917c5abb2ac2f9c1a1" args="(KeySet *ks, const char *name, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksLookupByName           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>name</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Look for a Key contained in <code>ks</code> that matches <code>name</code>.<p>
+<code><a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName()</a></code> is designed to let you work with entirely pre-loaded KeySets, so instead of <a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a>, key by key, the idea is to fully <a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName()</a> for your application root key and process it all at once with <code><a class="el" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName()</a></code>.<p>
+This function is very efficient by using binary search. Together with <a class="el" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName()</a> which can you load the whole configuration with only some communication to backends you can write very effective but short code for configuration.<p>
+If found, <code>ks</code> internal cursor will be positioned in the matched key (also accessible by <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a>), and a pointer to the Key is returned. If not found, <code>ks</code> internal cursor will not move, and a NULL pointer is returned.<h2><a class="anchor" name="cascading">
+Cascading</a></h2>
+Cascading is done if the first character is a /. This leads to ignoring the prefix like system/ and user/. <div class="fragment"><pre class="fragment">        <span class="keywordflow">if</span> (<a class="code" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName</a>(handle, <span class="stringliteral">"/sw/myapp/current"</span>, myConfig, 0 ) == -1)
+                ErrorHandler (<span class="stringliteral">"Could not get Keys"</span>);
+
+        <span class="keywordflow">if</span> ((myKey = <a class="code" href="group__keyset.html#gd2e30fb6d4739d917c5abb2ac2f9c1a1">ksLookupByName</a> (myConfig, <span class="stringliteral">"/myapp/current/key"</span>, 0)) == NULL)
+                ErrorHandler (<span class="stringliteral">"Could not Lookup Key"</span>);
+</pre></div><p>
+This is the way multi user Programs should get there configuration and search after the values. It is guaranteed that more namespaces can be added easily and that all values can be set by admin and user.<h2><a class="anchor" name="fullsearch">
+Full Search</a></h2>
+When KDB_O_NOALL is set the keyset will be only searched from <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> to <a class="el" href="group__keyset.html#gdca442c4ab43cf532b15091d7711559e">ksTail()</a>. You need to <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a> the keyset yourself. <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> is always set properly after searching a key, so you can go on searching another key after the found key.<p>
+When KDB_O_NOALL is not set the cursor will stay untouched and all keys are considered. A much more efficient binary search will be used then.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>where to look for </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>name</em>&nbsp;</td><td>key name you are looking for </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>some <code>KDB_O_*</code> option bits:<ul>
+<li><code>KDB_O_NOCASE</code> <br>
+ Lookup ignoring case.</li><li><code>KDB_O_WITHOWNER</code> <br>
+ Also consider correct owner.</li><li><code>KDB_O_NOALL</code> <br>
+ Only search from <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> to end of keyset, see above text.</li><li><code>KDB_O_POP</code> <br>
+ Pop the key which was found.</li><li><code>KDB_O_SORT</code> <br>
+ Force sorting before searching, see <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a>. Together with KDB_O_NOALL the search will start from beginning.</li></ul>
+</td></tr>
+  </table>
+</dl>
+Currently no options supported. <dl class="return" compact><dt><b>Returns:</b></dt><dd>pointer to the Key found, 0 otherwise <p>
+0 on NULL pointers </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>keyCompare() for very powerfull Key lookups in KeySets <p>
+<a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a>, <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>, <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g671e1aaee3ae9dc13b4834a4ddbd2c3c"></a><!-- doxytag: member="keyset.c::ksNew" ref="g671e1aaee3ae9dc13b4834a4ddbd2c3c" args="(size_t alloc,...)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">KeySet* ksNew           </td>
+          <td>(</td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>alloc</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">&nbsp;</td>
+          <td class="paramname"> <em>...</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Allocate, initialize and return a new KeySet object.<p>
+Objects created with <a class="el" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew()</a> must be destroyed with <a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a>.<p>
+You can use a various long list of parameters to preload the keyset with a list of keys. Either your first and only parameter is 0 or your last parameter must be KEY_END.<p>
+For most uses <div class="fragment"><pre class="fragment">KeySet *keys = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+<span class="comment">// work with it</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (keys);
+</pre></div> goes ok, the alloc size will be 16, defined in kdbprivate.h. The alloc size will be doubled whenever size reaches alloc size, so it also performs out large keysets.<p>
+But if you have any clue how large your keyset may be you should read the next statements.<p>
+If you want a keyset with length 15 (because you know of your application that you normally need about 12 up to 14 keys), use: <div class="fragment"><pre class="fragment">KeySet * keys = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a> (15, KS_END);
+<span class="comment">// work with it</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (keys);
+</pre></div><p>
+If you start having 3 keys, and your application needs approximately 200-500 keys, you can use: <div class="fragment"><pre class="fragment">KeySet * config = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a> (500,
+        <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/sw/app/fixedConfiguration/key1"</span>, KEY_SWITCH_VALUE, <span class="stringliteral">"value1"</span>, 0),
+        <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/sw/app/fixedConfiguration/key2"</span>, KEY_SWITCH_VALUE, <span class="stringliteral">"value2"</span>, 0),
+        <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/sw/app/fixedConfiguration/key3"</span>, KEY_SWITCH_VALUE, <span class="stringliteral">"value3"</span>, 0),
+        KS_END); <span class="comment">// don't forget the KS_END at the end!</span>
+<span class="comment">// work with it</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (config);
+</pre></div> Alloc size is 500, the size of the keyset will be 3 after ksNew. This means the keyset will reallocate when appending more than 497 keys.<p>
+The main benefit of taking a list of variant length parameters is to be able to have one C-Statement for any possible KeySet.<p>
+Due to ABI compatibility, the <code>KeySet</code> structure is only declared in kdb.h, and not defined. So you can only declare <code>pointers</code> to <code>KeySets</code> in your program. See <a href="http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135">http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135</a><p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel()</a> to free the <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> afterwards <p>
+<a class="el" href="group__keyset.html#gc59e4b328245463f1451f68d5106151c">ksDup()</a> to duplicate an existing <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>alloc</em>&nbsp;</td><td>gives a hint for the size how many Keys may be stored initially </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a ready to use KeySet object <p>
+0 on memory error </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g317321c9065b5a4b3e33fe1c399bcec9"></a><!-- doxytag: member="keyset.c::ksNext" ref="g317321c9065b5a4b3e33fe1c399bcec9" args="(KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksNext           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Returns the next Key in a KeySet.<p>
+KeySets have an internal cursor that can be reset with <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>. Every time <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> is called the cursor is incremented and the new current Key is returned.<p>
+You'll get a NULL pointer if the key after the end of the KeySet was reached. It will set the cursor to the beginning of the KeySet and the next time the first key is returned.<p>
+The <code>ks</code> internal cursor will be changed, so it is not const.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>You must not delete or change the key, use <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a> if you want to delete it.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the new current Key <p>
+0 when the end is reached <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>, <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ge42530b04defb772059de0600159cf69"></a><!-- doxytag: member="keyset.c::ksPop" ref="ge42530b04defb772059de0600159cf69" args="(KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksPop           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Remove and return the last key of <code>ks</code>.<p>
+The reference counter will be decremented by one.<p>
+The KeySets cursor will not be effected if it did not point to the popped key.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>You need to <a class="el" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel()</a> the key afterwards, if you don't append it to another keyset. It has the same semantics like a key allocated with <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> or <a class="el" href="group__key.html#ge6ec6a60cc4b8c1463fa08623d056ce3">keyDup()</a>.</dd></dl>
+<div class="fragment"><pre class="fragment">ks1=<a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+ks2=<a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+
+k1=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0); <span class="comment">// ref counter 0</span>
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks1, k1); <span class="comment">// ref counter 1</span>
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks2, k1); <span class="comment">// ref counter 2</span>
+
+k1=<a class="code" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop</a> (ks1); <span class="comment">// ref counter 1</span>
+k1=<a class="code" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop</a> (ks2); <span class="comment">// ref counter 0, like after keyNew()</span>
+
+<a class="code" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey</a>(ks1, k1); <span class="comment">// ref counter 1</span>
+
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (ks1); <span class="comment">// key is deleted too</span>
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (ks2);
+ *
+</pre></div><p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the last key of <code>ks</code> <p>
+NULL if <code>ks</code> is empty or on NULL pointer </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>KeySet to work with </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a>, <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a> <p>
+commandList() for an example </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gbe793ff51f1728e3429c84a8a9086b70"></a><!-- doxytag: member="keyset.c::ksRewind" ref="gbe793ff51f1728e3429c84a8a9086b70" args="(KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksRewind           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Rewinds the KeySet internal cursor.<p>
+Use it to set the cursor to the beginning of the KeySet. <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> will then always return NULL afterwards. So you want to <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> first.<p>
+<div class="fragment"><pre class="fragment"><a class="code" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a> (ks);
+<span class="keywordflow">while</span> ((key = keyNext (ks))!=0) {}
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 on success <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a>, <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gd94c9ffaa3e8034564c0712fd407c345"></a><!-- doxytag: member="keyset.c::ksSetCursor" ref="gd94c9ffaa3e8034564c0712fd407c345" args="(KeySet *ks, cursor_t cursor)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksSetCursor           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">cursor_t&nbsp;</td>
+          <td class="paramname"> <em>cursor</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the KeySet internal cursor.<p>
+Use it to set the cursor to a stored position. <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> will then be the position which you got with.<p>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>Cursors may get invalid when the key was <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a>ed or <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> was used together with KDB_O_POP.</dd></dl>
+<div class="fragment"><pre class="fragment">cursor_t cursor;
+..
+<span class="comment">// key now in any position here</span>
+cursor = <a class="code" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor</a> (ks);
+<span class="keywordflow">while</span> ((key = keyNext (ks))!=0) {}
+<a class="code" href="group__keyset.html#gd94c9ffaa3e8034564c0712fd407c345">ksSetCursor</a> (ks, cursor); <span class="comment">// reset state</span>
+<a class="code" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent</a>(ks); <span class="comment">// in same position as before</span>
+</pre></div><p>
+An invalid cursor will set the keyset to its beginning like <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>. When you set an invalid cursor <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> is 0 and <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> == <a class="el" href="group__keyset.html#ge7dbf3aef70e67b5328475eb3d1f92f5">ksHead()</a>.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>cursor</em>&nbsp;</td><td>the cursor to use </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>0 when the keyset is <a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>ed <p>
+1 otherwise <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a>, <a class="el" href="group__keyset.html#gffe507ab9281c322eb16c3e992075d29">ksGetCursor()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g023554d395ccf2319a3807b8b5d2530c"></a><!-- doxytag: member="keyset.c::ksSort" ref="g023554d395ccf2319a3807b8b5d2530c" args="(KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">void ksSort           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Sorts a KeySet alphabetically by Key name.<p>
+You need <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> only in few cases directly.<h2><a class="anchor" name="sortlookup">
+Don't need to sort before using ksLookup</a></h2>
+You don't need it if you just just <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and subsequent <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a>. <div class="fragment"><pre class="fragment">KeySet *ks = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(h, ks, k, 0);
+<span class="comment">// ksPop(), ksAppend() and ksAppendKey() allowed here</span>
+<a class="code" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup</a>(ks, s, 0); <span class="comment">// you dont need to sort ks</span>
+</pre></div><p>
+This is because the KeySet tracks if it needs to be sorted and <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> will sort when needed.<h2><a class="anchor" name="sortiterate">
+Sort when iterating</a></h2>
+Before you iterate over a keyset you have to sort it, if you need it in a sorted way.<p>
+To achieve that you can pass option_t::KDB_O_SORT to <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>. Then you will receive a already sorted keyset over which you can iterate. <div class="fragment"><pre class="fragment">KeySet *ks = <a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+<a class="code" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet</a>(h, ks, k, KDB_O_SORT);
+<span class="comment">// no changes to keyset allowed</span>
+<a class="code" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a>(ks);
+<span class="comment">// now you can iterate over a sorted keyset</span>
+</pre></div><p>
+Its of course also possible to use <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> once, because it will sort the keyset too.<h2><a class="anchor" name="sortkey">
+Sort when changing key</a></h2>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>You should not use <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a> or <a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a> when a key belongs to a keyset. When you are doing this, you always need to <code>manually</code> sort <code>all</code> keysets where the key was before using <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> (otherwise <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> won't find that keys), <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> or <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> methods.</dd></dl>
+When you change a key's name or its remove status the order which was previously correctly is then wrong. The keyset does not recognize this, so the programmer has to take care that <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> is called before any operation which needs a sorted keyset (which are all <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a>, all <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and all <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> functions).<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>You can remember that easily that all functions which get options require one of the following:<ul>
+<li>that you did not manipulate a keys name or a remove status</li><li>that you pass KDB_O_SORT when you know that you manipulated at least one key</li><li>that you <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> yourself after manipulating keys</li></ul>
+</dd></dl>
+<h2><a class="anchor" name="dirty">
+Dirty KeySet</a></h2>
+When you use <a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a>, <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a>, <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a> the keyset is dirty afterwards, which means that it needs to be sorted. This is done automatically using a <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> method and in ksGet() or ksSet() (All methods which accept options).<p>
+It won't be done if you just iterate over the keyset, so you might use a <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> or <a class="el" href="group__keyset.html#g023554d395ccf2319a3807b8b5d2530c">ksSort()</a> first. <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> will be more efficient in that case, because it will only sort when needed. Don't pass KDB_O_NOALL (it will deactivate the sorting feature), see <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> for more information.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>KeySet to be sorted </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>, <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>, <a class="el" href="group__keyset.html#ga34fc43a081e6b01e4120daa6c112004">ksLookup()</a> for some functions which may need sorting before they are called. (All functions which take options as arguments) <p>
+<a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#g6e804bd453f98c28b0ff51430d1df407">keySetBaseName()</a>, <a class="el" href="group__keyname.html#ga942091fc4bd5c2699e49ddc50829524">keyAddBaseName()</a> and <a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a> for all methods which change the sorting state where the <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> can't track the change. <p>
+<a class="el" href="group__keyset.html#g21eb9c3a14a604ee3a8bdc779232e7b7">ksAppend()</a>, <a class="el" href="group__keyset.html#ga5a1d467a4d71041edce68ea7748ce45">ksAppendKey()</a>, <a class="el" href="group__keyset.html#ge42530b04defb772059de0600159cf69">ksPop()</a> for all methods which make a <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> dirty. </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gdca442c4ab43cf532b15091d7711559e"></a><!-- doxytag: member="keyset.c::ksTail" ref="gdca442c4ab43cf532b15091d7711559e" args="(const KeySet *ks)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">Key* ksTail           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return the last key in the KeySet.<p>
+The KeySets cursor will not be effected.<p>
+If <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a>==ksTail() you know you are on the last key. <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> will return a NULL pointer afterwards.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the last Key of a keyset <p>
+0 on NULL pointer or empty keyset </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyset.html#ge7dbf3aef70e67b5328475eb3d1f92f5">ksHead()</a> for the first <a class="el" href="group__key.html" title="Key construction and initialization methods.">Key :: Basic Methods</a> <p>
+<a class="el" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind()</a>, <a class="el" href="group__keyset.html#g4287b9416912c5f2ab9c195cb74fb094">ksCurrent()</a> and <a class="el" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext()</a> for iterating over the <a class="el" href="group__keyset.html" title="Methods to manipulate KeySets.">KeySet :: Class Methods</a> </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__keytest.html b/doc/elektra-api/html/group__keytest.html
new file mode 100644 (file)
index 0000000..3daf102
--- /dev/null
@@ -0,0 +1,443 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Key :: Methods for Making Tests</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Key :: Methods for Making Tests</h1>Methods to do various tests on Keys.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#g3908b6511648a950f37cd0005bfea5d5">keyNeedStat</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#gf247df0de7aca04b32ef80e39ef12950">keyNeedSync</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#gae91159815480fbb3b3d9d7fa85e77b9">keyNeedRemove</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#gfe49cfb61c2accb3073131c23a56fb14">keyIsSystem</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#g373acc20c6209357045891f4b0c70041">keyIsUser</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#g03332b5d97c76a4fd2640aca4762b8df">keyIsBelow</a> (const Key *key, const Key *check)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#g4f175aafd98948ce6c774f3bd92b72ca">keyIsDirectBelow</a> (const Key *key, const Key *check)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#ga25f699f592031c1a0abc1504d14e13e">keyIsInactive</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#gc0a10c602d52a35f81347e8a32312017">keyIsDir</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#g9526b371087564e43e3dff8ad0dac949">keyIsBinary</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keytest.html#gea7670778abd07fee0fe8ac12a149190">keyIsString</a> (const Key *key)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to do various tests on Keys. 
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdb.h&gt;</span>
+</pre></div> <hr><h2>Function Documentation</h2>
+<a class="anchor" name="g03332b5d97c76a4fd2640aca4762b8df"></a><!-- doxytag: member="keytest.c::keyIsBelow" ref="g03332b5d97c76a4fd2640aca4762b8df" args="(const Key *key, const Key *check)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsBelow           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>check</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check if the key check is below the key key or not.<p>
+<div class="fragment"><pre class="fragment">Example:
+key user/sw/app
+check user/sw/app/key
+
+returns <span class="keyword">true</span> because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+returns also <span class="keyword">true</span> because check is indirect below key
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>check</em>&nbsp;</td><td>the key to find the relative position of </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if check is below key <p>
+0 if it is not below or if it is the same key </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a>, <a class="el" href="group__keytest.html#g4f175aafd98948ce6c774f3bd92b72ca">keyIsDirectBelow()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g9526b371087564e43e3dff8ad0dac949"></a><!-- doxytag: member="keytest.c::keyIsBinary" ref="g9526b371087564e43e3dff8ad0dac949" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsBinary           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check if a key is binary type.<p>
+The function checks if the keytype is in the range between KEY_TYPE_BINARY and less than excluding KEY_TYPE_STRING. Then it will be interpreted as binary.<p>
+Make sure to use this function and don't test the binary type another way to ensure compatibility and to write less error prone programs.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if it is binary <p>
+0 if it is not <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">keySetType()</a> for more information on types <p>
+<a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a>, <a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key to check </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gc0a10c602d52a35f81347e8a32312017"></a><!-- doxytag: member="keytest.c::keyIsDir" ref="gc0a10c602d52a35f81347e8a32312017" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsDir           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check if a key is directory key.<p>
+Folder keys may also have value and comment. They are discern by having a executable bit set.<p>
+If any executable bit is set it will be recognized as a directory.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>keyIsDir may return true even though you can't access the directory.</dd></dl>
+To know if you can access the directory, you need to check, if your<ul>
+<li>user ID is equal the key's user ID and the mode &amp; 100 is true</li><li>group ID is equal the key's group ID and the mode &amp; 010 is true</li><li>mode &amp; 001 is true</li></ul>
+<p>
+Accessing does not mean that you can get any value or comments below, see <a class="el" href="group__backend.html#mode">Mode</a> for more information.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if key is a directory, 0 otherwise <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#gae575bd86a628a15ee45baa860522e75">keySetDir()</a>, <a class="el" href="group__keymeta.html#g8803037e35b9da1ce492323a88ff6bc3">keySetMode()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4f175aafd98948ce6c774f3bd92b72ca"></a><!-- doxytag: member="keytest.c::keyIsDirectBelow" ref="g4f175aafd98948ce6c774f3bd92b72ca" args="(const Key *key, const Key *check)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsDirectBelow           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>check</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check if the key check is direct below the key key or not.<p>
+<div class="fragment"><pre class="fragment">Example:
+key user/sw/app
+check user/sw/app/key
+
+returns <span class="keyword">true</span> because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+does not <span class="keywordflow">return</span> <span class="keyword">true</span>, because there is only a indirect relation
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>check</em>&nbsp;</td><td>the key to find the relative position of </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if check is below key <p>
+0 if it is not below or if it is the same key <p>
+-1 on null pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keytest.html#g03332b5d97c76a4fd2640aca4762b8df">keyIsBelow()</a>, <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#gb29a850168d9b31c9529e90cf9ab68be">keyGetName()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga25f699f592031c1a0abc1504d14e13e"></a><!-- doxytag: member="keytest.c::keyIsInactive" ref="ga25f699f592031c1a0abc1504d14e13e" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsInactive           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check whether a key is inactive or not.<p>
+In elektra terminology any key is inactive if the it's basename starts with '.'. Inactive keys must not have any meaning to applications, they are reserved for users and administrators.<p>
+To remove a whole hierarchy in elektra, don't forget to pass option_t::KDB_O_INACTIVE to <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> to receive the inactive keys in order to remove them.<p>
+Otherwise you should not fetch these keys.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if the key is inactive, 0 otherwise <p>
+-1 on NULL pointer or when key has no name </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gea7670778abd07fee0fe8ac12a149190"></a><!-- doxytag: member="keytest.c::keyIsString" ref="gea7670778abd07fee0fe8ac12a149190" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsString           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check if a key is string type.<p>
+The function checks if the keytype is larger or equal KEY_TYPE_STRING. Then it will be considered as string type.<p>
+Make sure to use this function and don't test the string type another way to ensure compatibility and to write less error prone programs.<p>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if it is string <p>
+0 if it is not <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#gb92003db4b938594df48807c16766bf7">keySetType</a> for more information on types <p>
+<a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a>, <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key to check </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gfe49cfb61c2accb3073131c23a56fb14"></a><!-- doxytag: member="keytest.c::keyIsSystem" ref="gfe49cfb61c2accb3073131c23a56fb14" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsSystem           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check whether a key is under the <code>system</code> namespace or not<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if key name begins with <code>system</code>, 0 otherwise <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keytest.html#g373acc20c6209357045891f4b0c70041">keyIsUser()</a>, <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g373acc20c6209357045891f4b0c70041"></a><!-- doxytag: member="keytest.c::keyIsUser" ref="g373acc20c6209357045891f4b0c70041" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyIsUser           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Check whether a key is under the <code>user</code> namespace or not.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if key name begins with <code>user</code>, 0 otherwise <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keytest.html#gfe49cfb61c2accb3073131c23a56fb14">keyIsSystem()</a>, <a class="el" href="group__keyname.html#g7699091610e7f3f43d2949514a4b35d9">keySetName()</a>, <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gae91159815480fbb3b3d9d7fa85e77b9"></a><!-- doxytag: member="keytest.c::keyNeedRemove" ref="gae91159815480fbb3b3d9d7fa85e77b9" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyNeedRemove           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Ask if key is marked for permanent remove.<p>
+Ask if the key will be removed instead of writing in the key database when doing <a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a> or <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#g6e14e5f1de26e1318100631a149f2984">keyRemove()</a> <p>
+<a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>, <a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a>, <a class="el" href="group__kdbhighlevel.html#gf9adbbeb3f49c63fb2f89930445c8060">kdbRemove()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if it is marked, 0 otherwise <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g3908b6511648a950f37cd0005bfea5d5"></a><!-- doxytag: member="keytest.c::keyNeedStat" ref="g3908b6511648a950f37cd0005bfea5d5" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyNeedStat           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Ask if key is marked for stat only.<p>
+Ask if the key will be stat instead of get it from the key database completely doing <a class="el" href="group__kdbhighlevel.html#ga62877888f0cad395898859395e6635f">kdbGetKey()</a> or <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a>. This is useful if you are not interested in the value, comment or key type.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keymeta.html#gb8189add5e562bdb148675ee595bd95b">keyStat()</a>, <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if it is marked, 0 otherwise <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gf247df0de7aca04b32ef80e39ef12950"></a><!-- doxytag: member="keytest.c::keyNeedSync" ref="gf247df0de7aca04b32ef80e39ef12950" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyNeedSync           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Test if a key needs to be synced to backend storage.<p>
+If any key modification took place the key will be flagged with KEY_FLAG_SYNC so that <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> knows which keys were modified and which not.<p>
+After <a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> the flag will normally be set, but after <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a> the flag will be removed. When you modify the key the flag will be set again.<p>
+In your application you can make use of that flag to know if you changed something in a key after a <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> or <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that also changes in the meta data will set that flag.</dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew()</a> </dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 if <code>key</code> was changed in memory, 0 otherwise <p>
+-1 on NULL pointer </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__keyvalue.html b/doc/elektra-api/html/group__keyvalue.html
new file mode 100644 (file)
index 0000000..6b0f367
--- /dev/null
@@ -0,0 +1,569 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Key :: Value Manipulation Methods</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Key :: Value Manipulation Methods</h1>Methods to do various operations on Key values.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">const void *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString</a> (const Key *key, char *returnedString, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString</a> (Key *key, const char *newStringValue)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary</a> (const Key *key, void *returnedBinary, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary</a> (Key *key, const void *newBinary, size_t dataSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">const char *&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#g0dd737fadc16d4cf16720d17f066a9d3">keyGetCommentSize</a> (const Key *key)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment</a> (const Key *key, char *returnedComment, size_t maxSize)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment</a> (Key *key, const char *newComment)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to do various operations on Key values. 
+<p>
+A key can contain a value in different format. The most likely situation is, that the value is interpreted as text. Use <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> for that. You can save any Unicode Symbols and Elektra will take care that you get the same back, independent of your current environment.<p>
+In some situations this idea fails. When you need exactly the same value back without any interpretation of the characters, there is <a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary()</a>. If you use that, its very likely that your Configuration is not according to the standard. Also for Numbers, Booleans and Date you should use <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a>. To do so, you might use strtod() strtol() and then atol() or atof() to convert back.<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdb.h&gt;</span>
+</pre></div> <hr><h2>Function Documentation</h2>
+<a class="anchor" name="gc89fd319783b3457db45b4c09e55274a"></a><!-- doxytag: member="keyvalue.c::keyComment" ref="gc89fd319783b3457db45b4c09e55274a" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">const char* keyComment           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return a pointer to the real internal <code>key</code> comment.<p>
+This is a much more efficient version of <a class="el" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment()</a> and you should use it if you are responsible enough to not mess up things. You are not allowed to change anything in the memory region the returned pointer points to.<p>
+<a class="el" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment()</a> returns "" when there is no keyComment. The reason is <div class="fragment"><pre class="fragment">key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment</a>(key,<span class="stringliteral">""</span>);
+<a class="code" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment</a>(key); <span class="comment">// you would expect "" here</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+</pre></div><p>
+See <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a> for more information on comments.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by <a class="el" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment()</a> method to set a new value. Use <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a> instead.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to the internal managed comment <p>
+"" when there is no comment <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g0dd737fadc16d4cf16720d17f066a9d3">keyGetCommentSize()</a> for size and <a class="el" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment()</a> as alternative </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4c0d8a4a11174197699c231e0b5c3c84"></a><!-- doxytag: member="keyvalue.c::keyGetBinary" ref="g4c0d8a4a11174197699c231e0b5c3c84" args="(const Key *key, void *returnedBinary, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetBinary           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">void *&nbsp;</td>
+          <td class="paramname"> <em>returnedBinary</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the value of a key as a binary.<p>
+If the type is not binary -1 will be returned.<p>
+When the binary data is empty (this is not the same as ""!) 0 will be returned and the returnedBinary will not be changed.<p>
+For string values see <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> and <a class="el" href="group__keytest.html#gea7670778abd07fee0fe8ac12a149190">keyIsString()</a>.<p>
+When the returnedBinary is to small to hold the data (its maximum size is given by maxSize), the returnedBinary will not be changed and -1 is returned.<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment">Key *key = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/keyname"</span>, KEY_TYPE, KEY_TYPE_BINARY, KEY_END);
+<span class="keywordtype">char</span> buffer[300];
+
+<span class="keywordflow">if</span> (<a class="code" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary</a>(key,buffer,<span class="keyword">sizeof</span>(buffer)) == -1)
+{
+        <span class="comment">// handle error</span>
+}
+</pre></div></dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the object to gather the value from </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returnedBinary</em>&nbsp;</td><td>pre-allocated memory to store a copy of the key value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>number of bytes of pre-allocated memory in <code>returnedBinary</code> </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually copied to <code>returnedBinary</code> <p>
+0 if the binary is empty <p>
+-1 on NULL pointers <p>
+-1 when maxSize is 0, too small to hold the value or larger than SSIZE_MAX <p>
+-1 on typing error when the key is not binary </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a>, <a class="el" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize()</a>, <a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary()</a> <p>
+<a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> and <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> as preferred alternative to binary <p>
+<a class="el" href="group__keytest.html#g9526b371087564e43e3dff8ad0dac949">keyIsBinary()</a> to see how to check for binary type </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gfb89735689929ff717cc9f2d0d0b46a2"></a><!-- doxytag: member="keyvalue.c::keyGetComment" ref="gfb89735689929ff717cc9f2d0d0b46a2" args="(const Key *key, char *returnedComment, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetComment           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returnedComment</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the key comment.<h2><a class="anchor" name="comment">
+Comments</a></h2>
+A Key comment is description for humans what this key is for. It may be a textual explanation of valid values, when and why a user or administrator changed the key or any other text that helps the user or administrator related to that key.<p>
+Don't depend on a comment in your program. A user is always allowed to remove or change it in any way he wants to. But you are allowed or even encouraged to always show the content of the comment to the user and allow him to change it.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returnedComment</em>&nbsp;</td><td>pre-allocated memory to copy the comments to </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>number of bytes that will fit returnedComment </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually copied to <code>returnedString</code>, including final NULL <p>
+1 if the string is empty <p>
+-1 on NULL pointer <p>
+-1 if maxSize is 0, not enough to store the comment or when larger then SSIZE_MAX </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g0dd737fadc16d4cf16720d17f066a9d3">keyGetCommentSize()</a>, <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g0dd737fadc16d4cf16720d17f066a9d3"></a><!-- doxytag: member="keyvalue.c::keyGetCommentSize" ref="g0dd737fadc16d4cf16720d17f066a9d3" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetCommentSize           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Calculates number of bytes needed to store a key comment, including final NULL.<p>
+Use this method to know to size for allocated memory to retrieve a key comment.<p>
+See <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a> for more information on comments.<p>
+For an empty key name you need one byte to store the ending NULL. For that reason 1 is returned.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">char</span> *buffer;
+buffer = malloc (<a class="code" href="group__keyvalue.html#g0dd737fadc16d4cf16720d17f066a9d3">keyGetCommentSize</a> (key));
+<span class="comment">// use this buffer to store the comment</span>
+<span class="comment">// pass keyGetCommentSize (key) for maxSize</span>
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes needed <p>
+1 if there is no comment <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment()</a>, <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g41b9fac5ccddafe407fc0ae1e2eb8778"></a><!-- doxytag: member="keyvalue.c::keyGetString" ref="g41b9fac5ccddafe407fc0ae1e2eb8778" args="(const Key *key, char *returnedString, size_t maxSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetString           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">char *&nbsp;</td>
+          <td class="paramname"> <em>returnedString</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>maxSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Get the value of a key as a string.<p>
+When there is no value inside the string, 1 will be returned and the returnedString will be empty "" to avoid programming errors that old strings are shown to the user.<p>
+For binary values see <a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a> and <a class="el" href="group__keytest.html#g9526b371087564e43e3dff8ad0dac949">keyIsBinary()</a>.<p>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment">Key *key = <a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a> (<span class="stringliteral">"user/keyname"</span>, KEY_END);
+<span class="keywordtype">char</span> buffer[300];
+
+<span class="keywordflow">if</span> (<a class="code" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString</a>(key,buffer,<span class="keyword">sizeof</span>(buffer)) == -1)
+{
+        <span class="comment">// handle error</span>
+} <span class="keywordflow">else</span> {
+        printf (<span class="stringliteral">"buffer: %s\n"</span>, buffer);
+}
+</pre></div></dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the object to gather the value from </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>returnedString</em>&nbsp;</td><td>pre-allocated memory to store a copy of the key value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>maxSize</em>&nbsp;</td><td>number of bytes of allocated memory in <code>returnedString</code> </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually copied to <code>returnedString</code>, including final NULL <p>
+1 if the string is empty <p>
+-1 on NULL pointer <p>
+-1 on type mismatch <p>
+maxSize is 0, too small for string or is larger than SSIZE_MAX </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a>, <a class="el" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize()</a>, <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> <p>
+<a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a> for working with binary data </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ge326672fffb7474abfe9baf53b73217e"></a><!-- doxytag: member="keyvalue.c::keyGetValueSize" ref="ge326672fffb7474abfe9baf53b73217e" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyGetValueSize           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Returns the number of bytes needed to store the key value, including the NULL terminator.<p>
+It returns the correct size, independent of the Key Type. If it is a binary there might be '\0' values in it.<p>
+For an empty string you need one byte to store the ending NULL. For that reason 1 is returned. This is not true for binary data, so there might be returned 0 too.<p>
+A binary key has no '\0' termination. String types have it, so to there length will be added 1 to have enough space to store it.<p>
+This method can be used with malloc() before <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> or <a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a> is called.<p>
+<div class="fragment"><pre class="fragment"><span class="keywordtype">char</span> *buffer;
+buffer = malloc (<a class="code" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize</a> (key));
+<span class="comment">// use this buffer to store the value (binary or string)</span>
+<span class="comment">// pass keyGetValueSize (key) for maxSize</span>
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes needed to store the key value <p>
+1 when there is no data and type is not binary <p>
+0 when there is no data and type is binary <p>
+-1 on null pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a>, <a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a>, <a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="ga50a5358fd328d373a45f395fa1b99e7"></a><!-- doxytag: member="keyvalue.c::keySetBinary" ref="ga50a5358fd328d373a45f395fa1b99e7" args="(Key *key, const void *newBinary, size_t dataSize)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keySetBinary           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const void *&nbsp;</td>
+          <td class="paramname"> <em>newBinary</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">size_t&nbsp;</td>
+          <td class="paramname"> <em>dataSize</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the value of a key as a binary.<p>
+A private copy of <code>newBinary</code> will allocated and saved inside <code>key</code>, so the parameter can be deallocated after the call.<p>
+The <code>filesys</code> backend, when used through a <a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a>, will make the value be kdbbEncoded into a human readable hex-digit text format.<p>
+Consider using a string key instead.<p>
+When newBinary is a NULL pointer the binary will be freed and 0 will be returned.<p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>When the type of the key is already a binary type it won't be changed.</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the object on which to set the value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>newBinary</em>&nbsp;</td><td>is a pointer to any binary data or NULL to free the previous set data </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>dataSize</em>&nbsp;</td><td>number of bytes to copy from <code>newBinary</code> </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually copied to internal struct storage <p>
+0 when the internal binary was freed <p>
+-1 on NULL pointer <p>
+-1 when dataSize is 0 (but newBinary not NULL) or larger than SSIZE_MAX </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a> <p>
+<a class="el" href="group__keytest.html#g9526b371087564e43e3dff8ad0dac949">keyIsBinary()</a> to check if the type is binary <p>
+<a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> and <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> as preferred alternative to binary </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g8863a877a84fa46e6017fe72e49b89c1"></a><!-- doxytag: member="keyvalue.c::keySetComment" ref="g8863a877a84fa46e6017fe72e49b89c1" args="(Key *key, const char *newComment)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keySetComment           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>newComment</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set a comment for a key.<p>
+A key comment is like a configuration file comment. See <a class="el" href="group__keyvalue.html#g8863a877a84fa46e6017fe72e49b89c1">keySetComment()</a> for more information.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>newComment</em>&nbsp;</td><td>the comment, that can be freed after this call. </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually saved including final NULL <p>
+1 when the comment was freed <p>
+-1 on NULL pointer or memory problems </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#gfb89735689929ff717cc9f2d0d0b46a2">keyGetComment()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g622bde1eb0e0c4994728331326340ef2"></a><!-- doxytag: member="keyvalue.c::keySetString" ref="g622bde1eb0e0c4994728331326340ef2" args="(Key *key, const char *newStringValue)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keySetString           </td>
+          <td>(</td>
+          <td class="paramtype">Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>newStringValue</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Set the value for <code>key</code> as <code>newStringValue</code>.<p>
+The function will allocate and save a private copy of <code>newStringValue</code>, so the parameter can be freed after the call.<p>
+String values will be saved in backend storage, when <a class="el" href="group__kdbhighlevel.html#g23b2f5fead4cddeb5542051a197ddc20">kdbSetKey()</a> will be called, in UTF-8 universal encoding, regardless of the program's current encoding, when compiled with --enable-iconv.<p>
+The type will be set to KEY_TYPE_STRING. When the type of the key is already a string type it won't be changed.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key to set the string value </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>newStringValue</em>&nbsp;</td><td>NULL-terminated text string to be set as <code>key's</code> value </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>the number of bytes actually saved in private struct including final NULL <p>
+-1 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a>, <a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a> </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g6f29609c5da53c6dc26a98678d5752af"></a><!-- doxytag: member="keyvalue.c::keyValue" ref="g6f29609c5da53c6dc26a98678d5752af" args="(const Key *key)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">const void* keyValue           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>          </td>
+          <td>&nbsp;)&nbsp;</td>
+          <td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Return a pointer to the real internal <code>key</code> value.<p>
+This is a much more efficient version of <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> <a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a>, and you should use it if you are responsible enough to not mess up things. You are not allowed to modify anything in the returned string. If you need a copy of the Value, consider to use <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a> or <a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a> instead.<h2><a class="anchor" name="string">
+String Handling</a></h2>
+If <code>key</code> is string (<a class="el" href="group__keytest.html#gea7670778abd07fee0fe8ac12a149190">keyIsString()</a>), you may cast the returned as a <code>"char *"</code> because you'll get a NULL terminated regular string.<p>
+<a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a> returns "" in string mode when there is no value. The reason is <div class="fragment"><pre class="fragment">key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString</a>(key,<span class="stringliteral">""</span>);
+<a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(key); <span class="comment">// you would expect "" here</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+</pre></div><h2><a class="anchor" name="binary">
+Binary Data Handling</a></h2>
+If the data is binary, the size of the value must be determined by <a class="el" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize()</a>, any strlen() operations are not suitable to determine the size.<p>
+<a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a> returns 0 in binary mode when there is no value. The reason is <div class="fragment"><pre class="fragment"><span class="keywordtype">int</span> i=23;
+key=<a class="code" href="group__key.html#gf6893c038b3ebee90c73a9ea8356bebf">keyNew</a>(0);
+<a class="code" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary</a>(key, 0, 0);
+<a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(key); <span class="comment">// you would expect 0 here</span>
+
+<a class="code" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary</a>(key,<span class="stringliteral">""</span>, 1);
+<a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(key); <span class="comment">// you would expect "" (a pointer to '\0') here</span>
+
+<a class="code" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary</a>(key, (<span class="keywordtype">void</span>*)&amp;i, 4);
+(<span class="keywordtype">int</span>*)<a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(key); <span class="comment">// you would expect a pointer to (int)23 here</span>
+<a class="code" href="group__key.html#g3df95bbc2494e3e6703ece5639be5bb1">keyDel</a>(key);
+</pre></div><p>
+<dl class="note" compact><dt><b>Note:</b></dt><dd>Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by <a class="el" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue()</a> method to set a new value. Use <a class="el" href="group__keyvalue.html#g622bde1eb0e0c4994728331326340ef2">keySetString()</a> or <a class="el" href="group__keyvalue.html#ga50a5358fd328d373a45f395fa1b99e7">keySetBinary()</a> instead.</dd></dl>
+<dl class="warning" compact><dt><b>Warning:</b></dt><dd>Binary keys will return a NULL pointer when there is no data in contrast to <a class="el" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName()</a>, <a class="el" href="group__keyname.html#gaff35e7ca8af5560c47e662ceb9465f5">keyBaseName()</a>, <a class="el" href="group__keyname.html#gf6485fb8599714b6bbd830cf915ffea5">keyOwner()</a> and <a class="el" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment()</a>. For string value the behaviour is the same.</dd></dl>
+<dl class="user" compact><dt><b>Example:</b></dt><dd><div class="fragment"><pre class="fragment">KDB *handle = <a class="code" href="group__kdb.html#gb7be60c387892d2235907836c5060e1f">kdbOpen</a>();
+KeySet *ks=<a class="code" href="group__keyset.html#g671e1aaee3ae9dc13b4834a4ddbd2c3c">ksNew</a>(0);
+Key *current=0;
+
+<a class="code" href="group__kdbhighlevel.html#g3013d5768bbcf3c34652f489151940e2">kdbGetByName</a>(handle,ks,<span class="stringliteral">"system/sw/my"</span>,KDB_O_SORT|KDB_O_RECURSIVE);
+
+<a class="code" href="group__keyset.html#gbe793ff51f1728e3429c84a8a9086b70">ksRewind</a>(ks);
+<span class="keywordflow">while</span>(current=<a class="code" href="group__keyset.html#g317321c9065b5a4b3e33fe1c399bcec9">ksNext</a>(ks)) {
+        <span class="keywordtype">size_t</span> size=0;
+        
+        <span class="keywordflow">if</span> (keyIsBin(current)) {
+                size=<a class="code" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize</a>(current);
+                printf(<span class="stringliteral">"Key %s has a value of size %d bytes. Value: &lt;BINARY&gt;\nComment: %s"</span>,
+                        <a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(current),
+                        size,
+                        <a class="code" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment</a>(current));
+        } <span class="keywordflow">else</span> {
+                size=<a class="code" href="group__internal.html#ga13b38e07d4e54738b951c3e0bef2faf">kdbiStrLen</a>((<span class="keywordtype">char</span> *)<a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(current));
+                printf(<span class="stringliteral">"Key %s has a value of size %d bytes. Value: %s\nComment: %s"</span>,
+                        <a class="code" href="group__keyname.html#g8e805c726a60da921d3736cda7813513">keyName</a>(current),
+                        size,
+                        (<span class="keywordtype">char</span> *)<a class="code" href="group__keyvalue.html#g6f29609c5da53c6dc26a98678d5752af">keyValue</a>(current),
+                        <a class="code" href="group__keyvalue.html#gc89fd319783b3457db45b4c09e55274a">keyComment</a>(current));
+        }
+}
+
+<a class="code" href="group__keyset.html#g27e5c16473b02a422238c8d970db7ac8">ksDel</a> (ks);
+<a class="code" href="group__kdb.html#gd9bb8bd3f1296bfa77cc9a1b41b7a859">kdbClose</a> (handle);
+</pre></div></dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>a pointer to internal value <p>
+"" when there is no data and key is not binary <p>
+0 where there is no data and key is binary <p>
+0 on NULL pointer </dd></dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__keyvalue.html#ge326672fffb7474abfe9baf53b73217e">keyGetValueSize()</a>, <a class="el" href="group__keyvalue.html#g41b9fac5ccddafe407fc0ae1e2eb8778">keyGetString()</a>, <a class="el" href="group__keyvalue.html#g4c0d8a4a11174197699c231e0b5c3c84">keyGetBinary()</a> </dd></dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/group__stream.html b/doc/elektra-api/html/group__stream.html
new file mode 100644 (file)
index 0000000..86a2de7
--- /dev/null
@@ -0,0 +1,617 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Streaming</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Streaming</h1>Methods to output, generate and toXML Keys and Keysets.  
+<a href="#_details">More...</a><table border="0" cellpadding="0" cellspacing="0">
+<tr><td></td></tr>
+<tr><td colspan="2"><br><h2>Enumerations</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">enum &nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g0f01b410963104a39a8c0109c339d1cd">KDBStream</a> { <br>
+&nbsp;&nbsp;<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdf108bbeaf014509dccbe4098a828d71a">KDB_O_SHOWMETA</a> = 0xF0, 
+<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd1f8f6dde6172b89e07c3a3be222da2c6">KDB_O_SHOWFLAGS</a> = 1&lt;&lt;14, 
+<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd7ed84255e173af98655d16c480e324bf">KDB_O_SHOWINDICES</a> = 1&lt;&lt;15, 
+<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2">KDB_O_CONDENSED</a> = 1&lt;&lt;16, 
+<br>
+&nbsp;&nbsp;<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd7df0465c4d5d6c3e063860e7ce6205d9">KDB_O_NUMBER</a> = 1&lt;&lt;17, 
+<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdb7831d9df99114658de58990c5158d29">KDB_O_HEADER</a> = 1&lt;&lt;18, 
+<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d">KDB_O_FULLNAME</a> = 1&lt;&lt;19, 
+<a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856">KDB_O_HIER</a> = 1&lt;&lt;20
+<br>
+ }</td></tr>
+
+<tr><td colspan="2"><br><h2>Functions</h2></td></tr>
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g68078d5ed73459629e01a228503e6821">ksFromXMLfile</a> (KeySet *ks, const char *filename)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g2fb7bd97dbf4fcaeb7d75efb6cad45d8">ksFromXML</a> (KeySet *ks, int fd)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#gba86ec51ba1abc125372c5519abd45a9">keyToStream</a> (const Key *key, FILE *stream, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g32b9dcebc110c4bf69d9c08ca9f96400">keyToStreamBasename</a> (const Key *key, FILE *stream, const char *parent, const size_t parentSize, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">ssize_t&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g7766ffe8c534fa7154a74fd26bd974f3">ksToStream</a> (const KeySet *ks, FILE *stream, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g4eb79e22d81302283ef9acda19b372f5">keyOutput</a> (const Key *k, FILE *stream, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g45ae77a71186960d05d4e39f47184cb1">ksOutput</a> (const KeySet *ks, FILE *stream, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g7522c446271b79d62db8e52157495f40">keyGenerate</a> (const Key *key, FILE *stream, option_t options)</td></tr>
+
+<tr><td class="memItemLeft" nowrap align="right" valign="top">int&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="group__stream.html#g4a4eb7b7cfea5e2faef99ac331eb15e2">ksGenerate</a> (const KeySet *ks, FILE *stream, option_t options)</td></tr>
+
+</table>
+<hr><a name="_details"></a><h2>Detailed Description</h2>
+Methods to output, generate and toXML Keys and Keysets. 
+<p>
+Here are some functions that are in a separate library because they depend on non-basic libraries as libxml. Link against kdbtools library if your program won't be installed in /bin, or is not essential in early boot stages.<p>
+It is also possible to have a dynamic linkage, see the sourcecode from kdb-tool or testing framework how to achieve that.<p>
+Output prints keys line per line, meaned to be read by humans.<ul>
+<li><a class="el" href="group__stream.html#g4eb79e22d81302283ef9acda19b372f5">keyOutput()</a></li><li><a class="el" href="group__stream.html#g45ae77a71186960d05d4e39f47184cb1">ksOutput()</a></li></ul>
+<p>
+Generate prints keys and keysets as C-Structs, meaned to be read by a C-Compiler.<ul>
+<li><a class="el" href="group__stream.html#g7522c446271b79d62db8e52157495f40">keyGenerate()</a></li><li><a class="el" href="group__stream.html#g4a4eb7b7cfea5e2faef99ac331eb15e2">ksGenerate()</a></li></ul>
+<p>
+toXML prints keys and keysets as XML, meaned to be used as exchange format.<ul>
+<li><a class="el" href="group__stream.html#gba86ec51ba1abc125372c5519abd45a9">keyToStream()</a></li><li><a class="el" href="group__stream.html#g7766ffe8c534fa7154a74fd26bd974f3">ksToStream()</a></li></ul>
+<p>
+To use them: <div class="fragment"><pre class="fragment"><span class="preprocessor">#include &lt;kdbtools.h&gt;</span>
+</pre></div> <hr><h2>Enumeration Type Documentation</h2>
+<a class="anchor" name="g0f01b410963104a39a8c0109c339d1cd"></a><!-- doxytag: member="kdbtools.h::KDBStream" ref="g0f01b410963104a39a8c0109c339d1cd" args="" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">enum <a class="el" href="group__stream.html#g0f01b410963104a39a8c0109c339d1cd">KDBStream</a>          </td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Options to change the default behavior of streaming.<p>
+On default the streaming options only output the names of the given keysets. If you want more information, header, metainfo, compressed output, full names, values or comments you will find the appropriate options here.<p>
+For full influence of value, comment and metadata shown, use these options together with keyswitch_t. All bits of meta-information ORed together are KDB_O_SHOWMETA.<p>
+For more information about the flags, consult the documentation of the streaming methods.<p>
+These options can be ORed. That is the |-Operator in C.<p>
+It uses the values defined in keyswitch_t too, so it starts with 14.<p>
+<dl class="see" compact><dt><b>See also:</b></dt><dd>kdbGetChildKeys() <p>
+<a class="el" href="group__stream.html#g7766ffe8c534fa7154a74fd26bd974f3">ksToStream()</a> <p>
+<a class="el" href="group__stream.html#gba86ec51ba1abc125372c5519abd45a9">keyToStream()</a> </dd></dl>
+<dl compact><dt><b>Enumerator: </b></dt><dd>
+<table border="0" cellspacing="2" cellpadding="0">
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cdf108bbeaf014509dccbe4098a828d71a"></a><!-- doxytag: member="KDB_O_SHOWMETA" ref="gg0f01b410963104a39a8c0109c339d1cdf108bbeaf014509dccbe4098a828d71a" args="" -->KDB_O_SHOWMETA</em>&nbsp;</td><td>
+Show all metadata (type, uid, gid, mode) </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cd1f8f6dde6172b89e07c3a3be222da2c6"></a><!-- doxytag: member="KDB_O_SHOWFLAGS" ref="gg0f01b410963104a39a8c0109c339d1cd1f8f6dde6172b89e07c3a3be222da2c6" args="" -->KDB_O_SHOWFLAGS</em>&nbsp;</td><td>
+Show all flags </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cd7ed84255e173af98655d16c480e324bf"></a><!-- doxytag: member="KDB_O_SHOWINDICES" ref="gg0f01b410963104a39a8c0109c339d1cd7ed84255e173af98655d16c480e324bf" args="" -->KDB_O_SHOWINDICES</em>&nbsp;</td><td>
+Show the indices for the entries </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2"></a><!-- doxytag: member="KDB_O_CONDENSED" ref="gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2" args="" -->KDB_O_CONDENSED</em>&nbsp;</td><td>
+Spare any whitespaces and do not group visually together. </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cd7df0465c4d5d6c3e063860e7ce6205d9"></a><!-- doxytag: member="KDB_O_NUMBER" ref="gg0f01b410963104a39a8c0109c339d1cd7df0465c4d5d6c3e063860e7ce6205d9" args="" -->KDB_O_NUMBER</em>&nbsp;</td><td>
+Use a number intead of user and group name. </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cdb7831d9df99114658de58990c5158d29"></a><!-- doxytag: member="KDB_O_HEADER" ref="gg0f01b410963104a39a8c0109c339d1cdb7831d9df99114658de58990c5158d29" args="" -->KDB_O_HEADER</em>&nbsp;</td><td>
+Show also the header of the document. </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d"></a><!-- doxytag: member="KDB_O_FULLNAME" ref="gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d" args="" -->KDB_O_FULLNAME</em>&nbsp;</td><td>
+Export <code>user</code> keys using full name. </td></tr>
+<tr><td valign="top"><em><a class="anchor" name="gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856"></a><!-- doxytag: member="KDB_O_HIER" ref="gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856" args="" -->KDB_O_HIER</em>&nbsp;</td><td>
+Export to the new hierarchical XML representation using key basename. See <a class="el" href="group__stream.html#g7766ffe8c534fa7154a74fd26bd974f3">ksToStream()</a>. </td></tr>
+</table>
+</dl>
+
+</div>
+</div><p>
+<hr><h2>Function Documentation</h2>
+<a class="anchor" name="g7522c446271b79d62db8e52157495f40"></a><!-- doxytag: member="stream.c::keyGenerate" ref="g7522c446271b79d62db8e52157495f40" args="(const Key *key, FILE *stream, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyGenerate           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Generate a C-Style key and stream it.<p>
+This keyset can be used to include as c-code for applikations using elektra.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>the file pointer where to send the stream </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on success </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4eb79e22d81302283ef9acda19b372f5"></a><!-- doxytag: member="stream.c::keyOutput" ref="g4eb79e22d81302283ef9acda19b372f5" args="(const Key *k, FILE *stream, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int keyOutput           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>k</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Output every information of a single key depending on options.<p>
+The format is not very strict and only intend to be read by human eyes for debugging purposes. Don't rely on the format in your applications.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>k</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>the file pointer where to send the stream </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>see text above </td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__stream.html#g45ae77a71186960d05d4e39f47184cb1">ksOutput()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on success <p>
+-1 on allocation errors </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="gba86ec51ba1abc125372c5519abd45a9"></a><!-- doxytag: member="stream.c::keyToStream" ref="gba86ec51ba1abc125372c5519abd45a9" args="(const Key *key, FILE *stream, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyToStream           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Prints an XML representation of the key.<p>
+String generated is of the form: <div class="fragment"><pre class="fragment">
+       &lt;key name="system/sw/xorg/Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660"&gt;
+
+               &lt;value&gt;Samsung TFT panel&lt;/value&gt;
+               &lt;comment&gt;My monitor&lt;/comment&gt;
+       &lt;/key&gt;</pre></div><p>
+<div class="fragment"><pre class="fragment">
+       &lt;key parent="system/sw/xorg/Monitor/Monitor0" basename="Name"
+               type="string" uid="root" gid="root" mode="0660"&gt;
+
+               &lt;value&gt;Samsung TFT panel&lt;/value&gt;
+               &lt;comment&gt;My monitor&lt;/comment&gt;
+       &lt;/key&gt;</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>where to write output: a file or stdout </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>Some option_t ORed:<ul>
+<li><code>option_t::KDB_O_NUMBERS</code> <br>
+ Do not convert UID and GID into user and group names</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2">option_t::KDB_O_CONDENSED</a></code> <br>
+ Less human readable, more condensed output</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d">option_t::KDB_O_FULLNAME</a></code> <br>
+ The <code>user</code> keys are exported with their full names (including user domains)</li></ul>
+</td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__stream.html#g7766ffe8c534fa7154a74fd26bd974f3">ksToStream()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to output </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g32b9dcebc110c4bf69d9c08ca9f96400"></a><!-- doxytag: member="stream.c::keyToStreamBasename" ref="g32b9dcebc110c4bf69d9c08ca9f96400" args="(const Key *key, FILE *stream, const char *parent, const size_t parentSize, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t keyToStreamBasename           </td>
+          <td>(</td>
+          <td class="paramtype">const Key *&nbsp;</td>
+          <td class="paramname"> <em>key</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>parent</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const size_t&nbsp;</td>
+          <td class="paramname"> <em>parentSize</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Same as <a class="el" href="group__stream.html#gba86ec51ba1abc125372c5519abd45a9">keyToStream()</a> but tries to strip <code>parentSize</code> bytes from <code>key</code> name if it matches <code>parent</code> .<p>
+Taking the example from <a class="el" href="group__stream.html#gba86ec51ba1abc125372c5519abd45a9">keyToStream()</a>, if <code>parent</code> is <code>"system/sw/xorg"</code>, the generated string is of the form: <div class="fragment"><pre class="fragment">
+       &lt;key basename="Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660"&gt;
+
+               &lt;value&gt;Samsung TFT panel&lt;/value&gt;
+               &lt;comment&gt;My monitor&lt;/comment&gt;
+       &lt;/key&gt;</pre></div><p>
+It usefull to produce more human readable XML output of a key when it is being represented in a context that defines the parent key name. For example:<p>
+<div class="fragment"><pre class="fragment">
+       &lt;keyset parent="user/sw"&gt;
+               &lt;key basename="kdbedit"..../&gt;
+               &lt;key basename="phototools"..../&gt;
+               &lt;key basename="myapp"..../&gt;
+       &lt;/keyset&gt;</pre></div><p>
+In the bove example, each <code>&lt;key&gt;</code> entry was generated by a call to <a class="el" href="group__stream.html#g32b9dcebc110c4bf69d9c08ca9f96400">keyToStreamBasename()</a> having <code>"user/sw"</code> as <code>parent</code> .<p>
+This method is used when <a class="el" href="group__stream.html#g7766ffe8c534fa7154a74fd26bd974f3">ksToStream()</a> is called with <a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856">KDBOption::KDB_O_HIER</a> option.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>key</em>&nbsp;</td><td>the key object to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>the FILE where to send the stream </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parentSize</em>&nbsp;</td><td>the maximum size of <code>parent</code> that will be used. If 0, the entire <code>parent</code> will be used. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>parent</em>&nbsp;</td><td>the string (or part of it, defined by <code>parentSize</code> ) that will be used to strip from the key name. </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>Some option_t ORed:<ul>
+<li><code>option_t::KDB_O_NUMBERS</code> <br>
+ Do not convert UID and GID into user and group names</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2">option_t::KDB_O_CONDENSED</a></code> <br>
+ Less human readable, more condensed output</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d">option_t::KDB_O_FULLNAME</a></code> <br>
+ The <code>user</code> keys are exported with their full names (including user domains)</li></ul>
+</td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to output </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g2fb7bd97dbf4fcaeb7d75efb6cad45d8"></a><!-- doxytag: member="kdbtools.c::ksFromXML" ref="g2fb7bd97dbf4fcaeb7d75efb6cad45d8" args="(KeySet *ks, int fd)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksFromXML           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&nbsp;</td>
+          <td class="paramname"> <em>fd</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+FIXME: not working when fd is stdin Given a file descriptor (that can be <code>stdin</code>) for an XML file, validate schema, process nodes, convert and save it in the <code>ks</code> KeySet.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>keyset </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>fd</em>&nbsp;</td><td>Filedeskriptor?? should be FILE* </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g68078d5ed73459629e01a228503e6821"></a><!-- doxytag: member="kdbtools.c::ksFromXMLfile" ref="g68078d5ed73459629e01a228503e6821" args="(KeySet *ks, const char *filename)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksFromXMLfile           </td>
+          <td>(</td>
+          <td class="paramtype">KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">const char *&nbsp;</td>
+          <td class="paramname"> <em>filename</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Given an XML <code>filename</code>, open it, validate schema, process nodes, convert and save it in the <code>ks</code> KeySet.<p>
+Currently, the XML file can have many root <code>&lt;keyset&gt;</code> and <code>&lt;key&gt;</code> nodes. They will all be reduced to simple keys returned in <code>ks</code>.<p>
+To check if the xml file is valid (best before you read from it): <div class="fragment"><pre class="fragment"><span class="preprocessor">#include </span>
+<span class="preprocessor"></span><span class="keywordtype">char</span> schemaPath[513];
+schemaPath[0]=0;
+ret=<a class="code" href="group__kdbhighlevel.html#g1e1b1a2beace8c9ce93d16259564b51f">kdbGetString</a>(handle, KDB_SCHEMA_PATH_KEY,schemaPath,<span class="keyword">sizeof</span>(schemaPath));
+
+<span class="keywordflow">if</span> (ret==0) ret = isValidXML(filename,schemaPath);
+<span class="keywordflow">else</span> ret = isValidXML(filename,KDB_SCHEMA_PATH); 
+</pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>filename</em>&nbsp;</td><td>the file to parse </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g4a4eb7b7cfea5e2faef99ac331eb15e2"></a><!-- doxytag: member="stream.c::ksGenerate" ref="g4a4eb7b7cfea5e2faef99ac331eb15e2" args="(const KeySet *ks, FILE *stream, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksGenerate           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Generate a C-Style keyset and stream it.<p>
+This keyset can be used to include as c-code for applikations using elektra.<p>
+The options takes the same options as <a class="el" href="group__kdb.html#g37b44bda1b83bc0144916bf21a86c1b5">kdbGet()</a> and <a class="el" href="group__kdb.html#g953cf29721e6000c2516cd6b5d36f571">kdbSet()</a>.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>the file pointer where to send the stream </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>which keys not to output </td></tr>
+  </table>
+</dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on success </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g45ae77a71186960d05d4e39f47184cb1"></a><!-- doxytag: member="stream.c::ksOutput" ref="g45ae77a71186960d05d4e39f47184cb1" args="(const KeySet *ks, FILE *stream, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">int ksOutput           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Output all information of a keyset.<p>
+The format is not very strict and only intend to be read by human eyes for debugging purposes. Don't rely on the format in your applications.<p>
+Keys are printed line per line with <a class="el" href="group__stream.html#g4eb79e22d81302283ef9acda19b372f5">keyOutput()</a>.<p>
+The same options as <a class="el" href="group__stream.html#g4eb79e22d81302283ef9acda19b372f5">keyOutput()</a> are taken and passed to them.<p>
+Additional KDB_O_HEADER will print the number of keys as first line.<p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the keyset to work with </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>the file pointer where to send the stream </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td></td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__stream.html#g4eb79e22d81302283ef9acda19b372f5">keyOutput()</a> </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>1 on success <p>
+-1 on allocation errors </dd></dl>
+
+</div>
+</div><p>
+<a class="anchor" name="g7766ffe8c534fa7154a74fd26bd974f3"></a><!-- doxytag: member="stream.c::ksToStream" ref="g7766ffe8c534fa7154a74fd26bd974f3" args="(const KeySet *ks, FILE *stream, option_t options)" -->
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">ssize_t ksToStream           </td>
+          <td>(</td>
+          <td class="paramtype">const KeySet *&nbsp;</td>
+          <td class="paramname"> <em>ks</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">FILE *&nbsp;</td>
+          <td class="paramname"> <em>stream</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">option_t&nbsp;</td>
+          <td class="paramname"> <em>options</em></td><td>&nbsp;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td><td></td>
+        </tr>
+      </table>
+</div>
+<div class="memdoc">
+
+<p>
+Writes to <code>stream</code> an XML version of the <code>ks</code> object.<p>
+String generated is of the form: <div class="fragment"><pre class="fragment">
+&lt;keyset&gt;
+&lt;key name=...&gt;...&lt;/key&gt;
+&lt;key name=...&gt;...&lt;/key&gt;
+&lt;key name=...&gt;...&lt;/key&gt;
+
+&lt;/keyset&gt;
+ * </pre></div><p>
+or if KDB_O_HIER is used, the form will be: <div class="fragment"><pre class="fragment">
+&lt;keyset parent="user/smallest/parent/name"&gt;
+
+&lt;key basename=...&gt;...&lt;/key&gt;
+&lt;key name=...&gt;...&lt;/key&gt; &lt;!-- a key thats not under this keyset's parent --&gt;
+&lt;key basename=...&gt;...&lt;/key&gt;
+
+&lt;/keyset&gt;
+ * </pre></div><p>
+KDB_O_HEADER will additionally generate a header like: <div class="fragment"><pre class="fragment">
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;!-- Generated by Elektra API. Total of n keys. --&gt;
+&lt;keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"&gt;
+ * </pre></div><p>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>the KeySet to serialize </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>where to write output: a file or stdout </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>accepted option_t ORed:<ul>
+<li><code>option_t::KDB_O_NUMBERS</code> <br>
+ Do not convert UID and GID into user and group names.</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d">option_t::KDB_O_FULLNAME</a></code> <br>
+ The <code>user</code> keys are exported with their full names (including user domains)</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2">option_t::KDB_O_CONDENSED</a></code> <br>
+ Less human readable, more condensed output.</li><li><code>option_t::KDB_O_XMLHEADERS</code> <br>
+ Exclude the correct XML headers in the output. If not used, the &lt;?xml?&gt; and schema info inside the &lt;keyset&gt; object will not be generated.</li><li><code><a class="el" href="group__stream.html#gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856">option_t::KDB_O_HIER</a></code> <br>
+ Will generate a &lt;keyset&gt; node containing a <code>parent</code> attribute, and &lt;key&gt; nodes with a <code>basename</code> relative to that <code>parent</code>. The <code>parent</code> is calculated by taking the smallest key name in the keyset, so it is a good idea to have only related keys on the keyset. Otherwise, a valid consistent XML document still will be generated with regular absolute <code>name</code> attribute for the &lt;key&gt; nodes, due to a clever <a class="el" href="group__stream.html#g32b9dcebc110c4bf69d9c08ca9f96400">keyToStreamBasename()</a> implementation.</li></ul>
+</td></tr>
+  </table>
+</dl>
+<dl class="see" compact><dt><b>See also:</b></dt><dd><a class="el" href="group__stream.html#gba86ec51ba1abc125372c5519abd45a9">keyToStream()</a> <p>
+commandList() for usage example </dd></dl>
+<dl class="return" compact><dt><b>Returns:</b></dt><dd>number of bytes written to output, or -1 if some error occurs</dd></dl>
+<dl compact><dt><b>Parameters:</b></dt><dd>
+  <table border="0" cellspacing="2" cellpadding="0">
+    <tr><td valign="top"></td><td valign="top"><em>ks</em>&nbsp;</td><td>The keyset to output </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>stream</em>&nbsp;</td><td>the file pointer where to send the stream </td></tr>
+    <tr><td valign="top"></td><td valign="top"><em>options</em>&nbsp;</td><td>see above text </td></tr>
+  </table>
+</dl>
+
+</div>
+</div><p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/index.html b/doc/elektra-api/html/index.html
new file mode 100644 (file)
index 0000000..6513080
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Main Page</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li class="current"><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Elektra Projekt Documentation</h1>
+<p>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/modules.html b/doc/elektra-api/html/modules.html
new file mode 100644 (file)
index 0000000..f02279b
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Module Index</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li class="current"><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Modules</h1>Here is a list of all modules:<ul>
+<li><a class="el" href="group__kdbhighlevel.html">KDB :: High Level methods</a>
+<li><a class="el" href="group__kdb.html">KDB :: Low Level Methods</a>
+<li><a class="el" href="group__backendhelper.html">KDB Backends :: Backend Helper for Elektra</a>
+<li><a class="el" href="group__backend.html">KDB Backends :: Elektra framework for pluggable backends</a>
+<li><a class="el" href="group__internal.html">KDB Backends :: Internal Helper for Elektra</a>
+<li><a class="el" href="group__backendhandle.html">KDB Backends :: KDB access functions</a>
+<li><a class="el" href="group__key.html">Key :: Basic Methods</a>
+<li><a class="el" href="group__keymeta.html">Key :: Meta Info Manipulation Methods</a>
+<li><a class="el" href="group__keytest.html">Key :: Methods for Making Tests</a>
+<li><a class="el" href="group__keyname.html">Key :: Name Manipulation Methods</a>
+<li><a class="el" href="group__keyvalue.html">Key :: Value Manipulation Methods</a>
+<li><a class="el" href="group__keyset.html">KeySet :: Class Methods</a>
+<li><a class="el" href="group__stream.html">Streaming</a>
+</ul>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/pages.html b/doc/elektra-api/html/pages.html
new file mode 100644 (file)
index 0000000..f491b9f
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+<title>Elektra Projekt: Page Index</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head><body>
+<!-- Generated by Doxygen 1.5.6 -->
+<div class="navigation" id="top">
+  <div class="tabs">
+    <ul>
+      <li><a href="index.html"><span>Main&nbsp;Page</span></a></li>
+      <li class="current"><a href="pages.html"><span>Related&nbsp;Pages</span></a></li>
+      <li><a href="modules.html"><span>Modules</span></a></li>
+    </ul>
+  </div>
+</div>
+<div class="contents">
+<h1>Related Pages</h1>Here is a list of all related documentation pages:<ul>
+<li><a class="el" href="err.html">Error</a>
+
+</ul>
+</div>
+<hr size="1"><address style="text-align: right;"><small>Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by&nbsp;
+<a href="http://www.doxygen.org/index.html">
+<img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> 1.5.6 </small></address>
+</body>
+</html>
diff --git a/doc/elektra-api/html/tab_b.gif b/doc/elektra-api/html/tab_b.gif
new file mode 100644 (file)
index 0000000..0d62348
Binary files /dev/null and b/doc/elektra-api/html/tab_b.gif differ
diff --git a/doc/elektra-api/html/tab_l.gif b/doc/elektra-api/html/tab_l.gif
new file mode 100644 (file)
index 0000000..9b1e633
Binary files /dev/null and b/doc/elektra-api/html/tab_l.gif differ
diff --git a/doc/elektra-api/html/tab_r.gif b/doc/elektra-api/html/tab_r.gif
new file mode 100644 (file)
index 0000000..ce9dd9f
Binary files /dev/null and b/doc/elektra-api/html/tab_r.gif differ
diff --git a/doc/elektra-api/html/tabs.css b/doc/elektra-api/html/tabs.css
new file mode 100644 (file)
index 0000000..95f00a9
--- /dev/null
@@ -0,0 +1,102 @@
+/* tabs styles, based on http://www.alistapart.com/articles/slidingdoors */
+
+DIV.tabs
+{
+   float            : left;
+   width            : 100%;
+   background       : url("tab_b.gif") repeat-x bottom;
+   margin-bottom    : 4px;
+}
+
+DIV.tabs UL
+{
+   margin           : 0px;
+   padding-left     : 10px;
+   list-style       : none;
+}
+
+DIV.tabs LI, DIV.tabs FORM
+{
+   display          : inline;
+   margin           : 0px;
+   padding          : 0px;
+}
+
+DIV.tabs FORM
+{
+   float            : right;
+}
+
+DIV.tabs A
+{
+   float            : left;
+   background       : url("tab_r.gif") no-repeat right top;
+   border-bottom    : 1px solid #84B0C7;
+   font-size        : x-small;
+   font-weight      : bold;
+   text-decoration  : none;
+}
+
+DIV.tabs A:hover
+{
+   background-position: 100% -150px;
+}
+
+DIV.tabs A:link, DIV.tabs A:visited,
+DIV.tabs A:active, DIV.tabs A:hover
+{
+       color: #1A419D;
+}
+
+DIV.tabs SPAN
+{
+   float            : left;
+   display          : block;
+   background       : url("tab_l.gif") no-repeat left top;
+   padding          : 5px 9px;
+   white-space      : nowrap;
+}
+
+DIV.tabs INPUT
+{
+   float            : right;
+   display          : inline;
+   font-size        : 1em;
+}
+
+DIV.tabs TD
+{
+   font-size        : x-small;
+   font-weight      : bold;
+   text-decoration  : none;
+}
+
+
+
+/* Commented Backslash Hack hides rule from IE5-Mac \*/
+DIV.tabs SPAN {float : none;}
+/* End IE5-Mac hack */
+
+DIV.tabs A:hover SPAN
+{
+   background-position: 0% -150px;
+}
+
+DIV.tabs LI.current A
+{
+   background-position: 100% -150px;
+   border-width     : 0px;
+}
+
+DIV.tabs LI.current SPAN
+{
+   background-position: 0% -150px;
+   padding-bottom   : 6px;
+}
+
+DIV.navpath
+{
+   background       : none;
+   border           : none;
+   border-bottom    : 1px solid #84B0C7;
+}
diff --git a/doc/elektra-api/latex/FreeSans.ttf b/doc/elektra-api/latex/FreeSans.ttf
new file mode 100644 (file)
index 0000000..b550b90
Binary files /dev/null and b/doc/elektra-api/latex/FreeSans.ttf differ
diff --git a/doc/elektra-api/latex/doxygen.sty b/doc/elektra-api/latex/doxygen.sty
new file mode 100644 (file)
index 0000000..9169e1c
--- /dev/null
@@ -0,0 +1,78 @@
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{doxygen}
+\RequirePackage{calc}
+\RequirePackage{array}
+\pagestyle{fancyplain}
+\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}}
+\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
+\renewcommand{\sectionmark}[1]{\markright{\thesection\ #1}}
+\lhead[\fancyplain{}{\bfseries\thepage}]
+        {\fancyplain{}{\bfseries\rightmark}}
+\rhead[\fancyplain{}{\bfseries\leftmark}]
+        {\fancyplain{}{\bfseries\thepage}}
+\rfoot[\fancyplain{}{\bfseries\scriptsize Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by Doxygen }]{}
+\lfoot[]{\fancyplain{}{\bfseries\scriptsize Generated on Tue Jun 30 14:43:54 2009 for Elektra Projekt by Doxygen }}
+\cfoot{}
+\newenvironment{Code}
+{\footnotesize}
+{\normalsize}
+\newcommand{\doxyref}[3]{\textbf{#1} (\textnormal{#2}\,\pageref{#3})}
+\newenvironment{DocInclude}
+{\footnotesize}
+{\normalsize}
+\newenvironment{VerbInclude}
+{\footnotesize}
+{\normalsize}
+\newenvironment{Image}
+{\begin{figure}[H]}
+{\end{figure}}
+\newenvironment{ImageNoCaption}{}{}
+\newenvironment{CompactList}
+{\begin{list}{}{
+  \setlength{\leftmargin}{0.5cm}
+  \setlength{\itemsep}{0pt}
+  \setlength{\parsep}{0pt}
+  \setlength{\topsep}{0pt}
+  \renewcommand{\makelabel}{\hfill}}}
+{\end{list}}
+\newenvironment{CompactItemize}
+{
+  \begin{itemize}
+  \setlength{\itemsep}{-3pt}
+  \setlength{\parsep}{0pt}
+  \setlength{\topsep}{0pt}
+  \setlength{\partopsep}{0pt}
+}
+{\end{itemize}}
+\newcommand{\PBS}[1]{\let\temp=\\#1\let\\=\temp}
+\newlength{\tmplength}
+\newenvironment{TabularC}[1]
+{
+\setlength{\tmplength}
+     {\linewidth/(#1)-\tabcolsep*2-\arrayrulewidth*(#1+1)/(#1)}
+      \par\begin{tabular*}{\linewidth}
+             {*{#1}{|>{\PBS\raggedright\hspace{0pt}}p{\the\tmplength}}|}
+}
+{\end{tabular*}\par}
+\newcommand{\entrylabel}[1]{
+   {\parbox[b]{\labelwidth-4pt}{\makebox[0pt][l]{\textbf{#1}}\vspace{1.5\baselineskip}}}}
+\newenvironment{Desc}
+{\begin{list}{}
+  {
+    \settowidth{\labelwidth}{40pt}
+    \setlength{\leftmargin}{\labelwidth}
+    \setlength{\parsep}{0pt}
+    \setlength{\itemsep}{-4pt}
+    \renewcommand{\makelabel}{\entrylabel}
+  }
+}
+{\end{list}}
+\newenvironment{Indent}
+  {\begin{list}{}{\setlength{\leftmargin}{0.5cm}}
+      \item[]\ignorespaces}
+  {\unskip\end{list}}
+\setlength{\parindent}{0cm}
+\setlength{\parskip}{0.2cm}
+\addtocounter{secnumdepth}{1}
+\sloppy
+\usepackage[T1]{fontenc}
diff --git a/doc/elektra-api/latex/err.tex b/doc/elektra-api/latex/err.tex
new file mode 100644 (file)
index 0000000..13e3b3d
--- /dev/null
@@ -0,0 +1,20 @@
+\label{err__err000002}
+ \begin{description}
+\item[Global \doxyref{kdbbReadLock}{p.}{group__backendhelper_g89aa8c310a5720766639c305e643c069} ]sets KDB\_\-ERR\_\-NOLOCK when locking failed \end{description}
+
+
+\label{err__err000003}
+ \begin{description}
+\item[Global \doxyref{kdbbUnlock}{p.}{group__backendhelper_gb969d5d25762464c5f719f4f90757fe8} ]sets KDB\_\-ERR\_\-NOLOCK when locking failed \end{description}
+
+
+\label{err__err000001}
+ \begin{description}
+\item[Global \doxyref{kdbbWriteLock}{p.}{group__backendhelper_gcea5819334a71744a54a6b290a8c3bdc} ]sets KDB\_\-ERR\_\-NOLOCK when locking failed \end{description}
+
+
+\label{err__err000004}
+ \begin{description}
+\item[Global \doxyref{kdbSet\_\-backend}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23} ]In normal execution cases a positive value will be returned. But in some cases you are not able to set keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added with 0.7.1)
+
+\end{description}
diff --git a/doc/elektra-api/latex/group__backend.tex b/doc/elektra-api/latex/group__backend.tex
new file mode 100644 (file)
index 0000000..d5c3374
--- /dev/null
@@ -0,0 +1,498 @@
+\section{KDB Backends :: Elektra framework for pluggable backends}
+\label{group__backend}\index{KDB Backends :: Elektra framework for pluggable backends@{KDB Backends :: Elektra framework for pluggable backends}}
+The tactics to create pluggable backends to libelektra.so.  
+\subsection*{Enumerations}
+\begin{CompactItemize}
+\item 
+enum {\bf backend\_\-t} \{ \par
+{\bf KDB\_\-BE\_\-OPEN} = 1, 
+{\bf KDB\_\-BE\_\-CLOSE} = 1$<$$<$1, 
+{\bf KDB\_\-BE\_\-GET} = 1$<$$<$2, 
+{\bf KDB\_\-BE\_\-SET} = 1$<$$<$3, 
+\par
+{\bf KDB\_\-BE\_\-VERSION} = 1$<$$<$4, 
+{\bf KDB\_\-BE\_\-DESCRIPTION} = 1$<$$<$5, 
+{\bf KDB\_\-BE\_\-AUTHOR} = 1$<$$<$6, 
+{\bf KDB\_\-BE\_\-LICENCE} = 1$<$$<$7, 
+\par
+{\bf KDB\_\-BE\_\-END} = 0
+ \}
+\end{CompactItemize}
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+KDB $\ast$ {\bf kdbBackendExport} (const char $\ast$backendName,...)
+\item 
+int {\bf kdbOpen\_\-backend} (KDB $\ast$handle)
+\item 
+int {\bf kdbClose\_\-backend} (KDB $\ast$handle)
+\item 
+ssize\_\-t {\bf kdbGet\_\-backend} (KDB $\ast$handle, KeySet $\ast$returned, const Key $\ast$parentKey)
+\item 
+ssize\_\-t {\bf kdbSet\_\-backend} (KDB $\ast$handle, KeySet $\ast$returned, const Key $\ast$parentKey)
+\item 
+{\bf KDBEXPORT} (backend)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+The tactics to create pluggable backends to libelektra.so. 
+
+\subsection{Introduction}\label{group__backend_intro}
+\begin{Desc}
+\item[Since:]Since version 0.4.9, Elektra can dynamically load different key storage backends.
+
+Since version 0.7.0 Elektra can have multiple storage backends, called just backends henceforth, at once for different purposes.\end{Desc}
+\begin{Desc}
+\item[Definition: You refers to the implementation of the function in this specification.]If you read the documentation about \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d}, then the caller is \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} which is the only function which can and will call (invoke) you. The Preconditions will always be met by the caller, you can count on them. But you (as said before we speak about the function) need to take care that all Postconditions are met.\end{Desc}
+\subsubsection{Overview}\label{group__backend_overview}
+The methods of class KDB that are backend dependent are only \doxyref{kdbOpen\_\-backend()}{p.}{group__backend_g8ce84f1f6defc40a9239058991e257da}, \doxyref{kdbClose\_\-backend()}{p.}{group__backend_g6dd468490ad54c3a52f7e5083b8c721b}, \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d}, \doxyref{kdbSet\_\-backend()}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23} and \doxyref{KDBEXPORT()}{p.}{group__backend_g40bb01174ff716b57525c4dd7730aac8} to export these methods. A backend must implement each of them. A detailed specification of these methods and methods needed in that context follows in this Documentation Module.
+
+The other KDB methods are higher level. They use the above methods to do their job, and generally don't have to be reimplemented for a different backend, but there might be a solution to do so for higher performance in future. kdbh$\ast$ methods are for access to the internals of KDB, which will be passed to all functions.\subsubsection{Include Files}\label{group__backend_incl}
+The backend implementation must include: 
+
+\begin{Code}\begin{verbatim}#include <kdbbackend.h>
+\end{verbatim}
+\end{Code}
+
+ to have direct access to the structs, which is currently needed to access the capability structure.
+
+Don't include kdb.h, it will be automatically included and some macros will avoid redefining structs where you have more insight from a backend than you would normally have. Additionally you get the declaration of all functions described here, except the one you have to implement.\subsubsection{Dynamic Mounting}\label{group__backend_dyn}
+An elektrified program will use elektra/libelektra-default.so as its default backend. This backend provides the system/ hierarchy and some base configuration in system/elektra for elektra itself. Everything below system/ and the other hierarchies can be stored in any different backend. This is allowed through the technique mounting. A backend can be mounted to any path except system/ and system/elektra.
+
+A backends is guaranteed to be loaded whenever calling \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} or \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} requires the backend, but may already be loaded at \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f}. It might be loaded explizit by \doxyref{kdbMount()}{p.}{group__kdb_g40e35f26cc69bd43ef1b2207f4fa121b} at any time after \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f}. Backends get a chance to initialize by calling \doxyref{kdbOpen\_\-backend()}{p.}{group__backend_g8ce84f1f6defc40a9239058991e257da} whenever they are loaded.
+
+Using \doxyref{kdbUnmount()}{p.}{group__kdb_g400ca66a9bdc04ecadb66d84dc06bd55} a backend may closed during runtime. All backends will be closed when \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859} is called. Backends might be unloaded after some time of inactivity or other reasons. After loading backends get a chance to cleanup by calling \doxyref{kdbClose\_\-backend()}{p.}{group__backend_g6dd468490ad54c3a52f7e5083b8c721b}.
+
+That means it is not guaranteed that the backend live the whole time nor it will be loaded only one time. A tactic to handle this well is to build stateless backends referring to \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d} and \doxyref{kdbSet\_\-backend()}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23}. That means that there is no more information present than in the storage itself. Be aware that you must not have any global variables in your backend. Read more about that in \doxyref{kdbOpen\_\-backend()}{p.}{group__backend_g8ce84f1f6defc40a9239058991e257da}. But to be stateless you also have to consider not to store any other than caching information into \doxyref{kdbhGetBackendData()}{p.}{group__backendhandle_ge463be8651422015fd12811ed66d20f3}. I repeat: it must be possible to restore everything dynamically stored without exception.\subsubsection{Library Names}\label{group__backend_lib}
+Elektra source code or development package provides a skeleton and Makefile to implement a backend. Copy src/backends/template to have a good starting point. See the CODING document to know how to integrate the backend in the build system or how to compile it external.
+
+A backend is defined by a single name, for example {\tt BACKENDNAME}, that causes libelektra.so look for its library as {\tt libelektra-BACKENDNAME.so}.
+
+\begin{Desc}
+\item[Example of a complete backend:]
+
+\begin{Code}\begin{verbatim}//
+// This is my implementation for an Elektra backend storage.
+//
+// To compile it:
+// $ cc -fpic `pkg-config --cflags elektra` -o myback.o -c myback.c
+// $ cc -shared -fpic `pkg-config --libs elektra` -o libelektra-myback.so myback.o
+//
+// To use it:
+// $ preload mount myback system/myback myback /tmp/nofile
+// $ kdb ls system/myback
+// $ kdb set system/myback/key "value"
+// $ kdb get system/myback/key
+//
+
+#include <kdbbackend.h>
+
+#define BACKENDNAME "backend"
+
+
+int kdbOpen_backend(KDB *handle) {...}
+int kdbClose_backend(KDB *handle) {...}
+int kdbGet_backend(KDB handle, KeySet *returned, Key *key) {...}
+int kdbSet_backend(KDB handle, KeySet *returned, Key *key) {...}
+
+KDBEXPORT(backend) {
+        return kdbBackendExport(BACKENDNAME,
+                KDB_BE_OPEN,  &kdbOpen_backend,
+                KDB_BE_CLOSE, &kdbClose_backend,
+                KDB_BE_GET,   &kdbGet_backend,
+                KDB_BE_SET,   &kdbSet_backend,
+                KDB_BE_END);
+}
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+In the example, the $\ast$\_\-backend() methods can have other random names, since you'll correctly pass them later to \doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3}. It is recommended to use names according to your backendname to avoid name clashes. Be aware that every symbol name in the linked application must be unique.
+
+Don't copy above example out, use src/backends/template, it does compile as-is and does some initialization and cleanup already.
+
+Elektra source code tree includes several backend implementations {\tt https://svn.libelektra.org/svn/elektra/trunk/src/backends/} that can also be used as a reference.\subsection{Details}\label{group__backend_backenddetail}
+\subsubsection{Introduction}\label{group__backend_intro}
+Capabilities may make your live much easier. If it is impossible, very hard or would impact performance badly you may leave out some parts described here, but need to declare that you have done so with capabilites.
+
+It is allowed to provide additional information, even if you declared you don't have it. If you declare that you are capable of doing something, you must provide it without exceptions.\subsubsection{Owner}\label{group__backend_owner}
+You need to set the owner of keys by \doxyref{keySetOwner()}{p.}{group__keyname_ga899d9f0251cb98a89761ef112910eca}. Owner is the name to whom a specific key of the user/ hierarchy belongs. If you declare kdbcGetnoOwner() you need not to set the owner of the keys. It also means that even if you want to get keys from another user hierarchy you get yours.\subsubsection{Values}\label{group__backend_value}
+Values are the central information of keys next to the name describing what informations it holds. Parse them out of your backend and put them into the key with \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2}. The information will be duplicated, so you might need to free() your string. Don't try to directly access key-$>$data, things may change there and your backend might be compiled with a different libc than elektra. If you support types, you might want to use keySetRaw() to not change the key type. If you don't support values for all keys declare kdbcGetnoValue().\subsubsection{IDs}\label{group__backend_id}
+You need to set uid respective gid for any key not having the uid and gid of the current process. This will be set by default in every key. You can do it with \doxyref{keySetUID()}{p.}{group__keymeta_gb5f284f5ecd261e0a290095f50ba1af7} and \doxyref{keySetGID()}{p.}{group__keymeta_g9e3d0fb3f7ba906e067727b9155d22e3}. Declaring kdbcGetnoUID() and kdbcGetnoGID() you need not set uid and gid.\subsubsection{Mode}\label{group__backend_mode}
+Mode shows what can be done with the key having or not having the above uid and gid. Use \doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3} to set the correct mode description, read the description in \doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3} for the semantics of the 3 octal representation. Declaring kdbcGetnoMode() means mode will remain default.
+
+The very related method \doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75} sets the executable bits of mode. Even if your backend does not support mode, it might support directories, meaning that keys have the mode 0664 or 0775 for directories. Declaring kdbcGetnoDir() means that the backend is flat, no key will be true for \doxyref{keyIsDir()}{p.}{group__keytest_gc0a10c602d52a35f81347e8a32312017} and so can't have any subkeys.\subsubsection{Timing}\label{group__backend_timing}
+Keys should have exact timing information of their modification and access times. Use \doxyref{keySetATime()}{p.}{group__keymeta_g995d8b84731673c88c7c01f3fed538b9}, \doxyref{keySetMTime()}{p.}{group__keymeta_g481d8997187992fe4bbf288bc8ef4db7} and \doxyref{keySetCTime()}{p.}{group__keymeta_g9f502ecab8ab43f0b17220fcc95f3fa5} to store appropriate information. ATime need to be stored in database, if you stat a key the backend need to return the time \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} was last used for the keys. If you don't support this, declare kdbcGetnoATime() and simple store time(0) in the atime. This must be the same for every key for a single \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d}. If you only stat keys with \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}, see below, then the access time should not be updated. MTime is the last modification time of value or comment. If you don't support this, declare kdbcGetnoMTime() and simple store time(0) in the mtime. This must be the same for every key for a single \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d}. CTime is the last change time of any metadata or add/remove of subkeys. If you don't support this, declare kdbcGetnoCTime() and simple store time(0) in the ctime. This must be the same for every key for a single \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d}.\subsubsection{Types}\label{group__backend_type}
+Keys having value and comment can be one of two fundamental types, string or binary, both called value. While string is a null terminated utf8 character sequence, binary is any data of a specific length. Be sure to use \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} for string and \doxyref{keySetBinary()}{p.}{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7} if you want to store binary data. If you do not support one of these, be sure to declare kdbcGetnoBinary() or kdbcGetnoString(), if you don't support both make sure to also declare kdbcGetnoValue().
+
+Using keySetRaw() does not set the type, be sure to use \doxyref{keySetType()}{p.}{group__keymeta_gb92003db4b938594df48807c16766bf7} afterwards. This can be KEY\_\-TYPE\_\-STRING and KEY\_\-TYPE\_\-BINARY or any other type in type\_\-t, leading to same results as explained above, but also any other number in the range of type\_\-t. Declare kdbcGetnoTypes() when your backend does not support arbitrary types. 
+
+\subsection{Enumeration Type Documentation}
+\index{backend@{backend}!backend\_\-t@{backend\_\-t}}
+\index{backend\_\-t@{backend\_\-t}!backend@{backend}}
+\subsubsection[backend\_\-t]{\setlength{\rightskip}{0pt plus 5cm}enum {\bf backend\_\-t}}\label{group__backend_ge857eadce7085ecd3a24671a1826940a}
+
+
+Switches to denote the backend methods. Used in calls to \doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3}. \begin{Desc}
+\item[Enumerator: ]\par
+\begin{description}
+\index{KDB\_\-BE\_\-OPEN@{KDB\_\-BE\_\-OPEN}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-OPEN@{KDB\_\-BE\_\-OPEN}}\item[{\em 
+KDB\_\-BE\_\-OPEN\label{group__backend_gge857eadce7085ecd3a24671a1826940a4293cef5efac0aa0ad61c18e41c0848c}
+}]Next arg is backend for \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} \index{KDB\_\-BE\_\-CLOSE@{KDB\_\-BE\_\-CLOSE}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-CLOSE@{KDB\_\-BE\_\-CLOSE}}\item[{\em 
+KDB\_\-BE\_\-CLOSE\label{group__backend_gge857eadce7085ecd3a24671a1826940a28f303117ceb95838f92780007407955}
+}]Next arg is backend for \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859} \index{KDB\_\-BE\_\-GET@{KDB\_\-BE\_\-GET}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-GET@{KDB\_\-BE\_\-GET}}\item[{\em 
+KDB\_\-BE\_\-GET\label{group__backend_gge857eadce7085ecd3a24671a1826940ae1e894d0fe6e3f05f444f03de2bc7885}
+}]Next arg is backend for \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} \index{KDB\_\-BE\_\-SET@{KDB\_\-BE\_\-SET}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-SET@{KDB\_\-BE\_\-SET}}\item[{\em 
+KDB\_\-BE\_\-SET\label{group__backend_gge857eadce7085ecd3a24671a1826940ab2fe708111d49ea21669db6bd7276007}
+}]Next arg is backend for \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} \index{KDB\_\-BE\_\-VERSION@{KDB\_\-BE\_\-VERSION}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-VERSION@{KDB\_\-BE\_\-VERSION}}\item[{\em 
+KDB\_\-BE\_\-VERSION\label{group__backend_gge857eadce7085ecd3a24671a1826940aeca3339f3b62cd2136646cb59678603e}
+}]Next arg is char $\ast$ for Version \index{KDB\_\-BE\_\-DESCRIPTION@{KDB\_\-BE\_\-DESCRIPTION}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-DESCRIPTION@{KDB\_\-BE\_\-DESCRIPTION}}\item[{\em 
+KDB\_\-BE\_\-DESCRIPTION\label{group__backend_gge857eadce7085ecd3a24671a1826940ac60eb75a1391a6a4d30420f4079e4244}
+}]Next arg is char $\ast$ for Description \index{KDB\_\-BE\_\-AUTHOR@{KDB\_\-BE\_\-AUTHOR}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-AUTHOR@{KDB\_\-BE\_\-AUTHOR}}\item[{\em 
+KDB\_\-BE\_\-AUTHOR\label{group__backend_gge857eadce7085ecd3a24671a1826940adc638374d367d0a52d9201f782eb9f5c}
+}]Next arg is char $\ast$ for Author \index{KDB\_\-BE\_\-LICENCE@{KDB\_\-BE\_\-LICENCE}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-LICENCE@{KDB\_\-BE\_\-LICENCE}}\item[{\em 
+KDB\_\-BE\_\-LICENCE\label{group__backend_gge857eadce7085ecd3a24671a1826940ad3348e5ab9428da29ba7b51886b87bbe}
+}]Next arg is char $\ast$ for Licence \index{KDB\_\-BE\_\-END@{KDB\_\-BE\_\-END}!backend@{backend}}\index{backend@{backend}!KDB\_\-BE\_\-END@{KDB\_\-BE\_\-END}}\item[{\em 
+KDB\_\-BE\_\-END\label{group__backend_gge857eadce7085ecd3a24671a1826940adab7f08d13e598d0fafa2db1aa707b2f}
+}]End of arguments \end{description}
+\end{Desc}
+
+
+
+\subsection{Function Documentation}
+\index{backend@{backend}!kdbBackendExport@{kdbBackendExport}}
+\index{kdbBackendExport@{kdbBackendExport}!backend@{backend}}
+\subsubsection[kdbBackendExport]{\setlength{\rightskip}{0pt plus 5cm}KDB$\ast$ kdbBackendExport (const char $\ast$ {\em backendName}, \/   {\em ...})}\label{group__backend_g2b02d413c4bb3c11e0f2560937b82db3}
+
+
+This function must be called by a backend's kdbBackendFactory() to define the backend's methods that will be exported.
+
+See \doxyref{KDBEXPORT()}{p.}{group__backend_g40bb01174ff716b57525c4dd7730aac8} how to use it for backends.
+
+The order and number of arguments are flexible (as in \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} and \doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c}) to let libelektra.so evolve without breaking its ABI compatibility with backends. So for each method a backend must export, there is a flag defined by \doxyref{backend\_\-t}{p.}{group__backend_ge857eadce7085ecd3a24671a1826940a}. Each flag tells \doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3} which method comes next. A backend can have no implementation for a few methods that have default inefficient high-level implementations and to use these defaults, simply don't pass anything to \doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3} about them.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em backendName}]a simple name for this backend \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]an object that contains all backend informations needed by libelektra.so \end{Desc}
+\index{backend@{backend}!kdbClose\_\-backend@{kdbClose\_\-backend}}
+\index{kdbClose\_\-backend@{kdbClose\_\-backend}!backend@{backend}}
+\subsubsection[kdbClose\_\-backend]{\setlength{\rightskip}{0pt plus 5cm}int kdbClose\_\-backend (KDB $\ast$ {\em handle})}\label{group__backend_g6dd468490ad54c3a52f7e5083b8c721b}
+
+
+Finalize the backend. Called prior to unloading the backend dynamic module. Should ensure that no functions or static/global variables from the module will ever be accessed again.
+
+Make sure to free all memory that your backend requested at runtime.
+
+Specifically make sure to capDel() all capabilites and free your backendData in \doxyref{kdbhGetBackendData()}{p.}{group__backendhandle_ge463be8651422015fd12811ed66d20f3}.
+
+After this call, libelektra.so will unload the backend library, so this is the point to shutdown any affairs with the storage.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success, anything else otherwise. \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859} \end{Desc}
+\index{backend@{backend}!KDBEXPORT@{KDBEXPORT}}
+\index{KDBEXPORT@{KDBEXPORT}!backend@{backend}}
+\subsubsection[KDBEXPORT]{\setlength{\rightskip}{0pt plus 5cm}KDBEXPORT (backend)}\label{group__backend_g40bb01174ff716b57525c4dd7730aac8}
+
+
+All KDB methods implemented by the backend can have random names, except kdbBackendFactory(). This is the single symbol that will be looked up when loading the backend, and the first method of the backend implementation that will be called.
+
+Its purpose is to publish the exported methods for libelektra.so. The implementation inside the provided skeleton is usually enough: simply call \doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3} with all methods that must be exported.
+
+The first paramter is the name of the backend. Then every backend must have: {\tt KDB\_\-BE\_\-OPEN}, {\tt KDB\_\-BE\_\-CLOSE}, {\tt KDB\_\-BE\_\-GET} and {\tt KDB\_\-BE\_\-SET} 
+
+You might also give following information by char $\ast$: {\tt KDB\_\-BE\_\-VERSION}, {\tt KDB\_\-BE\_\-AUTHOR}, {\tt KDB\_\-BE\_\-LICENCE} and {\tt KDB\_\-BE\_\-DESCRIPTION} 
+
+You must use static \char`\"{}char arrays\char`\"{} in a read only segment. Don't allocate storage, it won't be freed.
+
+With capability you can get that information on runtime from any backend with kdbGetCapability().
+
+The last parameter must be {\tt KDB\_\-BE\_\-END}.
+
+\begin{Desc}
+\item[Returns:]\doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3} with the above described parameters. \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbBackendExport()}{p.}{group__backend_g2b02d413c4bb3c11e0f2560937b82db3} for an example 
+
+kdbOpenBackend() \end{Desc}
+\index{backend@{backend}!kdbGet\_\-backend@{kdbGet\_\-backend}}
+\index{kdbGet\_\-backend@{kdbGet\_\-backend}!backend@{backend}}
+\subsubsection[kdbGet\_\-backend]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbGet\_\-backend (KDB $\ast$ {\em handle}, \/  KeySet $\ast$ {\em returned}, \/  const Key $\ast$ {\em parentKey})}\label{group__backend_gf3c9c38b7710c435e3caed9e8df2184d}
+
+
+Retrieve information from a permanent storage to construct a keyset.\subsection{Introduction}\label{group__backend_intro}
+This function does everything related to get keys out from a backend. There is only one function for that purpose to make implementation and locking much easier.
+
+The keyset {\tt returned} needs to be filled with information so that the application using elektra can access it. See the live cycle of a comment to understand: 
+
+\begin{Code}\begin{verbatim}kdbGet_backend(KDB *handle, KeySet *returned, Key *parentKey)
+{
+        // the task of kdbGet_backend is to retrieve the comment out of the permanent storage
+        Key *key = keyDup (parentKey); // generate a new key to hold the information
+        char *comment;
+        loadfromdisc (comment);
+        keySetComment (key, comment, size); // set the information
+        ksAppendKey(returned, key);
+}
+
+// Now return to kdbGet
+int kdbGet(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+        kdbGet_backend (handle, keyset, 0);
+        // postprocess the keyset and return it
+}
+
+// Now return to usercode, waiting for the comment
+void usercode (Key *key)
+{
+        kdbGet (handle, keyset, parentKey, 0);
+        key = ksCurrent (keyset, key); // lookup the key from the keyset
+        keyGetComment (key); // now the usercode retrieves the comment
+}
+\end{verbatim}
+\end{Code}
+
+ Of course not only the comment, but all information of every key in the keyset {\tt returned} need to be fetched from permanent storage and stored in the key. So this specification needs to give an exhaustive list of information present in a key.\subsection{Conditions}\label{group__backend_conditions}
+\begin{Desc}
+\item[Precondition:]The caller \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will make sure before you are called that the parentKey:\begin{itemize}
+\item is a valid key (means that it is a system or user key).\item is below (see \doxyref{keyIsBelow()}{p.}{group__keytest_g03332b5d97c76a4fd2640aca4762b8df}) your mountpoint and that your backend is responsible for it.\item has \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} set when you should only stat keys (see later). and that the returned:\item is a valid keyset.\item has {\tt all} keys with the flag KEY\_\-FLAG\_\-SYNC set.\item {\tt may} has keys with the flag KEY\_\-FLAG\_\-STAT set.\item contains only valid keys direct below (see \doxyref{keyIsDirectBelow()}{p.}{group__keytest_g4f175aafd98948ce6c774f3bd92b72ca}) your parentKey. That also means, that the parentKey will not be in that keyset.\item have keyIsStat() set when the value/comment information is not necessary.\item is in a sorted order, see \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}. and that the handle:\begin{itemize}
+\item is a valid KDB for your backend.\item that kdbhGetBackendHandle() contains the same handle for lifetime \doxyref{kdbOpen\_\-backend()}{p.}{group__backend_g8ce84f1f6defc40a9239058991e257da} until \doxyref{kdbClose\_\-backend()}{p.}{group__backend_g6dd468490ad54c3a52f7e5083b8c721b} was called.\end{itemize}
+\end{itemize}
+
+
+The caller \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will make sure that afterwards you were called, whenever the user requested it with the options, that:\begin{itemize}
+\item hidden keys they will be thrown away.\item dirs or only dirs \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will remove the other.\item you will be called again recursively with all subdirectories.\item the keyset will be sorted when needed.\item the keys in returned having KEY\_\-FLAG\_\-SYNC will be sorted out.\end{itemize}
+\end{Desc}
+\begin{Desc}
+\item[Invariant:]There are no global variables and \doxyref{kdbhGetBackendData()}{p.}{group__backendhandle_ge463be8651422015fd12811ed66d20f3} only stores information which can be regenerated any time. The handle is the same when it is the same backend.\end{Desc}
+\begin{Desc}
+\item[Postcondition:]The keyset {\tt returned} has the {\tt parentKey} and all keys direct below (\doxyref{keyIsDirectBelow()}{p.}{group__keytest_g4f175aafd98948ce6c774f3bd92b72ca}) with all information from the storage. Make sure to return all keys, all directories and also all hidden keys. If some of them are not wished, the caller \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will drop these keys, see above.\end{Desc}
+\subsection{Details}\label{group__backend_detail}
+Now lets look at an example how the typical \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d} might be implemented. To explain we introduce some pseudo functions which do all the work with the storage (which is of course 90\% of the work for a real backend):\begin{itemize}
+\item find\_\-key() gets an key out from the storage and memorize the position.\item next\_\-key() will find the next key and return it (with the name).\item fetch\_\-key() gets out all information of a key from storage (details see below example). It removes the \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} and \doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950} flag afterwards.\item stat\_\-key() gets all meta information (everything but value and comment). It removes the key \doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950} flag afterwards. returns the next key out from the storage. The typical loop now will be like: 
+
+\begin{Code}\begin{verbatim}ssize_t kdbGet_backend(KDB *handle, KeySet *update, const Key *parentKey) {
+        Key * current;
+        KeySet *returned = ksNew(ksGetSize(update)*2, KS_END);
+
+        find_key (parentKey);
+        current = keyDup (parentKey);
+        if (keyNeedStat(parentKey))
+        {
+                current = stat_key(current);
+        } else {
+                current = fetch_key(current);
+        }
+        clear_bit (KEY_FLAG_SYNC, current->flags);
+        ksAppendKey(returned, current);
+
+        while ((current = next_key()) != 0)
+        {
+                // search if key was passed in update by caller
+                Key * tmp = ksLookup (update, current, KDB_O_WITHOWNER|KDB_O_POP);
+                if (tmp) current = tmp; // key was passed, so use it
+                if (keyNeedStat(parentKey) || keyNeedStat(current))
+                {
+                        current = stat_key (current);
+                        set_bit (KEY_FLAG_STAT, current->flags);
+                } else {
+                        current = fetch_key(current);
+                }
+                clear_bit (KEY_FLAG_SYNC, current->flags);
+                ksAppendKey(returned, current);
+                // TODO: delete lookup key
+        }
+
+        if (error_happened())
+        {
+                errno = restore_errno();
+                return -1;
+        }
+
+        ksClear (update); // the rest of update keys is not in storage anymore
+        ksAppend(update, returned); // append the keys
+        ksDel (returned);
+
+        return nr_keys();
+}
+\end{verbatim}
+\end{Code}
+
+\end{itemize}
+
+
+\begin{Desc}
+\item[Note:]- returned and update are separated, for details why see \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004}\begin{itemize}
+\item the bit KEY\_\-FLAG\_\-SYNC is always cleared, see postconditions\end{itemize}
+\end{Desc}
+So your mission is simple: Search the {\tt parentKey} and add it and then search all keys below and add them too, of course with all requested information (which is only depended on \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5}).\subsection{Stat}\label{group__backend_stat}
+Sometimes value and comment are not of interest, but metadata. To avoid a potential time-consuming \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} you can \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} the {\tt parentKey}. If the backend supports a less time-consuming method to just get names and metadata, implement it, otherwise declare kdbcGetnoStat().
+
+The implementation works as follows: When the {\tt parentKey} has \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} set, all keys need to be stated instead of getting them. So the keys you \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45} don't have a value nor a comment and make sure that KEY\_\-FLAG\_\-SYNC is not set, but \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} must be set for all keys which are only stated.
+
+The keys in {\tt returned} may already have \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} set. These keys must keep the status \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} and you don't need to get the value and comment. See the example above for code.\subsection{Updating}\label{group__backend_updating}
+To get all keys out of the storage over and over again can be very inefficient. You might know a more efficient method to know if the key needs update or not, e.g. by stating it or by an external time stamp info. In that case you can make use of {\tt returned} KeySet. There are following possibilities:\begin{itemize}
+\item The key is in returned and up to date. You just need to remove the KEY\_\-FLAG\_\-SYNC flag.\item The key is in returned but \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} is true. You just need to stat the key and remove the KEY\_\-FLAG\_\-SYNC flag and set the KEY\_\-FLAG\_\-STAT flag.\item The key is in returned, \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} is false (for the key and the {\tt parentKey}) and you know that the key has changed. You need to fully retrieve the key and remove the KEY\_\-FLAG\_\-SYNC flag.\item The key is not in returned, the {\tt parentKey} has \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5}. You just need to stat the key. Make sure that KEY\_\-FLAG\_\-SYNC is not set, but KEY\_\-FLAG\_\-STAT needs to be set. Append the key to {\tt returned}.\item The key is not in returned and the {\tt parentKey} \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} is false. You need to fully retrieve the key out of storage, clear KEY\_\-FLAG\_\-STAT and KEY\_\-FLAG\_\-SYNC and \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45} it to the {\tt returned} keyset.\end{itemize}
+
+
+\begin{Desc}
+\item[Note:]You must clear the flag KEY\_\-FLAG\_\-SYNC at the very last point where no more modification on the key will take place, because any modification on the key will set the KEY\_\-FLAG\_\-SYNC flag again. With that \doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950} will return true and the caller will sort this key out.\end{Desc}
+\subsection{only Full Get}\label{group__backend_fullget}
+In some backends it is not useful to get only a part of the configuration, because getting all keys would take as long as getting some. For this situation, you can declare onlyFullGet, see kdbcGetonlyFullGet().
+
+The only valid call for your backend is then that {\tt parentKey} equals the {\tt mountpoint}. For all other {\tt parentKey} you must, add nothing and just return 0.
+
+
+
+\begin{Code}\begin{verbatim}if (strcmp (keyName(kdbhGetMountpoint(handle)), keyName(parentKey))) return 0;
+\end{verbatim}
+\end{Code}
+
+
+
+If the {\tt parentKey} is your mountpoint you will of course fetch all keys, and not only the keys direct below the {\tt parentKey}. So {\tt returned} is valid iff:\begin{itemize}
+\item every key is below ( \doxyref{keyIsBelow()}{p.}{group__keytest_g03332b5d97c76a4fd2640aca4762b8df}) the parentKey\item every key has a direct parent (\doxyref{keyIsDirectBelow()}{p.}{group__keytest_g4f175aafd98948ce6c774f3bd92b72ca}) in the keyset\end{itemize}
+
+
+\begin{Desc}
+\item[Note:]This statement is only valid for backends with kdbcGetonlyFullGet() set.
+
+If any calls you use change errno, make sure to restore the old errno.\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} for caller.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em returned}]contains a keyset where the function need to append the keys got from the storage. There might be also some keys inside it, see conditions. You may use them to support efficient updating of keys, see \doxyref{Updating}{p.}{group__backend_updating}. \item[{\em parentKey}]contains the information below which key the keys should be gotten.\end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]Return how many keys you added.
+
+-1 on failure, the current key in returned shows the position. In normal execution cases a positive value will be returned. But in some cases you are not able to get keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added in 0.7.1) \end{Desc}
+\index{backend@{backend}!kdbOpen\_\-backend@{kdbOpen\_\-backend}}
+\index{kdbOpen\_\-backend@{kdbOpen\_\-backend}!backend@{backend}}
+\subsubsection[kdbOpen\_\-backend]{\setlength{\rightskip}{0pt plus 5cm}int kdbOpen\_\-backend (KDB $\ast$ {\em handle})}\label{group__backend_g8ce84f1f6defc40a9239058991e257da}
+
+
+Initialize the backend. This is the first method kdbOpenBackend() calls after dynamically loading the backend library.
+
+This method is responsible of:\begin{itemize}
+\item backend's specific configuration gathering\item all backend's internal structs initialization\item initial setup of all I/O details such as opening a file, connecting to a database, etc\end{itemize}
+
+
+If your backend does not support all aspects described in \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d} and \doxyref{kdbSet\_\-backend()}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23} you need capabilities to export this information. Per default you declare to be fully compliant to the specification given here, to change it get a pointer to KDBCap structure by using \doxyref{kdbhGetCapability()}{p.}{group__backendhandle_g090cfa7483afbb159b75c975eb1d513c}.
+
+You may also read the configuration you can get with \doxyref{kdbhGetConfig()}{p.}{group__backendhandle_gb14dc8708c2ae4ffcba6cfb130019115} and transform it into other structures used by your backend.
+
+But be aware that you don't have any global variables. If you do your backend will not be threadsafe. You can use \doxyref{kdbhSetBackendData()}{p.}{group__backendhandle_g97fab712e488c7ec3e198492106724ab} and \doxyref{kdbhGetBackendData()}{p.}{group__backendhandle_ge463be8651422015fd12811ed66d20f3} to store and get any information related to your backend.
+
+The correct substitute for global variables will be: 
+
+\begin{Code}\begin{verbatim}struct _GlobalData{ int global; };
+typedef struct _GlobalData GlobalData;
+int kdbOpen_backend(KDB *handle) {
+        PasswdData *data;
+        data=malloc(sizeof(PasswdData));
+        data.global = 20;
+        kdbhSetBackendData(handle,data);
+}
+\end{verbatim}
+\end{Code}
+
+
+
+Make sure to free everything in \doxyref{kdbClose\_\-backend()}{p.}{group__backend_g6dd468490ad54c3a52f7e5083b8c721b}.
+
+\begin{Desc}
+\item[Returns:]0 on success \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} \end{Desc}
+\index{backend@{backend}!kdbSet\_\-backend@{kdbSet\_\-backend}}
+\index{kdbSet\_\-backend@{kdbSet\_\-backend}!backend@{backend}}
+\subsubsection[kdbSet\_\-backend]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbSet\_\-backend (KDB $\ast$ {\em handle}, \/  KeySet $\ast$ {\em returned}, \/  const Key $\ast$ {\em parentKey})}\label{group__backend_g2d86ff43b693d59d4b82b597756e9e23}
+
+
+Store a keyset permanently.
+
+This function does everything related to set and remove keys in a backend. There is only one function for that purpose to make implementation and locking much easier.
+
+The keyset {\tt returned} was filled in with information from the application using elektra and the task of this function is to store it in a permanent way so that a subsequent call of \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d} can rebuild the keyset as it was before. See the live cycle of a comment to understand: 
+
+\begin{Code}\begin{verbatim}void usercode (Key *key)
+{
+        keySetComment (key, "mycomment"); // the usercode stores a comment for the key
+        ksAppendKey(keyset, key); // append the key to the keyset
+        kdbSet (handle, keyset, 0, 0);
+}
+
+// so now kdbSet is called
+int kdbSet(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+        // find appropriate backend
+        kdbSet_backend (handle, keyset, 0); // the keyset with the key will be passed to this function
+}
+
+// so now kdbSet_backend(), which is the function described here, is called
+kdbSet_backend(KDB *handle, KeySet *keyset, Key *parentKey)
+{
+        // the task of kdbSet_backend is now to store the comment
+        Key *key = ksCurrent (keyset); // get out the key where the user set the comment before
+        char *comment = allocate(size);
+        keyGetComment (key, comment, size);
+        savetodisc (comment);
+}
+\end{verbatim}
+\end{Code}
+
+ Of course not only the comment, but all information of every key in the keyset {\tt returned} need to be stored permanetly. So this specification needs to give an exhaustive list of information present in a key.
+
+\begin{Desc}
+\item[Precondition:]The keyset {\tt returned} holds all keys which must be saved permanently for this keyset. The keyset is sorted and rewinded. All keys having children must be true for \doxyref{keyIsDir()}{p.}{group__keytest_gc0a10c602d52a35f81347e8a32312017}.
+
+The {\tt parentKey} is the key which is the ancestor for all other keys in the keyset. The first key of the keyset {\tt returned} has the same keyname. The parentKey is below the mountpoint, see \doxyref{kdbhGetMountpoint()}{p.}{group__backendhandle_g8b5612940fc9bc56e99c15ecc427cbb2}.
+
+The caller kdbSet will fulfill following parts:\begin{itemize}
+\item If the user does not want hidden keys they will be thrown away. All keys in {\tt returned} need to be stored permanently.\item If the user does not want dirs or only dirs \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will remove the other.\item Sorting of the keyset. It is not important in which order the keys are appended. So make sure to set all keys, all directories and also all hidden keys. If some of them are not wished, the caller \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} will sort them out.\end{itemize}
+\end{Desc}
+\begin{Desc}
+\item[Invariant:]There are no global variables and \doxyref{kdbhGetBackendData()}{p.}{group__backendhandle_ge463be8651422015fd12811ed66d20f3} only stores information which can be regenerated any time. The handle is the same when it is the same backend.\end{Desc}
+\begin{Desc}
+\item[Postcondition:]The information of the keyset {\tt returned} is stored permanently.\end{Desc}
+When some keys have KEY\_\-FLAG\_\-REMOVE set, that means return true for \doxyref{keyNeedRemove()}{p.}{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9}, remove the keys instead of getting them. In this case the sorting order will be the reverse way, first will be the children, then the parentKey when iterating over the KeySet returned.
+
+Lock your permanent storage in an exclusive way, no access of a concurrent \doxyref{kdbSet\_\-backend()}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23} or \doxyref{kdbGet\_\-backend()}{p.}{group__backend_gf3c9c38b7710c435e3caed9e8df2184d} is possible and these methods block until the function has finished. Otherwise declare kdbcGetnoLock().
+
+\begin{Desc}
+\item[See also:]\doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} for caller.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em returned}]contains a keyset with relevant keys \item[{\em parentKey}]contains the information where to set the keys\end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]When everything works gracefully return the number of keys you set. The cursor position and the keys remaining in the keyset are not important.
+
+Return 0 on success with no changed key in database
+
+Return -1 on failure.\end{Desc}
+\begin{Desc}
+\item[Note:]If any calls you use change errno, make sure to restore the old errno.\end{Desc}
+\begin{Desc}
+\item[{\bf Error}]In normal execution cases a positive value will be returned. But in some cases you are not able to set keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added with 0.7.1)\end{Desc}
+You also have to make sure that \doxyref{ksGetCursor()}{p.}{group__keyset_gffe507ab9281c322eb16c3e992075d29} shows to the position where the error appeared. 
\ No newline at end of file
diff --git a/doc/elektra-api/latex/group__backendhandle.tex b/doc/elektra-api/latex/group__backendhandle.tex
new file mode 100644 (file)
index 0000000..889912d
--- /dev/null
@@ -0,0 +1,245 @@
+\section{KDB Backends :: KDB access functions}
+\label{group__backendhandle}\index{KDB Backends :: KDB access functions@{KDB Backends :: KDB access functions}}
+Methods to access the backend handle.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+void $\ast$ {\bf kdbhSetBackendData} (KDB $\ast$handle, void $\ast$data)
+\item 
+void $\ast$ {\bf kdbhGetBackendData} (const KDB $\ast$handle)
+\item 
+KDBCap $\ast$ {\bf kdbhSetCapability} (KDB $\ast$handle, KDBCap $\ast$cap)
+\item 
+KDBCap $\ast$ {\bf kdbhGetCapability} (const KDB $\ast$handle)
+\item 
+Trie $\ast$ {\bf kdbhGetTrie} (const KDB $\ast$handle)
+\item 
+void {\bf kdbhSetTrie} (KDB $\ast$handle, Trie $\ast$trie)
+\item 
+const Key $\ast$ {\bf kdbhGetMountpoint} (KDB $\ast$handle)
+\item 
+void {\bf kdbhSetMountpoint} (KDB $\ast$handle, const Key $\ast$mountpoint)
+\item 
+KeySet $\ast$ {\bf kdbhGetConfig} (KDB $\ast$handle)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to access the backend handle. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim} #include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+
+These functions provide access to the information stored in Backend Handles. 
+
+\subsection{Function Documentation}
+\index{backendhandle@{backendhandle}!kdbhGetBackendData@{kdbhGetBackendData}}
+\index{kdbhGetBackendData@{kdbhGetBackendData}!backendhandle@{backendhandle}}
+\subsubsection[kdbhGetBackendData]{\setlength{\rightskip}{0pt plus 5cm}void$\ast$ kdbhGetBackendData (const KDB $\ast$ {\em handle})}\label{group__backendhandle_ge463be8651422015fd12811ed66d20f3}
+
+
+Get the previously set backend-specific {\tt data} from the {\tt handle}.
+
+This is useful when your backend have a backend-global context or environment.
+
+This method will probably be called everytime one of your kdb$\ast$() implementations is called. And if you change something inside the data, you don't have to \doxyref{kdbhSetBackendData()}{p.}{group__backendhandle_g97fab712e488c7ec3e198492106724ab} again, bacause you are manipulating your data, and not a copy of it.
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}struct MyBackendData {
+ int context1;
+ int context2;
+};
+
+int kdbOpen_mybackend(KDB *handle) {
+        struct MyBackendData *context;
+
+        context=malloc(sizeof(struct MyBackendData));
+        // a random initialization...
+        context->context1=1;
+        context->context2=2;
+
+        kdbhSetBackendData(*handle,context);
+
+        return 0;
+}
+
+int kdbGetKey_maybackend(KDB handle) {
+        struct MyBackendData *context;
+
+        context=kdbhGetBackendData(handle);
+
+        // No do something with the context
+        . . .
+
+        return 0;
+}
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+On the \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859} implementation of your backend, you must remember to free all resources associated to your data.
+
+\begin{Desc}
+\item[Example of kdbClose() implementation that correctly cleans the context:]
+
+\begin{Code}\begin{verbatim}int kdbClose_mybackend(KDB &handle) {
+        struct MyBackendData *context;
+
+        context=kdbhGetBackendData(handle);
+        free(context);
+
+        return 0;
+}
+\end{verbatim}
+\end{Code}
+
+ \end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to the data previously set be \doxyref{kdbhSetBackendData()}{p.}{group__backendhandle_g97fab712e488c7ec3e198492106724ab} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\index{backendhandle@{backendhandle}!kdbhGetCapability@{kdbhGetCapability}}
+\index{kdbhGetCapability@{kdbhGetCapability}!backendhandle@{backendhandle}}
+\subsubsection[kdbhGetCapability]{\setlength{\rightskip}{0pt plus 5cm}KDBCap$\ast$ kdbhGetCapability (const KDB $\ast$ {\em handle})}\label{group__backendhandle_g090cfa7483afbb159b75c975eb1d513c}
+
+
+Gets capability for handle.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]The backend name set in {\tt handle}. \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhGetConfig@{kdbhGetConfig}}
+\index{kdbhGetConfig@{kdbhGetConfig}!backendhandle@{backendhandle}}
+\subsubsection[kdbhGetConfig]{\setlength{\rightskip}{0pt plus 5cm}KeySet$\ast$ kdbhGetConfig (KDB $\ast$ {\em handle})}\label{group__backendhandle_gb14dc8708c2ae4ffcba6cfb130019115}
+
+
+Returns configuration for handle.
+
+Every backend may have its own configuration using a Keyset.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the keyset containing configuration for a backend \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhGetMountpoint@{kdbhGetMountpoint}}
+\index{kdbhGetMountpoint@{kdbhGetMountpoint}!backendhandle@{backendhandle}}
+\subsubsection[kdbhGetMountpoint]{\setlength{\rightskip}{0pt plus 5cm}const Key$\ast$ kdbhGetMountpoint (KDB $\ast$ {\em handle})}\label{group__backendhandle_g8b5612940fc9bc56e99c15ecc427cbb2}
+
+
+Gets mountpoint for handle.
+
+Every mounted backend has a specific mountpoint where it is mounted. You may need to know where you were mounted inside a backend to calculate relative pathes.
+
+The \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} is where the backend is mounted, keyString() gives the name of which backend is mounted.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbhSetMountpoint()}{p.}{group__backendhandle_g65c8878c24776c77716ac9e8abe1a29f} \end{Desc}
+\begin{Desc}
+\item[Returns:]The Key containing the mountpoint. \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhGetTrie@{kdbhGetTrie}}
+\index{kdbhGetTrie@{kdbhGetTrie}!backendhandle@{backendhandle}}
+\subsubsection[kdbhGetTrie]{\setlength{\rightskip}{0pt plus 5cm}Trie$\ast$ kdbhGetTrie (const KDB $\ast$ {\em handle})}\label{group__backendhandle_g591d15bf10d8f4366ec7e1e9b8c6ddc7}
+
+
+Gets trie for handle.
+
+The trie is a datastructure containing the mounted backends.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbhSetTrie()}{p.}{group__backendhandle_g1c5c086715963f34f2a88fac89b62f82} \end{Desc}
+\begin{Desc}
+\item[Returns:]The backend name set in {\tt handle}. \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhSetBackendData@{kdbhSetBackendData}}
+\index{kdbhSetBackendData@{kdbhSetBackendData}!backendhandle@{backendhandle}}
+\subsubsection[kdbhSetBackendData]{\setlength{\rightskip}{0pt plus 5cm}void$\ast$ kdbhSetBackendData (KDB $\ast$ {\em handle}, \/  void $\ast$ {\em data})}\label{group__backendhandle_g97fab712e488c7ec3e198492106724ab}
+
+
+Set some backend-specific {\tt data} in the {\tt handle}.
+
+This is useful when your backend have a backend-global context or environment.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em data}]a pointer to general data specific to a backend implementation. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbhGetBackendData()}{p.}{group__backendhandle_ge463be8651422015fd12811ed66d20f3} \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhSetCapability@{kdbhSetCapability}}
+\index{kdbhSetCapability@{kdbhSetCapability}!backendhandle@{backendhandle}}
+\subsubsection[kdbhSetCapability]{\setlength{\rightskip}{0pt plus 5cm}KDBCap$\ast$ kdbhSetCapability (KDB $\ast$ {\em handle}, \/  KDBCap $\ast$ {\em cap})}\label{group__backendhandle_gd077fae396d9be2b9d5e01b6f9f60318}
+
+
+Sets capabilty for handle.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em cap}]a pointer to capability structure \item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]The backend name set in {\tt handle}. \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhSetMountpoint@{kdbhSetMountpoint}}
+\index{kdbhSetMountpoint@{kdbhSetMountpoint}!backendhandle@{backendhandle}}
+\subsubsection[kdbhSetMountpoint]{\setlength{\rightskip}{0pt plus 5cm}void kdbhSetMountpoint (KDB $\ast$ {\em handle}, \/  const Key $\ast$ {\em mountpoint})}\label{group__backendhandle_g65c8878c24776c77716ac9e8abe1a29f}
+
+
+Sets mountpoint for handle.
+
+You must not change the mountpoint inside your backend, it was set correctly already for you.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em mountpoint}]the key containing as name where backend is mounted and as value the backendname \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbhGetMountpoint()}{p.}{group__backendhandle_g8b5612940fc9bc56e99c15ecc427cbb2} \end{Desc}
+\begin{Desc}
+\item[Returns:]nothing \end{Desc}
+\index{backendhandle@{backendhandle}!kdbhSetTrie@{kdbhSetTrie}}
+\index{kdbhSetTrie@{kdbhSetTrie}!backendhandle@{backendhandle}}
+\subsubsection[kdbhSetTrie]{\setlength{\rightskip}{0pt plus 5cm}void kdbhSetTrie (KDB $\ast$ {\em handle}, \/  Trie $\ast$ {\em trie})}\label{group__backendhandle_g1c5c086715963f34f2a88fac89b62f82}
+
+
+Sets trie for handle.
+
+The trie is a datastructure containing the mounted backends. This must not done inside backends, it was set correctly already for you.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em trie}]the datastructure referencing to the other handles of backends \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbhGetTrie()}{p.}{group__backendhandle_g591d15bf10d8f4366ec7e1e9b8c6ddc7} \end{Desc}
+\begin{Desc}
+\item[Returns:]nothing \end{Desc}
diff --git a/doc/elektra-api/latex/group__backendhelper.tex b/doc/elektra-api/latex/group__backendhelper.tex
new file mode 100644 (file)
index 0000000..a2ead8e
--- /dev/null
@@ -0,0 +1,325 @@
+\section{KDB Backends :: Backend Helper for Elektra}
+\label{group__backendhelper}\index{KDB Backends :: Backend Helper for Elektra@{KDB Backends :: Backend Helper for Elektra}}
+Backend helper Methods for Elektra and Backends.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf kdbbWriteLock} (FILE $\ast$f)
+\item 
+int {\bf kdbbReadLock} (FILE $\ast$f)
+\item 
+int {\bf kdbbUnlock} (FILE $\ast$f)
+\item 
+ssize\_\-t {\bf kdbbEncode} (void $\ast$kdbbDecoded, size\_\-t size, char $\ast$returned)
+\item 
+ssize\_\-t {\bf kdbbDecode} (char $\ast$kdbbEncoded, void $\ast$returned)
+\item 
+int {\bf kdbbNeedsUTF8Conversion} ()
+\item 
+int {\bf kdbbUTF8Engine} (int direction, char $\ast$$\ast$string, size\_\-t $\ast$inputOutputByteSize)
+\item 
+int {\bf kdbbEncodeChar} (char c, char $\ast$buffer, size\_\-t bufSize)
+\item 
+int {\bf kdbbDecodeChar} (const char $\ast$from, char $\ast$into)
+\item 
+int {\bf kdbbFilenameToKeyName} (const char $\ast$string, char $\ast$buffer, int bufSize)
+\item 
+ssize\_\-t {\bf kdbbGetFullKeyName} (KDB $\ast$handle, const char $\ast$forFilename, const Key $\ast$parentKey, Key $\ast$returned)
+\item 
+int {\bf kdbbKeyNameToRelativeFilename} (const char $\ast$string, char $\ast$buffer, size\_\-t bufSize)
+\item 
+ssize\_\-t {\bf kdbbKeyCalcRelativeFilename} (const Key $\ast$key, char $\ast$relativeFilename, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf kdbbGetFullFilename} (KDB $\ast$handle, const Key $\ast$forKey, char $\ast$returned, size\_\-t maxSize)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Backend helper Methods for Elektra and Backends. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim} #include <kdbbackend.h>
+\end{verbatim}
+\end{Code}
+
+
+
+These backend helper methods provide functionality commonly used by backends to make backend development easier and to provide the same behaviour between backends. 
+
+\subsection{Function Documentation}
+\index{backendhelper@{backendhelper}!kdbbDecode@{kdbbDecode}}
+\index{kdbbDecode@{kdbbDecode}!backendhelper@{backendhelper}}
+\subsubsection[kdbbDecode]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbbDecode (char $\ast$ {\em kdbbEncoded}, \/  void $\ast$ {\em returned})}\label{group__backendhelper_gfc099de90661f22a048d5fd16fca48f5}
+
+
+UnkdbbEncodes a buffer of ASCII hexadecimal values into a byte stream.
+
+The allowed format for the hexadecimal values is just a stream of pairs of plain hex-digits, all together or space-separated.
+
+The {\tt returned} data won't be bigger than half the size of the source {\tt kdbbEncoded} data.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em kdbbEncoded}]the source of ASCII hexadecimal digits. \item[{\em returned}]preallocated destination for the kdbbDecoded data. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the amount of bytes kdbbDecoded 
+
+-1 on failure \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbbEncode()}{p.}{group__backendhelper_gba58d73495f15a309468fd4798539f22} \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbDecodeChar@{kdbbDecodeChar}}
+\index{kdbbDecodeChar@{kdbbDecodeChar}!backendhelper@{backendhelper}}
+\subsubsection[kdbbDecodeChar]{\setlength{\rightskip}{0pt plus 5cm}int kdbbDecodeChar (const char $\ast$ {\em from}, \/  char $\ast$ {\em into})}\label{group__backendhelper_g0ae368114cb42f6f6d56bc5cc96e44cf}
+
+
+Char decoding.
+
+Decode one char from 25, 2B, 2F, 2C following RFC 2396 or copy char untouched if different.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em from}]String containing sequence to decode \item[{\em into}]Decoded char \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]: Positive size of byte read from \char`\"{}from\char`\"{} for decoding the sequence if sucess or -1 if error (into untouched)\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbbEncodeChar}{p.}{group__backendhelper_gac1bda629cb9912eaaaa785b2874cad1}\end{Desc}
+NOTE: No '$\backslash$0' is added at the end of buffer. \index{backendhelper@{backendhelper}!kdbbEncode@{kdbbEncode}}
+\index{kdbbEncode@{kdbbEncode}!backendhelper@{backendhelper}}
+\subsubsection[kdbbEncode]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbbEncode (void $\ast$ {\em kdbbDecoded}, \/  size\_\-t {\em size}, \/  char $\ast$ {\em returned})}\label{group__backendhelper_gba58d73495f15a309468fd4798539f22}
+
+
+Encodes a buffer of data onto hexadecimal ASCII.
+
+The resulting data is made up of pairs of ASCII hex-digits, space- and newline-separated. This is the counterpart of \doxyref{kdbbDecode()}{p.}{group__backendhelper_gfc099de90661f22a048d5fd16fca48f5}.
+
+The {\tt returned} must allocated prior you call this function and won't be bigger than 3 times the size of the source {\tt kdbbDecoded} + 1 byte.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em kdbbDecoded}]the source buffer. \item[{\em size}]the size of the source buffer in bytes. \item[{\em returned}]the preallocated destination for the ASCII-kdbbEncoded data. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the amount of bytes used in the resulting kdbbEncoded buffer. \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbbDecode()}{p.}{group__backendhelper_gfc099de90661f22a048d5fd16fca48f5} \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbEncodeChar@{kdbbEncodeChar}}
+\index{kdbbEncodeChar@{kdbbEncodeChar}!backendhelper@{backendhelper}}
+\subsubsection[kdbbEncodeChar]{\setlength{\rightskip}{0pt plus 5cm}int kdbbEncodeChar (char {\em c}, \/  char $\ast$ {\em buffer}, \/  size\_\-t {\em bufSize})}\label{group__backendhelper_gac1bda629cb9912eaaaa785b2874cad1}
+
+
+Char encoding.
+
+Encode '/', '$\backslash$', '', '+', ' ' char following RFC 2396 or copy char untouched if different.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em c}]Char to kdbbEncode \item[{\em buffer}]string wich will contain kdbbEncoded char \item[{\em bufSize}]Size of the buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]: Size of the kdbbEncoded string if success or -1 if error $\ast$ (then buffer is untouched)\end{Desc}
+\begin{Desc}
+\item[See also:]kdbiDecodeChar\end{Desc}
+NOTE: No '$\backslash$0' is added at the end of buffer. \index{backendhelper@{backendhelper}!kdbbFilenameToKeyName@{kdbbFilenameToKeyName}}
+\index{kdbbFilenameToKeyName@{kdbbFilenameToKeyName}!backendhelper@{backendhelper}}
+\subsubsection[kdbbFilenameToKeyName]{\setlength{\rightskip}{0pt plus 5cm}int kdbbFilenameToKeyName (const char $\ast$ {\em string}, \/  char $\ast$ {\em buffer}, \/  int {\em bufSize})}\label{group__backendhelper_ge3ac53e11feb9d7ff956a4562f1d9bde}
+
+
+Translate a relative file name to a key name applying decoding.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em string}]Filename \item[{\em buffer}]decoded keyName \item[{\em bufSize}]Size of buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbbKeyNameToRelativeFilename}{p.}{group__backendhelper_g93921f5f2169e9d6ba1603807ee5bc2d} \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbGetFullFilename@{kdbbGetFullFilename}}
+\index{kdbbGetFullFilename@{kdbbGetFullFilename}!backendhelper@{backendhelper}}
+\subsubsection[kdbbGetFullFilename]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbbGetFullFilename (KDB $\ast$ {\em handle}, \/  const Key $\ast$ {\em forKey}, \/  char $\ast$ {\em returned}, \/  size\_\-t {\em maxSize})}\label{group__backendhelper_g6db2e7738f905ea0ab52b5c9c3981af6}
+
+
+Calculate the real file name for a key.
+
+system/ keys will get the prefix KDB\_\-DB\_\-SYSTEM
+
+For the user/ keys the algorithm works as follow: 1.) When the override environment KDB\_\-HOME exists the configuration will be searched below KDB\_\-HOME/KDB\_\-DB\_\-USER 2.) When the owner of the key exists in the elektra user database steps a.) and b.) will be tested: a.) The specific value for configuration storage of the user below system/users/$<$owner$>$/kdb b.) The home variable in system/users/$<$owner$>$/home will be merged together with KDB\_\-DB\_\-USER 3.) When the environment HOME exists the configuration will be searched below HOME/KDB\_\-DB\_\-USER 4.) Otherwise the KDB\_\-DB\_\-HOME/$<$owner$>$/KDB\_\-DB\_\-USER will be used
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em forKey}]the key object to work with \item[{\em handle}]the kdb handle to work with \item[{\em returned}]the buffer to return the calculated filename \item[{\em maxSize}]maximum number of bytes that fit the buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]kdbCalcRelativeFilename() \end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to the buffer, or 0 on error
+
+length of returned string on success 
+
+-1 on failure \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbGetFullKeyName@{kdbbGetFullKeyName}}
+\index{kdbbGetFullKeyName@{kdbbGetFullKeyName}!backendhelper@{backendhelper}}
+\subsubsection[kdbbGetFullKeyName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbbGetFullKeyName (KDB $\ast$ {\em handle}, \/  const char $\ast$ {\em forFilename}, \/  const Key $\ast$ {\em parentKey}, \/  Key $\ast$ {\em returned})}\label{group__backendhelper_g10364b65d79e44f99f9464e50dffa900}
+
+
+Calculates the keyname out of a relative filename.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]The kdb handle to work with \item[{\em forFilename}]needs to be the a null terminated string containing the relative filename \item[{\em parentKey}]is the key above the key which will be returned \item[{\em returned}]The proper keyname and owner will be stored in returned. A valid key must be passed. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to the buffer, or 0 on error
+
+length of returned string on success 
+
+-1 on failure \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbbKeyNameToRelativeFilename()}{p.}{group__backendhelper_g93921f5f2169e9d6ba1603807ee5bc2d} \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbKeyCalcRelativeFilename@{kdbbKeyCalcRelativeFilename}}
+\index{kdbbKeyCalcRelativeFilename@{kdbbKeyCalcRelativeFilename}!backendhelper@{backendhelper}}
+\subsubsection[kdbbKeyCalcRelativeFilename]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbbKeyCalcRelativeFilename (const Key $\ast$ {\em key}, \/  char $\ast$ {\em relativeFilename}, \/  size\_\-t {\em maxSize})}\label{group__backendhelper_g1d09dab69a7d6cee9a1eb2c1c650051e}
+
+
+This is a helper to kdbGetFullFilename()
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]has the relevant name for the relative filename \item[{\em relativeFilename}]the buffer to return the calculated filename \item[{\em maxSize}]maximum number of bytes that fit the buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]kdbGetFullFilename() \end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to the buffer 
+
+-1 on failure \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbKeyNameToRelativeFilename@{kdbbKeyNameToRelativeFilename}}
+\index{kdbbKeyNameToRelativeFilename@{kdbbKeyNameToRelativeFilename}!backendhelper@{backendhelper}}
+\subsubsection[kdbbKeyNameToRelativeFilename]{\setlength{\rightskip}{0pt plus 5cm}int kdbbKeyNameToRelativeFilename (const char $\ast$ {\em string}, \/  char $\ast$ {\em buffer}, \/  size\_\-t {\em bufSize})}\label{group__backendhelper_g93921f5f2169e9d6ba1603807ee5bc2d}
+
+
+Translate a key name to a relative file name applying encoding.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em string}]Keyname \item[{\em buffer}]kdbbEncoded filename \item[{\em bufSize}]Size of buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]Number of byte written in buffer on success, 
+
+-1 on failure\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbbKeyNameToRelativeFilename}{p.}{group__backendhelper_g93921f5f2169e9d6ba1603807ee5bc2d} \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbNeedsUTF8Conversion@{kdbbNeedsUTF8Conversion}}
+\index{kdbbNeedsUTF8Conversion@{kdbbNeedsUTF8Conversion}!backendhelper@{backendhelper}}
+\subsubsection[kdbbNeedsUTF8Conversion]{\setlength{\rightskip}{0pt plus 5cm}int kdbbNeedsUTF8Conversion (void)}\label{group__backendhelper_g11b83ea1eac5adb730c3f51660cded9d}
+
+
+Checks if UTF-8 conversion is needed in current context. if nl\_\-langinfo() is not available, no conversion is ever needed. If iconv usage is disabled there is no need to check if we need to convert. Furthermore, some systems have nl\_\-langinfo(), but lacks ability to get CODESET through it. Look at the comments by the \doxyref{kdbbUTF8Engine()}{p.}{group__backendhelper_g5b9a2cb642f2a626037c4c730c790c65} function for more information.
+
+\begin{Desc}
+\item[Returns:]0 if not needed 
+
+anything else if needed \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbReadLock@{kdbbReadLock}}
+\index{kdbbReadLock@{kdbbReadLock}!backendhelper@{backendhelper}}
+\subsubsection[kdbbReadLock]{\setlength{\rightskip}{0pt plus 5cm}int kdbbReadLock (FILE $\ast$ {\em f})}\label{group__backendhelper_g89aa8c310a5720766639c305e643c069}
+
+
+Locks file for read mode.
+
+Other processes and threads are allowed to read the file too simultaneous.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em f}]is a valid filedescriptor \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure\end{Desc}
+\begin{Desc}
+\item[{\bf Error}]sets KDB\_\-ERR\_\-NOLOCK when locking failed \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbUnlock@{kdbbUnlock}}
+\index{kdbbUnlock@{kdbbUnlock}!backendhelper@{backendhelper}}
+\subsubsection[kdbbUnlock]{\setlength{\rightskip}{0pt plus 5cm}int kdbbUnlock (FILE $\ast$ {\em f})}\label{group__backendhelper_gb969d5d25762464c5f719f4f90757fe8}
+
+
+Unlocks file.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em f}]is a valid filedescriptor \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure\end{Desc}
+\begin{Desc}
+\item[{\bf Error}]sets KDB\_\-ERR\_\-NOLOCK when locking failed \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbUTF8Engine@{kdbbUTF8Engine}}
+\index{kdbbUTF8Engine@{kdbbUTF8Engine}!backendhelper@{backendhelper}}
+\subsubsection[kdbbUTF8Engine]{\setlength{\rightskip}{0pt plus 5cm}int kdbbUTF8Engine (int {\em direction}, \/  char $\ast$$\ast$ {\em string}, \/  size\_\-t $\ast$ {\em inputOutputByteSize})}\label{group__backendhelper_g5b9a2cb642f2a626037c4c730c790c65}
+
+
+Converts string to ({\tt direction} = {\tt UTF8\_\-TO}) and from ({\tt direction} = {\tt UTF8\_\-FROM}) UTF-8.
+
+Since Elektra provides portability for key names and string values between different codesets, you should use this helper in your backend to convert to and from universal UTF-8 strings, when storing key names, values and comments.
+
+Broken locales in applications can cause problems too. Make sure to load the environment locales in your application using 
+
+\begin{Code}\begin{verbatim}setlocale (LC_ALL, "");
+\end{verbatim}
+\end{Code}
+
+
+
+Otherwise kdbbUTF8Engine will quit with -1 leading that backends return with error when non-ascii characters appear. Binary values are not effected.
+
+If iconv() or nl\_\-langinfo() is not available on your system, or if iconv() usage is disabled (--disable-iconv on build time) simply return 0 immediately.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em direction}]must be {\tt UTF8\_\-TO} (convert from current non-UTF-8 to UTF-8) or {\tt UTF8\_\-FROM} (convert from UTF-8 to current non-UTF-8) \item[{\em string}]before the call: the string to be converted; after the call: reallocated to carry the converted string \item[{\em inputOutputByteSize}]before the call: the size of the string including leading NULL; after the call: the size of the converted string including leading NULL \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure \end{Desc}
+\index{backendhelper@{backendhelper}!kdbbWriteLock@{kdbbWriteLock}}
+\index{kdbbWriteLock@{kdbbWriteLock}!backendhelper@{backendhelper}}
+\subsubsection[kdbbWriteLock]{\setlength{\rightskip}{0pt plus 5cm}int kdbbWriteLock (FILE $\ast$ {\em f})}\label{group__backendhelper_gcea5819334a71744a54a6b290a8c3bdc}
+
+
+Locks file for exclusive write mode.
+
+This function will block until all reader and writer have left the file.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em f}]is a valid filedescriptor \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure\end{Desc}
+\begin{Desc}
+\item[{\bf Error}]sets KDB\_\-ERR\_\-NOLOCK when locking failed \end{Desc}
diff --git a/doc/elektra-api/latex/group__internal.tex b/doc/elektra-api/latex/group__internal.tex
new file mode 100644 (file)
index 0000000..a91d188
--- /dev/null
@@ -0,0 +1,132 @@
+\section{KDB Backends :: Internal Helper for Elektra}
+\label{group__internal}\index{KDB Backends :: Internal Helper for Elektra@{KDB Backends :: Internal Helper for Elektra}}
+Internal Methods for Elektra and Backends.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf kdbiStrCaseCmp} (const char $\ast$s1, const char $\ast$s2)
+\item 
+int {\bf kdbiRealloc} (void $\ast$$\ast$buffer, size\_\-t size)
+\item 
+void {\bf kdbiFree} (void $\ast$ptr)
+\item 
+char $\ast$ {\bf kdbiStrDup} (const char $\ast$s)
+\item 
+size\_\-t {\bf kdbiStrLen} (const char $\ast$s)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Internal Methods for Elektra and Backends. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim} #include <kdbbackend.h>
+\end{verbatim}
+\end{Code}
+
+
+
+There are some areas where libraries have to reimplement some basic functions to archive support for non-standard systems, for testing purposes or to provide a little more convenience. 
+
+\subsection{Function Documentation}
+\index{internal@{internal}!kdbiFree@{kdbiFree}}
+\index{kdbiFree@{kdbiFree}!internal@{internal}}
+\subsubsection[kdbiFree]{\setlength{\rightskip}{0pt plus 5cm}void kdbiFree (void $\ast$ {\em ptr})}\label{group__internal_g758aca746b4458fd067165c5c46676be}
+
+
+Free memory of elektra or its backends.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ptr}]the pointer to free\end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]kdbiMalloc \end{Desc}
+\index{internal@{internal}!kdbiRealloc@{kdbiRealloc}}
+\index{kdbiRealloc@{kdbiRealloc}!internal@{internal}}
+\subsubsection[kdbiRealloc]{\setlength{\rightskip}{0pt plus 5cm}int kdbiRealloc (void $\ast$$\ast$ {\em buffer}, \/  size\_\-t {\em size})}\label{group__internal_gba2b4284a78cad9cb3ca4cc7291caec5}
+
+
+Reallocate Storage in a save way.
+
+
+
+\begin{Code}\begin{verbatim}if (kdbiRealloc ((void **) & buffer, new_length) < 0) {
+        // here comes the failure handler
+        // you can still use the old buffer
+#if DEBUG
+        fprintf (stderr, "Reallocation error\n");
+#endif
+        free (buffer);
+        buffer = 0;
+        // return with error
+}
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em buffer}]is a pointer to a malloc \item[{\em size}]is the new size for the memory \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]-1 on failure 
+
+0 on success \end{Desc}
+\index{internal@{internal}!kdbiStrCaseCmp@{kdbiStrCaseCmp}}
+\index{kdbiStrCaseCmp@{kdbiStrCaseCmp}!internal@{internal}}
+\subsubsection[kdbiStrCaseCmp]{\setlength{\rightskip}{0pt plus 5cm}int kdbiStrCaseCmp (const char $\ast$ {\em s1}, \/  const char $\ast$ {\em s2})}\label{group__internal_g18471c9f740ea7988a0e757e752834e6}
+
+
+Compare Strings ignoring case.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em s1}]The first string to be compared \item[{\em s2}]The second string to be compared\end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a negative number if s1 is less than s2 
+
+0 if s1 matches s2 
+
+a positive number if s1 is greater than s2 \end{Desc}
+\index{internal@{internal}!kdbiStrDup@{kdbiStrDup}}
+\index{kdbiStrDup@{kdbiStrDup}!internal@{internal}}
+\subsubsection[kdbiStrDup]{\setlength{\rightskip}{0pt plus 5cm}char$\ast$ kdbiStrDup (const char $\ast$ {\em s})}\label{group__internal_gc7b7254ccb104b362d3e730da654ea87}
+
+
+Copy string into new allocated memory.
+
+You need to free the memory yourself.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em s}]the null-terminated string to duplicate\end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbiFree}{p.}{group__internal_g758aca746b4458fd067165c5c46676be} 
+
+\doxyref{kdbiStrLen}{p.}{group__internal_ga13b38e07d4e54738b951c3e0bef2faf} \end{Desc}
+\index{internal@{internal}!kdbiStrLen@{kdbiStrLen}}
+\index{kdbiStrLen@{kdbiStrLen}!internal@{internal}}
+\subsubsection[kdbiStrLen]{\setlength{\rightskip}{0pt plus 5cm}size\_\-t kdbiStrLen (const char $\ast$ {\em s})}\label{group__internal_ga13b38e07d4e54738b951c3e0bef2faf}
+
+
+Calculates the length in bytes of a string.
+
+This function differs from strlen() because it is Unicode and multibyte chars safe. While strlen() counts characters and ignores the final NULL, \doxyref{kdbiStrLen()}{p.}{group__internal_ga13b38e07d4e54738b951c3e0bef2faf} count bytes including the ending NULL.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em s}]the string to get the length from \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes used by the string, including the final NULL. \end{Desc}
diff --git a/doc/elektra-api/latex/group__kdb.tex b/doc/elektra-api/latex/group__kdb.tex
new file mode 100644 (file)
index 0000000..9b9d722
--- /dev/null
@@ -0,0 +1,375 @@
+\section{KDB :: Low Level Methods}
+\label{group__kdb}\index{KDB :: Low Level Methods@{KDB :: Low Level Methods}}
+General methods to access the Key database.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf kdbMount} (KDB $\ast$handle, const Key $\ast$mountpoint, const KeySet $\ast$config)
+\item 
+int {\bf kdbUnmount} (KDB $\ast$handle, const Key $\ast$mountpoint)
+\item 
+Key $\ast$ {\bf kdbGetMountpoint} (KDB $\ast$handle, const Key $\ast$where)
+\item 
+KDB $\ast$ {\bf kdbOpen} ()
+\item 
+int {\bf kdbClose} (KDB $\ast$handle)
+\item 
+ssize\_\-t {\bf kdbGet} (KDB $\ast$handle, KeySet $\ast$returned, Key $\ast$parentKey, option\_\-t options)
+\item 
+ssize\_\-t {\bf kdbSet} (KDB $\ast$handle, KeySet $\ast$ks, Key $\ast$parentKey, option\_\-t options)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+General methods to access the Key database. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim} #include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+
+The kdb$\ast$() class of methods are used to access the storage, to get and set \doxyref{Keys }{p.}{group__key} or \doxyref{KeySets }{p.}{group__keyset}.
+
+The most important functions are:\begin{itemize}
+\item \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f}\item \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859}\item \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}\item \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}\end{itemize}
+
+
+The two essential functions for dynamic information about backends are:\begin{itemize}
+\item \doxyref{kdbGetMountpoint()}{p.}{group__kdb_ga3717146f45e5a9665377c7f5b71e39b}\item kdbGetCapability()\end{itemize}
+
+
+They use some backend implementation to know the details about how to access the storage. Currently we have this backends:\begin{itemize}
+\item {\tt berkeleydb:} the keys are stored in a Berkeley DB database, providing very small footprint, speed, and other advantages.\item {\tt filesys:} the key hierarchy and data are saved as plain text files in the filesystem.\item {\tt ini:} the key hierarchy are saved into configuration files. \begin{Desc}
+\item[See also:]{\tt http://www.libelektra.org/Ini}\end{Desc}
+\item {\tt fstab:} a reference backend used to interpret the {\tt /etc/fstab} file as a set of keys under {\tt system/filesystems} .\item {\tt gconf:} makes Elektra use the GConf daemon to access keys. Only the {\tt user/} tree is available since GConf is not system wide.\end{itemize}
+
+
+Backends are physically a library named {\tt /lib/libelektra-\{NAME\}}.so.
+
+See \doxyref{writing a new backend }{p.}{group__backend} for information about how to write a backend.
+
+Language binding writers should follow the same rules:\begin{itemize}
+\item You must relay completely on the backend-dependent methods.\item You may use or reimplement the second set of methods.\item You should completely reimplement in your language the higher lever methods.\item Many methods are just for comfort in C. These methods are marked and need not to be implemented if the binding language has e.g. string operators which can do the operation easily. \end{itemize}
+
+
+\subsection{Function Documentation}
+\index{kdb@{kdb}!kdbClose@{kdbClose}}
+\index{kdbClose@{kdbClose}!kdb@{kdb}}
+\subsubsection[kdbClose]{\setlength{\rightskip}{0pt plus 5cm}int kdbClose (KDB $\ast$ {\em handle})}\label{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859}
+
+
+Closes the session with the Key database.
+
+You should call this method when you finished your affairs with the key database. You can manipulate Key and KeySet objects also after \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859}. You must not use any kdb$\ast$ call afterwards. You can implement \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859} in the atexit() handler.
+
+This is the counterpart of \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f}.
+
+The {\tt handle} parameter will be finalized and all resources associated to it will be freed. After a \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859}, this {\tt handle} can't be used anymore, unless it gets initialized again with another call to \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f}.
+
+\begin{Desc}
+\item[See also:]\doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL pointer \end{Desc}
+\index{kdb@{kdb}!kdbGet@{kdbGet}}
+\index{kdbGet@{kdbGet}!kdb@{kdb}}
+\subsubsection[kdbGet]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbGet (KDB $\ast$ {\em handle}, \/  KeySet $\ast$ {\em returned}, \/  Key $\ast$ {\em parentKey}, \/  option\_\-t {\em options})}\label{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}
+
+
+Retrieve keys in an atomic and universal way, all other kdbGet Functions rely on that one.
+
+The {\tt returned} KeySet must be initialized or may already contain some keys. The new retrieved keys will be appended using \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}.
+
+In default behaviour ({\tt options} = 0) it will fully retrieve all keys under the {\tt parentKey} folder, with all subfolders and their children but not inactive keys or folders.
+
+The keyset will not be sorted at first place, but will be marked dirty and sorted afterwards when needed. That could be a subsequent \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004}, \doxyref{ksLookupByName()}{p.}{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1} or \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}. See \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} on that issue.
+
+The behaviour can be fine-tuned with options in various ways to make \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} more comfortable.\subsection{Options}\label{group__kdb_kdbgetoption}
+The {\tt option} is an array of the following ORed flags:
+
+\begin{itemize}
+\item {\tt option\_\-t::KDB\_\-O\_\-DEL} \par
+ Its often useful to \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} the parentKey in the line after \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}. Using this flag, you can just pass a key allocated with \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will free it for you in the end.\item {\tt option\_\-t::KDB\_\-O\_\-POP} \par
+ The {\tt parentKey} itself will always be added to {\tt returned}. If you only want the children of the parentKey in {\tt returned}, but not the parentKey itself, use this flag. This is only valid for the first parentKey, the one you passed. The other recursive parentKeys will stay in the keyset. To get only the leaves of the tree, without any parentKey, see option\_\-t::KDB\_\-O\_\-NODIR below.\item {\tt option\_\-t::KDB\_\-O\_\-NODIR} \par
+ Don't include folders in the {\tt returned} KeySet, so only keys without subkeys. You can picture it best that you only get the leaves of the tree of keys.\item {\tt option\_\-t::KDB\_\-O\_\-DIRONLY} \par
+ Put in {\tt returned} only the folder keys. The resulting KeySet will be only the skeleton of the tree. This option must not be ORed together with KDB\_\-O\_\-DIR.\item {\tt option\_\-t::KDB\_\-O\_\-NOSTAT} \par
+ Don't stat they keys, whatever \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} says. That means that also the key value and comment will be retrieved. The flag will result in that all keys in {\tt returned} don't have \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} set.\item {\tt option\_\-t::KDB\_\-O\_\-STATONLY} \par
+ Only stat the keys. It means that key value and comment will not be retrieved. The resulting keys will contain only meta info such as user and group IDs, owner, mode permissions and modification times. You don't need that flag if the keys already have \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} set. The flag will result in that all keys in {\tt returned} have \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} set.\item {\tt option\_\-t::KDB\_\-O\_\-INACTIVE} \par
+ Will make it not ignore inactive keys, so {\tt returned} will contain also inactive keys. Inactive keys are those that have names begining with '.' (dot). Please be sure that you know what you are doing, inactive keys must not have any semantics to the application. This flag should only be set in key browsers after explicit user request. You might also get inactive keys when you plan to remove a whole hierarchy.\item {\tt option\_\-t::KDB\_\-O\_\-SORT} \par
+ Force {\tt returned} to be \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}ed. Normally you don't want that the {\tt returned} is sorted immediately because you might add other keys or go for another \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}. Sorting will take place automatically when needed by \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} or \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}, also without this option set. But you need to sort the keyset for yourself, when you just iterate over it. If you want to do that, pass this flag at the last \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}.\item {\tt option\_\-t::KDB\_\-O\_\-NORECURSIVE} \par
+ Dont get the keys recursive. Only receive keys from one folder. This might not work if the backend does not support it. Be prepared for more keys and use \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} and avoid static assumptions on how many keys you get.\end{itemize}
+
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}KDB *handle;
+KeySet *myConfig;
+Key *key;
+
+myConfig=ksNew(0);
+
+handle = kdbOpen();
+
+key=keyNew("system/sw/MyApp",KEY_END);
+rc=kdbGet(handle,key, myConfig, 0);
+keyDel(key);
+
+key=keyNew("user/sw/MyApp",KEY_END);
+rc=kdbGet(handle,key, myConfig, 0);
+keyDel(key);
+
+// will sort keyset here
+key=ksLookupByName(myConfig,"/sw/MyApp/key", 0);
+// check if key is not 0 and work with it...
+
+ksDel (myConfig); // delete the in-memory configuration
+
+
+// maybe you want kdbSet() myConfig here
+
+kdbClose(handle); // no more affairs with the key database.
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+\subsection{Details}\label{group__kdb_kdbgetdetail}
+When no backend could be found (e.g. no backend mounted) the default backend will be used.
+
+If you pass a NULL pointer as handle and/or returned \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will return -1 and do nothing but \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} the parentKey when requested and not a NULL pointer.
+
+If you pass NULL as parentKey the root keys of all namespaces will be appended to returned.
+
+For every directory key (\doxyref{keyIsDir()}{p.}{group__keytest_gc0a10c602d52a35f81347e8a32312017}) the appropriate backend will be chosen and keys in it will be requested.
+
+If any backend reports an failure the recursive getting of keys will be stopped. Backends only report failure when they are not able to get keys for any problems.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em parentKey}]parent key or NULL to get the root keys \item[{\em returned}]the (pre-initialized) KeySet returned with all keys found \item[{\em options}]ORed options to control approaches \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]option\_\-t 
+
+\doxyref{kdb higher level Methods }{p.}{group__kdbhighlevel} that rely on \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} 
+
+\doxyref{ksLookupByName()}{p.}{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1}, ksLookupByString() for powerful lookups after the KeySet was retrieved 
+
+commandList() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage example 
+
+commandEdit() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage example 
+
+commandExport() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage example \end{Desc}
+\begin{Desc}
+\item[Returns:]number of keys contained by {\tt returned} 
+
+-1 on failure \end{Desc}
+\index{kdb@{kdb}!kdbGetMountpoint@{kdbGetMountpoint}}
+\index{kdbGetMountpoint@{kdbGetMountpoint}!kdb@{kdb}}
+\subsubsection[kdbGetMountpoint]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ kdbGetMountpoint (KDB $\ast$ {\em handle}, \/  const Key $\ast$ {\em where})}\label{group__kdb_ga3717146f45e5a9665377c7f5b71e39b}
+
+
+Lookup a mountpoint in a handle for a specific key.
+
+Will return a key representing the mountpoint or null if there is no appropriate mountpoint e.g. its the root mountpoint.
+
+Together with kdbGetCapability() the two essential informations about mounted backends.
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}Key * key = keyNew ("system/template");
+KDB * handle = kdbOpen();
+Key *mountpoint=0;
+mountpoint=kdbGetMountpoint(handle, key);
+
+printf("The library I am using is %s mounted in %s\n",
+        keyValue(mountpoint),
+        keyName(mountpoint));
+kdbClose (handle);
+keyDel (key);
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]is the data structure, where the mounted directories are saved. \item[{\em where}]the key, that should be looked up. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the mountpoint associated with the key \end{Desc}
+\index{kdb@{kdb}!kdbMount@{kdbMount}}
+\index{kdbMount@{kdbMount}!kdb@{kdb}}
+\subsubsection[kdbMount]{\setlength{\rightskip}{0pt plus 5cm}int kdbMount (KDB $\ast$ {\em handle}, \/  const Key $\ast$ {\em mountpoint}, \/  const KeySet $\ast$ {\em config})}\label{group__kdb_g40e35f26cc69bd43ef1b2207f4fa121b}
+
+
+Dynamically mount a single backend.
+
+Maps the mountpoint, defined through its name and value, into the global elektra hierachy. If successfull, under the mountpoint another backend will reside.
+
+This only works for a single KDB, that means a single thread in a single process. You may want statically mounting by editing system/elektra/mountpoints.
+
+If you allocated mountpoint and config first, make sure that you free it! It is ok to free it immediately afterwards.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]handle to the kdb data structure \item[{\em mountpoint}]the \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} of this key is the mountpoint, \doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af} the backend \item[{\em config}]the configuration passed for that backend \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success, -1 if an error occurred \end{Desc}
+\index{kdb@{kdb}!kdbOpen@{kdbOpen}}
+\index{kdbOpen@{kdbOpen}!kdb@{kdb}}
+\subsubsection[kdbOpen]{\setlength{\rightskip}{0pt plus 5cm}KDB$\ast$ kdbOpen (void)}\label{group__kdb_gb7be60c387892d2235907836c5060e1f}
+
+
+Opens the session with the Key database.
+
+The first step is to open the default backend. With it system/elektra/mountpoints will be loaded and all needed libraries and mountpoints will be determined. These libraries for backends will be loaded and with it the {\tt KDB} datastructure will be initialized.
+
+You must always call this method before retrieving or commiting any keys to the database. In the end of the program, after using the key database, you must not forget to \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859}. You can use the atexit () handler for it.
+
+The pointer to the {\tt KDB} structure returned will be initialized like described above, and it must be passed along on any kdb$\ast$() method your application calls.
+
+Get a {\tt KDB} handle for every thread using elektra. Don't share the handle across threads, and also not the pointer accessing it: 
+
+\begin{Code}\begin{verbatim}thread1 {
+        KDB * h;
+        h = kdbOpen();
+        // fetch keys and work with them
+        kdbClose(h);
+}
+thread2 {
+        KDB * h;
+        h = kdbOpen();
+        // fetch keys and work with them
+        kdbClose(h);
+}
+\end{verbatim}
+\end{Code}
+
+
+
+You don't need to use the \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} if you only want to manipulate plain in-memory Key or KeySet objects without any affairs with the backend key database,
+
+\begin{Desc}
+\item[See also:]\doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859} to end all affairs to the \doxyref{Key :: Basic Methods}{p.}{group__key} database. \end{Desc}
+\begin{Desc}
+\item[Returns:]a KDB pointer on success 
+
+NULL on failure \end{Desc}
+\index{kdb@{kdb}!kdbSet@{kdbSet}}
+\index{kdbSet@{kdbSet}!kdb@{kdb}}
+\subsubsection[kdbSet]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbSet (KDB $\ast$ {\em handle}, \/  KeySet $\ast$ {\em ks}, \/  Key $\ast$ {\em parentKey}, \/  option\_\-t {\em options})}\label{group__kdb_g953cf29721e6000c2516cd6b5d36f571}
+
+
+Set keys in an atomic and universal way, all other kdbSet Functions rely on that one.
+
+The given handle and keyset are the objects to work with.
+
+With parentKey you can only store a part of the given keyset. Otherwise pass a null pointer or a parentKey without a name.
+
+
+
+\begin{Code}\begin{verbatim}KeySet *ks = ksNew(0);
+kdbGet (h, ks, keyNew("system/myapp",0), KDB_O_DEL);
+kdbGet (h, ks, keyNew("user/myapp",0), KDB_O_DEL);
+
+//now only set everything below user, because you can't write to system
+kdbSet (h, ks, keyNew("user/myapp",0), KDB_O_DEL);
+
+ksDel (ks);
+\end{verbatim}
+\end{Code}
+
+
+
+Each key is checked with \doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950} before being actually committed. So only changed keys are updated. If no key of a backend needs to be synced the \doxyref{kdbSet\_\-backend()}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23} will be omitted.
+
+If some error occurs, \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} will stop. In this situation the KeySet internal cursor will be set on the key that generated the error. This specific key and all behind it were not set. To be failsafe jump over it and try to set the rest, but report the error to the user.
+
+\begin{Desc}
+\item[Example of how this method can be used:]
+
+\begin{Code}\begin{verbatim}int i;
+KeySet *ks;  // the KeySet I want to set
+// fill ks with some keys
+for (i=0; i< 10; i++) // limit to 10 tries
+{
+        ret=kdbSet(handle,ks, 0, 0);
+        if (ret == -1)
+        {
+                // We got an error. Warn user.
+                Key *problem;
+                problem=ksCurrent(ks);
+                if (problem)
+                {
+                        char keyname[300]="";
+                        keyGetFullName(problem,keyname,sizeof(keyname));
+                        fprintf(stderr,"kdb import: while importing %s", keyname);
+                } else break;
+                // And try to set keys again starting from the next key,
+                // unless we reached the end of KeySet
+                if (ksNext(ks) == 0) break;
+        }
+}
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+\subsection{Options}\label{group__kdb_kdbsetoption}
+There are some options changing the behaviour of \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}:
+
+\begin{itemize}
+\item {\tt option\_\-t::KDB\_\-O\_\-DEL} \par
+ Its often useful to \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} the parentKey in the line after \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}. Using this flag, you can just pass a key allocated with \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will free it for you in the end.\item {\tt option\_\-t::KDB\_\-O\_\-SYNC} \par
+ Will force to save all keys, independent of their sync state.\item {\tt option\_\-t::KDB\_\-O\_\-NOREMOVE} \par
+ Don't remove any key from disk, even if \doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984} was set. With that flag removing keys can't happen unintentional. The flag will result in that all keys in {\tt returned} don't have \doxyref{keyNeedRemove()}{p.}{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9} set.\item {\tt option\_\-t::KDB\_\-O\_\-REMOVEONLY} \par
+ Remove all keys instead of setting them. All keys in {\tt returned} will have \doxyref{keyNeedRemove()}{p.}{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9} set, but not \doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5} saying to you that the key was deleted permanently. This option implicit also activates {\tt option\_\-t::KDB\_\-O\_\-SYNC} because the sync state will be changed when they are marked remove. You might need option\_\-t::KDB\_\-O\_\-INACTIVE set for the previous call of \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} if there are any. Otherwise the recursive remove will fail, because removing directories is only possible when all subkeys are removed.\end{itemize}
+\subsection{Details}\label{group__kdb_kdbsetdetail}
+When you dont have a parentKey or its name empty, then all keys will be set.
+
+You can remove some keys instead of setting them by marking them with \doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984}. The \doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950} flag will be unset after successful removing. But the \doxyref{keyNeedRemove()}{p.}{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9} flag will stay, but its safe to delete the key.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em ks}]a KeySet which should contain changed keys, otherwise nothing is done \item[{\em parentKey}]holds the information below which key keys should be set \item[{\em options}]see in \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} documentation \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950}, \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9}, \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} 
+
+\doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984}, \doxyref{keyNeedRemove()}{p.}{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9} 
+
+commandEdit(), commandImport() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage and error handling example \end{Desc}
+\index{kdb@{kdb}!kdbUnmount@{kdbUnmount}}
+\index{kdbUnmount@{kdbUnmount}!kdb@{kdb}}
+\subsubsection[kdbUnmount]{\setlength{\rightskip}{0pt plus 5cm}int kdbUnmount (KDB $\ast$ {\em handle}, \/  const Key $\ast$ {\em mountpoint})}\label{group__kdb_g400ca66a9bdc04ecadb66d84dc06bd55}
+
+
+Dynamically unmount a single backend.
+
+Unmount a backend that was mounted with \doxyref{kdbMount()}{p.}{group__kdb_g40e35f26cc69bd43ef1b2207f4fa121b} before.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]handle to the kdb data structure \item[{\em mountpoint}]directory where backend is mounted to, that should be unmounted \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success, -1 if an error ocurred. \end{Desc}
diff --git a/doc/elektra-api/latex/group__kdbhighlevel.tex b/doc/elektra-api/latex/group__kdbhighlevel.tex
new file mode 100644 (file)
index 0000000..9720df0
--- /dev/null
@@ -0,0 +1,241 @@
+\section{KDB :: High Level methods}
+\label{group__kdbhighlevel}\index{KDB :: High Level methods@{KDB :: High Level methods}}
+High level methods to access the Key database.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf kdbGetKey} (KDB $\ast$handle, Key $\ast$dest)
+\item 
+int {\bf kdbSetKey} (KDB $\ast$handle, const Key $\ast$key)
+\item 
+int {\bf kdbGetString} (KDB $\ast$handle, const char $\ast$keyname, char $\ast$returned, size\_\-t maxSize)
+\item 
+int {\bf kdbSetString} (KDB $\ast$handle, const char $\ast$keyname, const char $\ast$value)
+\item 
+int {\bf kdbRemove} (KDB $\ast$handle, const char $\ast$keyname)
+\item 
+ssize\_\-t {\bf kdbGetByName} (KDB $\ast$handle, KeySet $\ast$returned, const char $\ast$name, option\_\-t options)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+High level methods to access the Key database. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim} #include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+
+These methods are higher level. They use \doxyref{kdbOpen()}{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f}, \doxyref{kdbClose()}{p.}{group__kdb_gd9bb8bd3f1296bfa77cc9a1b41b7a859}, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} methods to do their job, and don't have to be reimplemented for a different backend.
+
+These functions avoid limitations through not implemented capabilities. This will of course cost some effort, so read through the description carefully and decide if it is appropriate for your problem.
+
+Binding writers don't have to implement these functions, use features of the binding language instead. But you can use these functions as ideas what high level methods may be useful.
+
+Don't use writing single keys in a loop, prefer always writing out a keyset! 
+
+\subsection{Function Documentation}
+\index{kdbhighlevel@{kdbhighlevel}!kdbGetByName@{kdbGetByName}}
+\index{kdbGetByName@{kdbGetByName}!kdbhighlevel@{kdbhighlevel}}
+\subsubsection[kdbGetByName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t kdbGetByName (KDB $\ast$ {\em handle}, \/  KeySet $\ast$ {\em returned}, \/  const char $\ast$ {\em name}, \/  option\_\-t {\em options})}\label{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2}
+
+
+This method is similar \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} but the path is given by a string.
+
+When it is not possible to make a key out of that string -1 is returned .
+
+When parentName starts with / cascading will be used and both keys from user and system will be fetched.
+
+A typically app with about 3000 keys may have this line:
+
+
+
+\begin{Code}\begin{verbatim}KDB *handle = kdbOpen();
+KeySet *myConfig = (4096, KS_END);
+ssize_t ret = kdbGetByName (handle, myConfig, "/sw/app/current", 0);
+
+// check ret and work with keyset myConfig
+
+ksDel (myConfig);
+kdbClose (handle);
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+myConfig will be loaded with keys from system/sw/app/current but also user/sw/app/current.
+
+When one of these \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} fails -1 will be returned, but the other \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will be tried too.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em name}]the name where to get the keys below \item[{\em returned}]the (pre-initialized) KeySet returned with all keys found \item[{\em options}]ORed options to control approaches Unlike to \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} is KDB\_\-O\_\-POP set per default. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of keys contained by {\tt returned} 
+
+-1 on failure 
+
+-1 when {\tt name} is no valid key 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} \end{Desc}
+\index{kdbhighlevel@{kdbhighlevel}!kdbGetKey@{kdbGetKey}}
+\index{kdbGetKey@{kdbGetKey}!kdbhighlevel@{kdbhighlevel}}
+\subsubsection[kdbGetKey]{\setlength{\rightskip}{0pt plus 5cm}int kdbGetKey (KDB $\ast$ {\em handle}, \/  Key $\ast$ {\em dest})}\label{group__kdbhighlevel_ga62877888f0cad395898859395e6635f}
+
+
+Fully retrieves the passed {\tt key} from the backend storage.
+
+The backend will try to get the key, identified through its name.
+
+It uses \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} for retrieving the key and copies the found data to dest.
+
+While \doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f} is perfect for a simple get of a specific key, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbGetByName()}{p.}{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2} gives you more control over the keyset.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em dest}]a pointer to a Key that has a name set \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20} to set a single \doxyref{Key :: Basic Methods}{p.}{group__key} 
+
+commandGet() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage example 
+
+\doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbGetByName()}{p.}{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2} to have more control over \doxyref{KeySet :: Class Methods}{p.}{group__keyset} and options \end{Desc}
+\index{kdbhighlevel@{kdbhighlevel}!kdbGetString@{kdbGetString}}
+\index{kdbGetString@{kdbGetString}!kdbhighlevel@{kdbhighlevel}}
+\subsubsection[kdbGetString]{\setlength{\rightskip}{0pt plus 5cm}int kdbGetString (KDB $\ast$ {\em handle}, \/  const char $\ast$ {\em keyname}, \/  char $\ast$ {\em returned}, \/  size\_\-t {\em maxSize})}\label{group__kdbhighlevel_g1e1b1a2beace8c9ce93d16259564b51f}
+
+
+A high-level method to get a key value, by key name.
+
+This method gets a backend from any backend with \doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f} and extracts the string and store it into returned. It only works with string keys.
+
+This method gives you the direct relation between a keyname and the value, without any kdb specific structures. Use it when you just want some values out of the kdb namespace.
+
+You need to know the maximum string length of the object. That could be the case when you e.g. save a path which is limited with MAX\_\-PATH.
+
+
+
+\begin{Code}\begin{verbatim}KDB *handle = kdbOpen();
+char buffer [MAX_PATH];
+
+if (kdbGetString(handle, "user/key/to/get/pathname", buffer, sizeof(buffer)) == -1)
+{
+        // handle error cases
+} else {
+        printf ("The keys value is %s\n", buffer);
+}
+kdbClose(handle);
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em keyname}]the name of the key to receive the value \item[{\em returned}]a buffer to put the key value \item[{\em maxSize}]the size of the buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure 
+
+-1 on NULL pointers 
+
+-1 if maxSize is 0 or larger than SSIZE\_\-MAX \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbSetString()}{p.}{group__kdbhighlevel_g8e11b3403c9616c7fb3b9b37d1cb849e} and \doxyref{kdbRemove()}{p.}{group__kdbhighlevel_gf9adbbeb3f49c63fb2f89930445c8060} to set and remove a string 
+
+\doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f}, keySetKey() to work with Keys 
+
+\doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbGetByName()}{p.}{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2} for full access to \doxyref{KDB Backends :: Internal Helper for Elektra}{p.}{group__internal} datastructures \end{Desc}
+\index{kdbhighlevel@{kdbhighlevel}!kdbRemove@{kdbRemove}}
+\index{kdbRemove@{kdbRemove}!kdbhighlevel@{kdbhighlevel}}
+\subsubsection[kdbRemove]{\setlength{\rightskip}{0pt plus 5cm}int kdbRemove (KDB $\ast$ {\em handle}, \/  const char $\ast$ {\em keyname})}\label{group__kdbhighlevel_gf9adbbeb3f49c63fb2f89930445c8060}
+
+
+Remove a key by its name from the backend storage.
+
+With \doxyref{kdbSetString()}{p.}{group__kdbhighlevel_g8e11b3403c9616c7fb3b9b37d1cb849e} its only possible to set a key with an empty string. To really remove a key in a highlevel way you can use this method.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em keyname}]the name of the key to be removed \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure 
+
+-1 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[See also:]together with \doxyref{kdbSetString()}{p.}{group__kdbhighlevel_g8e11b3403c9616c7fb3b9b37d1cb849e} and \doxyref{kdbGetString()}{p.}{group__kdbhighlevel_g1e1b1a2beace8c9ce93d16259564b51f} a highlevel interface for \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} 
+
+commandRemove() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage example \end{Desc}
+\index{kdbhighlevel@{kdbhighlevel}!kdbSetKey@{kdbSetKey}}
+\index{kdbSetKey@{kdbSetKey}!kdbhighlevel@{kdbhighlevel}}
+\subsubsection[kdbSetKey]{\setlength{\rightskip}{0pt plus 5cm}int kdbSetKey (KDB $\ast$ {\em handle}, \/  const Key $\ast$ {\em key})}\label{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20}
+
+
+Sets {\tt key} in the backend storage.
+
+While \doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20} is perfect for a simple get of a specific key, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbGetByName()}{p.}{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2} gives you more control over the keyset.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em key}]Key to set \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on failure 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f} to get a single \doxyref{Key :: Basic Methods}{p.}{group__key} 
+
+\doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} for more control over \doxyref{KeySet :: Class Methods}{p.}{group__keyset} and options 
+
+commandSet() code in \doxyref{KDB :: Low Level Methods}{p.}{group__kdb} command for usage example \end{Desc}
+\index{kdbhighlevel@{kdbhighlevel}!kdbSetString@{kdbSetString}}
+\index{kdbSetString@{kdbSetString}!kdbhighlevel@{kdbhighlevel}}
+\subsubsection[kdbSetString]{\setlength{\rightskip}{0pt plus 5cm}int kdbSetString (KDB $\ast$ {\em handle}, \/  const char $\ast$ {\em keyname}, \/  const char $\ast$ {\em value})}\label{group__kdbhighlevel_g8e11b3403c9616c7fb3b9b37d1cb849e}
+
+
+A high-level method to set a value to a key, by key name.
+
+It will check if key exists first, and keep its metadata. So you'll not loose the previous key comment.
+
+This will set a text key. So if the key was previously a binary it will be retyped as string.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em handle}]contains internal information of \doxyref{opened }{p.}{group__kdb_gb7be60c387892d2235907836c5060e1f} key database \item[{\em keyname}]the name of the key to receive the value \item[{\em value}]the value to be set \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL pointers 
+
+-1 on failure \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbGetString()}{p.}{group__kdbhighlevel_g1e1b1a2beace8c9ce93d16259564b51f}, \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2}, \doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20} \end{Desc}
diff --git a/doc/elektra-api/latex/group__key.tex b/doc/elektra-api/latex/group__key.tex
new file mode 100644 (file)
index 0000000..a847ee7
--- /dev/null
@@ -0,0 +1,448 @@
+\section{Key :: Basic Methods}
+\label{group__key}\index{Key :: Basic Methods@{Key :: Basic Methods}}
+Key construction and initialization methods.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+Key $\ast$ {\bf keyNew} (const char $\ast$keyName,...)
+\item 
+Key $\ast$ {\bf keyDup} (const Key $\ast$source)
+\item 
+int {\bf keyCopy} (Key $\ast$dest, const Key $\ast$source)
+\item 
+int {\bf keyDel} (Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyIncRef} (Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyDecRef} (Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetRef} (const Key $\ast$key)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Key construction and initialization methods. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim}#include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+
+A Key is the essential class that encapsulates key \doxyref{name }{p.}{group__keyname}, \doxyref{value }{p.}{group__keyvalue} and \doxyref{metainfo }{p.}{group__keymeta}. Key properties are:\begin{itemize}
+\item \doxyref{Key name }{p.}{group__keyname}\item \doxyref{Key value }{p.}{group__keyvalue}\item \doxyref{Data type }{p.}{group__keymeta_gb92003db4b938594df48807c16766bf7}\item \doxyref{Key comment }{p.}{group__keyvalue_gfb89735689929ff717cc9f2d0d0b46a2}\item \doxyref{Key owner }{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a}\item \doxyref{UID, GID and filesystem-like mode permissions }{p.}{group__keymeta}\item \doxyref{Mode, change and modification times }{p.}{group__keymeta}\end{itemize}
+
+
+Described here the methods to allocate and free the key. 
+
+\subsection{Function Documentation}
+\index{key@{key}!keyCopy@{keyCopy}}
+\index{keyCopy@{keyCopy}!key@{key}}
+\subsubsection[keyCopy]{\setlength{\rightskip}{0pt plus 5cm}int keyCopy (Key $\ast$ {\em dest}, \/  const Key $\ast$ {\em source})}\label{group__key_g6a12cbbe656a1ad9f41b8c681d7a2f92}
+
+
+Copy or Clear a key.
+
+Most often you may prefer \doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3} which allocates a new key and returns a duplication of another key.
+
+But when you need to copy into an existing key, e.g. because it was passed by a pointer in a function you can do so:
+
+
+
+\begin{Code}\begin{verbatim}int h (Key *k)
+{
+        // receive key c
+        keyCopy (k, c);
+        // the caller will see the changed key k
+}
+\end{verbatim}
+\end{Code}
+
+
+
+The reference counter will not change for the destination key. Affiliation to keysets are also not affected.
+
+When you pass a NULL-pointer as source the data of dest will be cleaned completely and you get a fresh dest key.
+
+
+
+\begin{Code}\begin{verbatim}int g (Key *k)
+{
+        keyCopy (k, 0);
+        // k is now an empty and fresh key
+}
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em dest}]the key which will be written to \item[{\em source}]the key which should be copied or NULL to clean the destination key\end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]-1 on failure when a NULL pointer was passed for dest or a dynamic property could not be written. 
+
+0 when dest was cleaned 
+
+1 when source was successfully copied \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3} to get a duplication of a \doxyref{Key :: Basic Methods}{p.}{group__key} \end{Desc}
+\index{key@{key}!keyDecRef@{keyDecRef}}
+\index{keyDecRef@{keyDecRef}!key@{key}}
+\subsubsection[keyDecRef]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyDecRef (Key $\ast$ {\em key})}\label{group__key_g2c6433ca22109e4e141946057eccb283}
+
+
+Decrement the viability of a key object.
+
+The reference counter can't be decremented once it reached 0. In that situation nothing will happen and 0 will be returned.
+
+\begin{Desc}
+\item[Returns:]the value of the new reference counter 
+
+-1 on null pointer 
+
+0 when the key is ready to be freed \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetRef()}{p.}{group__key_g4aabc4272506dd63161db2bbb42de8ae}, \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1}, \doxyref{keyIncRef()}{p.}{group__key_g6970a6f254d67af7e39f8e469bb162f1} \end{Desc}
+\index{key@{key}!keyDel@{keyDel}}
+\index{keyDel@{keyDel}!key@{key}}
+\subsubsection[keyDel]{\setlength{\rightskip}{0pt plus 5cm}int keyDel (Key $\ast$ {\em key})}\label{group__key_g3df95bbc2494e3e6703ece5639be5bb1}
+
+
+A destructor for Key objects.
+
+Every key created by \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} must be deleted with \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1}.
+
+It is save to delete keys which are in a keyset, the number of references will be returned then.
+
+It is save to delete a nullpointer, -1 will be returned then.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to delete \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}, keyInc(), \doxyref{keyGetRef()}{p.}{group__key_g4aabc4272506dd63161db2bbb42de8ae} \end{Desc}
+\begin{Desc}
+\item[Returns:]the value of the reference counter if the key is within keyset(s) 
+
+0 when the key was freed 
+
+-1 on null pointers \end{Desc}
+\index{key@{key}!keyDup@{keyDup}}
+\index{keyDup@{keyDup}!key@{key}}
+\subsubsection[keyDup]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ keyDup (const Key $\ast$ {\em source})}\label{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3}
+
+
+Return a duplicate of a key.
+
+Memory will be allocated as needed for dynamic properties.
+
+The new key will not be member of any KeySet and will start with a new reference counter at 0. A subsequent \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} will delete the key.
+
+
+
+\begin{Code}\begin{verbatim}int f (const Key * source)
+{
+        Key * dup = keyDup (source);
+        // work with duplicate
+        keyDel (dup);
+        // everything related to dup is freed
+        // and source is unchanged
+}
+\end{verbatim}
+\end{Code}
+
+
+
+Like for a new key after \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} a subsequent \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7} makes a KeySet to take care of the lifecycle of the key.
+
+
+
+\begin{Code}\begin{verbatim}int g (const Key * source, KeySet * ks)
+{
+        Key * dup = keyDup (source);
+        // work with duplicate
+        ksAppendKey (ks, dup);
+        // ksDel(ks) will also free the duplicate
+        // source remains unchanged.
+}
+\end{verbatim}
+\end{Code}
+
+
+
+Duplication of keys should be preferred to \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}, because data like owner can be filled with a copy of the key instead of asking the environment. It can also be optimized in the checks, because the keyname is known to be valid.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em source}]has to be an initializised source Key \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 failure or on NULL pointer 
+
+a fully copy of source on success \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7}, \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} 
+
+keyClear(), \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} \end{Desc}
+\index{key@{key}!keyGetRef@{keyGetRef}}
+\index{keyGetRef@{keyGetRef}!key@{key}}
+\subsubsection[keyGetRef]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetRef (const Key $\ast$ {\em key})}\label{group__key_g4aabc4272506dd63161db2bbb42de8ae}
+
+
+Return how many references the key has.
+
+The references will be incremented when \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45} or \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7} uses the key and will be decremented when \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69} is used.
+
+\doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3} will reset the references for dupped key.
+
+For your own applications you can use \doxyref{keyIncRef()}{p.}{group__key_g6970a6f254d67af7e39f8e469bb162f1} and keyDelRef() for reference counting. Keys with zero references will be deleted when using \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1}.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of references 
+
+-1 on null pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyIncRef()}{p.}{group__key_g6970a6f254d67af7e39f8e469bb162f1} and \doxyref{keyDecRef()}{p.}{group__key_g2c6433ca22109e4e141946057eccb283} \end{Desc}
+\index{key@{key}!keyIncRef@{keyIncRef}}
+\index{keyIncRef@{keyIncRef}!key@{key}}
+\subsubsection[keyIncRef]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyIncRef (Key $\ast$ {\em key})}\label{group__key_g6970a6f254d67af7e39f8e469bb162f1}
+
+
+Increment the viability of a key object.
+
+This function is intended for applications using their own reference counter for key objects. With it you can increment the reference and thus avoid destruction of the object in a subsequent \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1}.
+
+
+
+\begin{Code}\begin{verbatim}Key *k;
+keyInc (k);
+function_that_keyDec(k);
+// work with k
+keyDel (k); // now really free it
+\end{verbatim}
+\end{Code}
+
+
+
+The reference counter can't be incremented once it reached SSIZE\_\-MAX. In that situation nothing will happen and SSIZE\_\-MAX will be returned.
+
+\begin{Desc}
+\item[Returns:]the value of the new reference counter 
+
+-1 on null pointer 
+
+SSIZE\_\-MAX when maximum exceeded \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetRef()}{p.}{group__key_g4aabc4272506dd63161db2bbb42de8ae}, \doxyref{keyDecRef()}{p.}{group__key_g2c6433ca22109e4e141946057eccb283}, \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} \end{Desc}
+\index{key@{key}!keyNew@{keyNew}}
+\index{keyNew@{keyNew}!key@{key}}
+\subsubsection[keyNew]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ keyNew (const char $\ast$ {\em keyName}, \/   {\em ...})}\label{group__key_gf6893c038b3ebee90c73a9ea8356bebf}
+
+
+A practical way to fully create a Key object in one step.
+
+This function tries to mimic the C++ way for constructors.
+
+To just get a key object, simple do: 
+
+\begin{Code}\begin{verbatim}Key *k = keyNew(0);
+// work with it
+keyDel (k);
+\end{verbatim}
+\end{Code}
+
+
+
+If you want the key object to contain a name, value, comment and other meta info read on.
+
+\begin{Desc}
+\item[Note:]When you already have a key with similar properties its easier and cheaper to \doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3} the key.\end{Desc}
+Due to ABI compatibility, the {\tt Key} structure is not defined in kdb.h, only declared. So you can only declare {\tt pointers} to {\tt Keys} in your program, and allocate and free memory for them with \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} and \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} respectively. See {\tt http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html\#AEN135}
+
+You can call it in many different ways depending on the attribute tags you pass as parameters. Tags are represented as the keyswitch\_\-t values, and tell \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} which Key attribute comes next.
+
+The simplest and minimum way to use it is with no tags, only a key name: 
+
+\begin{Code}\begin{verbatim}Key *nullKey,*emptyNamedKey;
+
+// Create a key that has no name, is completely empty, but is initialized
+nullKey=keyNew(0);
+keyDel (nullKey);
+
+// Is the same as above
+nullKey=keyNew("", KEY_END);
+keyDel (nullKey);
+
+// Create and initialize a key with a name and nothing else
+emptyNamedKey=keyNew("user/some/example",KEY_END);
+keyDel (emptyNamedKey);
+\end{verbatim}
+\end{Code}
+
+
+
+\doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} allocates memory for a key object and cleans everything up. After that, it processes the given argument list.
+
+The Key attribute tags are the following:\begin{itemize}
+\item keyswitch\_\-t::KEY\_\-TYPE \par
+ Next parameter is a type of the value. Default assumed is KEY\_\-TYPE\_\-UNDEFINED. Set this attribute so that a subsequent KEY\_\-VALUE can toggle to \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} or \doxyref{keySetBinary()}{p.}{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7} regarding to \doxyref{keyIsString()}{p.}{group__keytest_gea7670778abd07fee0fe8ac12a149190} or \doxyref{keyIsBinary()}{p.}{group__keytest_g9526b371087564e43e3dff8ad0dac949}. If you don't use KEY\_\-TYPE but a KEY\_\-VALUE follows afterwards, KEY\_\-TYPE\_\-STRING will be used.\item keyswitch\_\-t::KEY\_\-SIZE \par
+ Define a maximum length of the value. This is especially useful for setting a binary key. So make sure you use that before you KEY\_\-VALUE for binary keys.\item keyswitch\_\-t::KEY\_\-VALUE \par
+ Next parameter is a pointer to the value that will be set to the key If no keyswitch\_\-t::KEY\_\-TYPE was used before, keyswitch\_\-t::KEY\_\-TYPE\_\-STRING is assumed. If KEY\_\-TYPE was previously passed with a KEY\_\-TYPE\_\-BINARY, you should have passed KEY\_\-SIZE before! Otherwise it will be cut of with first $\backslash$0 in string!\item keyswitch\_\-t::KEY\_\-UID, {\tt keyswitch\_\-t::KEY\_\-GID} \par
+ Next parameter is taken as the UID (uid\_\-t) or GID (gid\_\-t) that will be defined on the key. See \doxyref{keySetUID()}{p.}{group__keymeta_gb5f284f5ecd261e0a290095f50ba1af7} and \doxyref{keySetGID()}{p.}{group__keymeta_g9e3d0fb3f7ba906e067727b9155d22e3}.\item keyswitch\_\-t::KEY\_\-MODE \par
+ Next parameter is taken as mode permissions (mode\_\-t) to the key. See \doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3}.\item keyswitch\_\-t::KEY\_\-DIR \par
+ Define that the key is a directory rather than a ordinary key. This means its executable bits in its mode are set. This option allows the key to have subkeys. See \doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75}.\item keyswitch\_\-t::KEY\_\-OWNER \par
+ Next parameter is the owner. See \doxyref{keySetOwner()}{p.}{group__keyname_ga899d9f0251cb98a89761ef112910eca}.\item keyswitch\_\-t::KEY\_\-COMMENT \par
+ Next parameter is a comment. See \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1}.\item keyswitch\_\-t::KEY\_\-REMOVE \par
+ Mark the key to be removed instead of set it. See \doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984}.\item keyswitch\_\-t::KEY\_\-STAT \par
+ Mark the key to be stated instead of get it. See \doxyref{keyStat()}{p.}{group__keymeta_gb8189add5e562bdb148675ee595bd95b}.\item keyswitch\_\-t::KEY\_\-END \par
+ Must be the last parameter passed to \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}. It is always required, unless the {\tt keyName} is 0.\end{itemize}
+
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}KeySet *ks=ksNew(0);
+
+ksAppendKey(ks,keyNew(0));       // an empty key
+
+ksAppendKey(ks,keyNew("user/sw",              // the name of the key
+        KEY_END));                      // no more args
+
+ksAppendKey(ks,keyNew("user/tmp/ex1",
+        KEY_VALUE,"some data",          // set a string value
+        KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew("user/tmp/ex2",
+        KEY_VALUE,"some data",          // with a simple value
+        KEY_MODE,0777,                  // permissions
+        KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew("user/tmp/ex4",
+        KEY_TYPE,KEY_TYPE_BINARY,       // key type
+        KEY_SIZE,7,                     // assume binary length 7
+        KEY_VALUE,"some data",          // value that will be truncated in 7 bytes
+        KEY_COMMENT,"value is truncated",
+        KEY_OWNER,"root",               // owner (not uid) is root
+        KEY_UID,0,                      // root uid
+        KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew("user/tmp/ex5",
+        KEY_TYPE,
+                KEY_TYPE_DIR | KEY_TYPE_BINARY,// dir key with a binary value
+        KEY_SIZE,7,
+        KEY_VALUE,"some data",          // value that will be truncated in 7 bytes
+        KEY_COMMENT,"value is truncated",
+        KEY_OWNER,"root",               // owner (not uid) is root
+        KEY_UID,0,                      // root uid
+        KEY_END));                      // end of args
+
+ksDel(ks);
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+The reference counter (see \doxyref{keyGetRef()}{p.}{group__key_g4aabc4272506dd63161db2bbb42de8ae}) will be initialized with 0, that means a subsequent call of \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} will delete the key. If you append the key to a keyset the reference counter will be incremented by one (see keyInc()) and the key can't be be deleted by a \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1}.
+
+
+
+\begin{Code}\begin{verbatim}Key *k = keyNew(0); // ref counter 0
+ksAppendKey(ks, k); // ref counter of key 1
+ksDel(ks); // key will be deleted with keyset
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+If you increment only by one with keyInc() the same as said above is valid:
+
+
+
+\begin{Code}\begin{verbatim}Key *k = keyNew(0); // ref counter 0
+keyIncRef(k); // ref counter of key 1
+keyDel(k);    // has no effect
+keyDecRef(k); // ref counter back to 0
+keyDel(k);    // key is now deleted
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+If you add the key to more keySets:
+
+
+
+\begin{Code}\begin{verbatim}Key *k = keyNew(0); // ref counter 0
+ksAppendKey(ks1, k); // ref counter of key 1
+ksAppendKey(ks2, k); // ref counter of key 2
+ksDel(ks1); // ref counter of key 1
+ksDel(ks2); // k is now deleted
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+or use keyInc() more than once:
+
+
+
+\begin{Code}\begin{verbatim}Key *k = keyNew(0); // ref counter 0
+keyIncRef(k); // ref counter of key 1
+keyDel (k);   // has no effect
+keyIncRef(k); // ref counter of key 2
+keyDel (k);   // has no effect
+keyDecRef(k); // ref counter of key 1
+keyDel (k);   // has no effect
+keyDecRef(k); // ref counter is now 0
+keyDel (k); // k is now deleted
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+they key won't be deleted by a \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} as long refcounter is not 0.
+
+The key's sync bit will always be set for any call, except: 
+
+\begin{Code}\begin{verbatim}Key *k = keyNew(0);
+// keyNeedSync() will be false
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em keyName}]a valid name to the key, or NULL to get a simple initialized, but really empty, object \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} \end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to a new allocated and initialized Key object, or NULL if an invalid {\tt keyName} was passed (see \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}). \end{Desc}
diff --git a/doc/elektra-api/latex/group__keymeta.tex b/doc/elektra-api/latex/group__keymeta.tex
new file mode 100644 (file)
index 0000000..be990ab
--- /dev/null
@@ -0,0 +1,521 @@
+\section{Key :: Meta Info Manipulation Methods}
+\label{group__keymeta}\index{Key :: Meta Info Manipulation Methods@{Key :: Meta Info Manipulation Methods}}
+Methods to do various operations on Key metainfo.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf keyStat} (Key $\ast$key)
+\item 
+int {\bf keyRemove} (Key $\ast$key)
+\item 
+uid\_\-t {\bf keyGetUID} (const Key $\ast$key)
+\item 
+int {\bf keySetUID} (Key $\ast$key, uid\_\-t uid)
+\item 
+gid\_\-t {\bf keyGetGID} (const Key $\ast$key)
+\item 
+int {\bf keySetGID} (Key $\ast$key, gid\_\-t gid)
+\item 
+int {\bf keySetDir} (Key $\ast$key)
+\item 
+mode\_\-t {\bf keyGetMode} (const Key $\ast$key)
+\item 
+int {\bf keySetMode} (Key $\ast$key, mode\_\-t mode)
+\item 
+type\_\-t {\bf keyGetType} (const Key $\ast$key)
+\item 
+int {\bf keySetType} (Key $\ast$key, type\_\-t newType)
+\item 
+time\_\-t {\bf keyGetATime} (const Key $\ast$key)
+\item 
+int {\bf keySetATime} (Key $\ast$key, time\_\-t atime)
+\item 
+time\_\-t {\bf keyGetMTime} (const Key $\ast$key)
+\item 
+int {\bf keySetMTime} (Key $\ast$key, time\_\-t mtime)
+\item 
+time\_\-t {\bf keyGetCTime} (const Key $\ast$key)
+\item 
+int {\bf keySetCTime} (Key $\ast$key, time\_\-t ctime)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to do various operations on Key metainfo. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim}#include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+
+Next to \doxyref{Name (key and owner) }{p.}{group__keyname} and \doxyref{Value (data and comment) }{p.}{group__keyvalue} there is the so called metainfo inside every key.
+
+Key metainfo insists of:\begin{itemize}
+\item UID, the user id\item GID, the group id\item filesystem-like mode permissions (rwx)\item Mode, change and modification times\end{itemize}
+
+
+The comment can contain userdata which directly belong to that key.
+
+Owner is the user that owns the key. It only works for the user/ hierachy.
+
+Every user and group of your System has a uniqe ID. These values are used in the keys too. They are very important for the mode. See man 2 chown.
+
+With the mode mode you can choose if a user, group or the world can mode your key. See man 2 chmod. 
+
+\subsection{Function Documentation}
+\index{keymeta@{keymeta}!keyGetATime@{keyGetATime}}
+\index{keyGetATime@{keyGetATime}!keymeta@{keymeta}}
+\subsubsection[keyGetATime]{\setlength{\rightskip}{0pt plus 5cm}time\_\-t keyGetATime (const Key $\ast$ {\em key})}\label{group__keymeta_g6b05da399c3c78904969ef39f191b0eb}
+
+
+Get last time the key data was read from disk.
+
+Every \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} might update the access time of a key. You get information when the key was read the last time from the database.
+
+You will get 0 when the key was not read already.
+
+Beware that multiple copies of keys with \doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3} might have different atimes because you \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} one, but not the other. You can use this information to decide which key is the latest.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]Key to get information from. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the time you got the key with \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} 
+
+0 on key that was never \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} 
+
+(time\_\-t)-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetATime()}{p.}{group__keymeta_g995d8b84731673c88c7c01f3fed538b9} 
+
+\doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} \end{Desc}
+\index{keymeta@{keymeta}!keyGetCTime@{keyGetCTime}}
+\index{keyGetCTime@{keyGetCTime}!keymeta@{keymeta}}
+\subsubsection[keyGetCTime]{\setlength{\rightskip}{0pt plus 5cm}time\_\-t keyGetCTime (const Key $\ast$ {\em key})}\label{group__keymeta_g2c213c120cbe02201278ef7fb8cd94be}
+
+
+Get last time the key metadata was changed from disk.
+
+You will get 0 when the key was not read already.
+
+Any changed field in metadata will influence the ctime of a key.
+
+This time is not updated if only value or comment are changed.
+
+Not changed keys will not update this time, even after \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}.
+
+It is possible that other keys written to disc influence this time if the backend is not grained enough.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]Key to get information from. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetCTime()}{p.}{group__keymeta_g9f502ecab8ab43f0b17220fcc95f3fa5} \end{Desc}
+\begin{Desc}
+\item[Returns:](time\_\-t)-1 on NULL pointer 
+
+the metadata change time \end{Desc}
+\index{keymeta@{keymeta}!keyGetGID@{keyGetGID}}
+\index{keyGetGID@{keyGetGID}!keymeta@{keymeta}}
+\subsubsection[keyGetGID]{\setlength{\rightskip}{0pt plus 5cm}gid\_\-t keyGetGID (const Key $\ast$ {\em key})}\label{group__keymeta_g46a95e81d7d7f4e3eb59e60e5f3738c0}
+
+
+Get the group ID of a key.\subsection{GID}\label{group__keymeta_GID}
+The group ID is a unique identification for every group present on a system. Keys will belong to root (0) as long as you did not get their real GID with \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}.
+
+Unlike UID users might change their group. This makes it possible to share configuration between some users.
+
+A fresh key will have (gid\_\-t)-1 also known as the group nogroup. It means that the key is not related to a group ID at the moment.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the system's GID of the key 
+
+(gid\_\-t)-1 on NULL key or currently unknown ID \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetGID()}{p.}{group__keymeta_g9e3d0fb3f7ba906e067727b9155d22e3}, \doxyref{keyGetUID()}{p.}{group__keymeta_gcaa5060e67b03f50ae49a3620c54bc46} \end{Desc}
+\index{keymeta@{keymeta}!keyGetMode@{keyGetMode}}
+\index{keyGetMode@{keyGetMode}!keymeta@{keymeta}}
+\subsubsection[keyGetMode]{\setlength{\rightskip}{0pt plus 5cm}mode\_\-t keyGetMode (const Key $\ast$ {\em key})}\label{group__keymeta_gbc0cec592ce3b77e9bc33dbc8e8f6bdc}
+
+
+Return the key mode permissions.
+
+Default is 0664 (octal) for keys and 0775 for directory keys which used \doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75}.
+
+The defaults are defined with the macros KEY\_\-DEF\_\-MODE and KEY\_\-DEF\_\-DIR.
+
+For more information about the mode permissions see \doxyref{Mode}{p.}{group__backend_mode}.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]mode permissions of the key 
+
+(mode\_\-t)-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3} \end{Desc}
+\index{keymeta@{keymeta}!keyGetMTime@{keyGetMTime}}
+\index{keyGetMTime@{keyGetMTime}!keymeta@{keymeta}}
+\subsubsection[keyGetMTime]{\setlength{\rightskip}{0pt plus 5cm}time\_\-t keyGetMTime (const Key $\ast$ {\em key})}\label{group__keymeta_g57689eb5691679071463b777ae786ae9}
+
+
+Get last modification time of the key on disk.
+
+You will get 0 when the key was not read already.
+
+Everytime you change value or comment and \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} the key the mtime will be updated. When you \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} the key, the atime is set appropriate.
+
+Not changed keys may not even passed to \doxyref{kdbSet\_\-backend()}{p.}{group__backend_g2d86ff43b693d59d4b82b597756e9e23} so it will not update this time, even after \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}.
+
+It is possible that other keys written to disc influence this time if the backend is not grained enough.
+
+If you add or remove a key the key thereunder in the hierarchy will update the mtime if written with \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} to disc.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]Key to get information from. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetMTime()}{p.}{group__keymeta_g481d8997187992fe4bbf288bc8ef4db7} \end{Desc}
+\begin{Desc}
+\item[Returns:]the last modification time 
+
+(time\_\-t)-1 on NULL pointer \end{Desc}
+\index{keymeta@{keymeta}!keyGetType@{keyGetType}}
+\index{keyGetType@{keyGetType}!keymeta@{keymeta}}
+\subsubsection[keyGetType]{\setlength{\rightskip}{0pt plus 5cm}type\_\-t keyGetType (const Key $\ast$ {\em key})}\label{group__keymeta_g4fbbf04b1a2c5928dfcec939972e7347}
+
+
+Returns the key data type.
+
+See type\_\-t for the type definition.
+
+\begin{Desc}
+\item[See also:]\doxyref{keySetType()}{p.}{group__keymeta_gb92003db4b938594df48807c16766bf7} 
+
+\doxyref{keyIsBinary()}{p.}{group__keytest_g9526b371087564e43e3dff8ad0dac949} and \doxyref{keyIsString()}{p.}{group__keytest_gea7670778abd07fee0fe8ac12a149190} 
+
+\doxyref{keyIsDir()}{p.}{group__keytest_gc0a10c602d52a35f81347e8a32312017} is not related to the type system \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]key where to get the type. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the key type 
+
+KEY\_\-TYPE\_\-UNDEFINED on keys without type 
+
+-1 on NULL pointer \end{Desc}
+\index{keymeta@{keymeta}!keyGetUID@{keyGetUID}}
+\index{keyGetUID@{keyGetUID}!keymeta@{keymeta}}
+\subsubsection[keyGetUID]{\setlength{\rightskip}{0pt plus 5cm}uid\_\-t keyGetUID (const Key $\ast$ {\em key})}\label{group__keymeta_gcaa5060e67b03f50ae49a3620c54bc46}
+
+
+Get the user ID of a key.\subsection{UID}\label{group__keymeta_UID}
+The user ID is a unique identification for every user present on a system. Keys will belong to root (0) as long as you did not get their real UID with \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}.
+
+Although usually the same, the UID of a key is not related to its owner.
+
+A fresh key will have (uid\_\-t)-1 also known as the user nobody. It means that the key is not related to a user ID at the moment.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the system's UID of the key 
+
+(uid\_\-t)-1 on NULL key or currently unknown ID \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetGID()}{p.}{group__keymeta_g46a95e81d7d7f4e3eb59e60e5f3738c0}, \doxyref{keySetUID()}{p.}{group__keymeta_gb5f284f5ecd261e0a290095f50ba1af7}, \doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a} \end{Desc}
+\index{keymeta@{keymeta}!keyRemove@{keyRemove}}
+\index{keyRemove@{keyRemove}!keymeta@{keymeta}}
+\subsubsection[keyRemove]{\setlength{\rightskip}{0pt plus 5cm}int keyRemove (Key $\ast$ {\em key})}\label{group__keymeta_g6e14e5f1de26e1318100631a149f2984}
+
+
+Permanently remove a key after committing to database.
+
+This functions sets a flag that the key needs to be removed. It also sets a flag that it is not synced.
+
+Remove the key instead of writing it in the key database when doing \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} and related functions.
+
+This key will be ignored and it is save to delete it afterwards. To be sure that it was removed, check if it needs sync with \doxyref{keyNeedSync()}{p.}{group__keytest_gf247df0de7aca04b32ef80e39ef12950}.
+
+\begin{Desc}
+\item[Note:]Delete in elektra terminology means to free memory, remove means to free permanent storage.\end{Desc}
+\begin{Desc}
+\item[Warning:]You should not change a key's remove status once it belongs to a keyset. See \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} for more information.\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyNeedRemove()}{p.}{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9}, \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}, \doxyref{kdbRemove()}{p.}{group__kdbhighlevel_gf9adbbeb3f49c63fb2f89930445c8060} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 on success 
+
+-1 on NULL pointer \end{Desc}
+\index{keymeta@{keymeta}!keySetATime@{keySetATime}}
+\index{keySetATime@{keySetATime}!keymeta@{keymeta}}
+\subsubsection[keySetATime]{\setlength{\rightskip}{0pt plus 5cm}int keySetATime (Key $\ast$ {\em key}, \/  time\_\-t {\em atime})}\label{group__keymeta_g995d8b84731673c88c7c01f3fed538b9}
+
+
+Update the atime information for a key.
+
+When you do manual sync of keys you might also update the atime to make them indistinguishable.
+
+It can also be useful if you work with keys not using a keydatabase.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]The Key object to work with \item[{\em atime}]The new access time for the key \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetATime()}{p.}{group__keymeta_g6b05da399c3c78904969ef39f191b0eb} \end{Desc}
+\index{keymeta@{keymeta}!keySetCTime@{keySetCTime}}
+\index{keySetCTime@{keySetCTime}!keymeta@{keymeta}}
+\subsubsection[keySetCTime]{\setlength{\rightskip}{0pt plus 5cm}int keySetCTime (Key $\ast$ {\em key}, \/  time\_\-t {\em ctime})}\label{group__keymeta_g9f502ecab8ab43f0b17220fcc95f3fa5}
+
+
+Update the ctime information for a key.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]The Key object to work with \item[{\em ctime}]The new change metadata time for the key \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetCTime()}{p.}{group__keymeta_g2c213c120cbe02201278ef7fb8cd94be} \end{Desc}
+\index{keymeta@{keymeta}!keySetDir@{keySetDir}}
+\index{keySetDir@{keySetDir}!keymeta@{keymeta}}
+\subsubsection[keySetDir]{\setlength{\rightskip}{0pt plus 5cm}int keySetDir (Key $\ast$ {\em key})}\label{group__keymeta_gae575bd86a628a15ee45baa860522e75}
+
+
+Set mode so that key will be recognized as directory.
+
+The function will add all executable bits.
+
+\begin{itemize}
+\item Mode 0200 will be translated to 0311\item Mode 0400 will be translated to 0711\item Mode 0664 will be translated to 0775\end{itemize}
+
+
+The macro KEY\_\-DEF\_\-DIR (defined to 0111) will be used for that.
+
+The executable bits show that child keys are allowed and listable. There is no way to have child keys which are not listable for anyone, but it is possible to restrict listing the keys to the owner only.
+
+\begin{itemize}
+\item Mode 0000 means that it is a key not read or writable to anyone.\item Mode 0111 means that it is a directory not read or writable to anyone. But it is recognized as directory to anyone.\end{itemize}
+
+
+For more about mode see \doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3}.
+
+It is not possible to access keys below a not executable key. If a key is not writeable and executable \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} will fail to access the keys below. If a key is not readable and executable \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} will fail to access the keys below.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key to set permissions to be recognized as directory. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3} \end{Desc}
+\index{keymeta@{keymeta}!keySetGID@{keySetGID}}
+\index{keySetGID@{keySetGID}!keymeta@{keymeta}}
+\subsubsection[keySetGID]{\setlength{\rightskip}{0pt plus 5cm}int keySetGID (Key $\ast$ {\em key}, \/  gid\_\-t {\em gid})}\label{group__keymeta_g9e3d0fb3f7ba906e067727b9155d22e3}
+
+
+Set the group ID of a key.
+
+See \doxyref{GID}{p.}{group__keymeta_GID} for more information about group IDs.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em gid}]is the group ID \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL key \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetGID()}{p.}{group__keymeta_g46a95e81d7d7f4e3eb59e60e5f3738c0}, \doxyref{keySetUID()}{p.}{group__keymeta_gb5f284f5ecd261e0a290095f50ba1af7} \end{Desc}
+\index{keymeta@{keymeta}!keySetMode@{keySetMode}}
+\index{keySetMode@{keySetMode}!keymeta@{keymeta}}
+\subsubsection[keySetMode]{\setlength{\rightskip}{0pt plus 5cm}int keySetMode (Key $\ast$ {\em key}, \/  mode\_\-t {\em mode})}\label{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3}
+
+
+Set the key mode permissions.
+
+The mode consists of 9 individual bits for mode permissions. In the following explanation the octal notation with leading zero will be used.
+
+Default is 0664 (octal) for keys and 0775 for directory keys which used \doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75}.
+
+The defaults are defined with the macros KEY\_\-DEF\_\-MODE and KEY\_\-DEF\_\-DIR.
+
+\begin{Desc}
+\item[Note:]libelektra 0.7.0 only allows 0775 (directory keys) and 0664 (other keys). More will be added later in a sense of the description below.\end{Desc}
+\subsection{Mode}\label{group__backend_mode}
+0000 is the most restrictive mode. No user might read, write or execute the key.
+
+Reading the key means to get the value and comment by \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} or all highlevel methods.
+
+Writing the key means to set the value and comment by \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} or all highlevel methods.
+
+Execute the key means to make a step deeper in the hierarchy. But you must be able to read the key to be able to list the keys below. See also \doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75} in that context. But you must be able to write the key to be able to add or remove keys below.
+
+0777 is the most relaxing mode. Every user is allowed to read, write and execute the key, if he is allowed to execute and read all keys below.
+
+0700 allows every action for the current user, identified by the uid. See \doxyref{keyGetUID()}{p.}{group__keymeta_gcaa5060e67b03f50ae49a3620c54bc46} and \doxyref{keySetUID()}{p.}{group__keymeta_gb5f284f5ecd261e0a290095f50ba1af7}.
+
+To be more specific for the user the single bits can elect the mode for read, write and execute. 0100 only allows executing which gives the information that it is a directory for that user, but not accessable. 0200 only allows reading. This information may be combined to 0300, which allows execute and reading of the directory. Last 0400 decides about the writing permissions.
+
+The same as above is also valid for the 2 other octal digits. 0070 decides about the group permissions, in that case full access. Groups are identified by the gid. See \doxyref{keyGetGID()}{p.}{group__keymeta_g46a95e81d7d7f4e3eb59e60e5f3738c0} and \doxyref{keySetGID()}{p.}{group__keymeta_g9e3d0fb3f7ba906e067727b9155d22e3}. In that example everyone with a different uid, but the gid of the the key, has full access.
+
+0007 decides about the world permissions. This is taken into account when neighter the uid nor the gid matches. So that example would allow everyone with a different uid and gid of that key gains full access.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key to set mode permissions \item[{\em mode}]the mode permissions \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL key \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetMode()}{p.}{group__keymeta_gbc0cec592ce3b77e9bc33dbc8e8f6bdc} \end{Desc}
+\index{keymeta@{keymeta}!keySetMTime@{keySetMTime}}
+\index{keySetMTime@{keySetMTime}!keymeta@{keymeta}}
+\subsubsection[keySetMTime]{\setlength{\rightskip}{0pt plus 5cm}int keySetMTime (Key $\ast$ {\em key}, \/  time\_\-t {\em mtime})}\label{group__keymeta_g481d8997187992fe4bbf288bc8ef4db7}
+
+
+Update the mtime information for a key.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]The Key object to work with \item[{\em mtime}]The new modification time for the key \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetMTime()}{p.}{group__keymeta_g57689eb5691679071463b777ae786ae9} \end{Desc}
+\index{keymeta@{keymeta}!keySetType@{keySetType}}
+\index{keySetType@{keySetType}!keymeta@{keymeta}}
+\subsubsection[keySetType]{\setlength{\rightskip}{0pt plus 5cm}int keySetType (Key $\ast$ {\em key}, \/  type\_\-t {\em newType})}\label{group__keymeta_gb92003db4b938594df48807c16766bf7}
+
+
+Set a new key type.
+
+This method is usually not needed, unless you are working with more semantic value types, or want to force a specific value type for a key. It is not usually needed because the data type is automatically set when setting the key value.
+
+See type\_\-t for the type defintion.
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}// here we define the new type
+enum
+{
+        KEY_TYPE_COLOR=KEY_TYPE_STRING+4
+};
+// here we make a new key with the type
+Key *k1 = keyNew ("user/sw/oyranos/current/color1",
+        KEY_VALUE, "#4B52CA",
+        KEY_COMMENT, "a custom color",
+        KEY_TYPE, KEY_TYPE_COLOR,
+        KEY_END);
+// lets check if it is really correct type
+if (keyGetType(k1) == KEY_TYPE_COLOR) printf ("correct type");
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+When using type\_\-t::KEY\_\-TYPE\_\-DIR, this method will not set mode permissions to the key. You'll have to set it manually after \doxyref{keySetType()}{p.}{group__keymeta_gb92003db4b938594df48807c16766bf7}, calling \doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3} with appropriate permissions. Or use the \doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75}.
+
+\begin{Desc}
+\item[See also:]\doxyref{keyGetType()}{p.}{group__keymeta_g4fbbf04b1a2c5928dfcec939972e7347} 
+
+\doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75} to see that the directory concept is independent of types \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em newType}]contains the new type \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on sucess 
+
+-1 on NULL pointer and when newType $>$= KEY\_\-TYPE\_\-MAX \end{Desc}
+\index{keymeta@{keymeta}!keySetUID@{keySetUID}}
+\index{keySetUID@{keySetUID}!keymeta@{keymeta}}
+\subsubsection[keySetUID]{\setlength{\rightskip}{0pt plus 5cm}int keySetUID (Key $\ast$ {\em key}, \/  uid\_\-t {\em uid})}\label{group__keymeta_gb5f284f5ecd261e0a290095f50ba1af7}
+
+
+Set the user ID of a key.
+
+See \doxyref{UID}{p.}{group__keymeta_UID} for more information about user IDs.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em uid}]the user ID to set \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL key \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetGID()}{p.}{group__keymeta_g9e3d0fb3f7ba906e067727b9155d22e3}, \doxyref{keyGetUID()}{p.}{group__keymeta_gcaa5060e67b03f50ae49a3620c54bc46}, \doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a} \end{Desc}
+\index{keymeta@{keymeta}!keyStat@{keyStat}}
+\index{keyStat@{keyStat}!keymeta@{keymeta}}
+\subsubsection[keyStat]{\setlength{\rightskip}{0pt plus 5cm}int keyStat (Key $\ast$ {\em key})}\label{group__keymeta_gb8189add5e562bdb148675ee595bd95b}
+
+
+Only stat a key instead of receiving value, comment and key type.
+
+Only stat the key in the database when doing \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}. The key may not have any value, comment or key type set.
+
+It is not possible to revert the action on per-key basis. When you want to remove the flag you have to pass option\_\-t::KDB\_\-O\_\-NOSTAT to the next \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}.
+
+\begin{Desc}
+\item[See also:]\doxyref{keyNeedStat()}{p.}{group__keytest_g3908b6511648a950f37cd0005bfea5d5}, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 on succuess 
+
+-1 on NULL pointer \end{Desc}
diff --git a/doc/elektra-api/latex/group__keyname.tex b/doc/elektra-api/latex/group__keyname.tex
new file mode 100644 (file)
index 0000000..9da42a7
--- /dev/null
@@ -0,0 +1,502 @@
+\section{Key :: Name Manipulation Methods}
+\label{group__keyname}\index{Key :: Name Manipulation Methods@{Key :: Name Manipulation Methods}}
+Methods to do various operations on Key names.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+const char $\ast$ {\bf keyName} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetNameSize} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetName} (const Key $\ast$key, char $\ast$returnedName, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf keySetName} (Key $\ast$key, const char $\ast$newName)
+\item 
+ssize\_\-t {\bf keyGetFullNameSize} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetFullName} (const Key $\ast$key, char $\ast$returnedName, size\_\-t maxSize)
+\item 
+const char $\ast$ {\bf keyBaseName} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetBaseNameSize} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetBaseName} (const Key $\ast$key, char $\ast$returned, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf keyAddBaseName} (Key $\ast$key, const char $\ast$baseName)
+\item 
+ssize\_\-t {\bf keySetBaseName} (Key $\ast$key, const char $\ast$baseName)
+\item 
+const char $\ast$ {\bf keyOwner} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetOwnerSize} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetOwner} (const Key $\ast$key, char $\ast$returned, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf keySetOwner} (Key $\ast$key, const char $\ast$owner)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to do various operations on Key names. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim}#include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+
+These functions make it easier for c programmers to work with key names. Everything here can also be done with keySetName, described in key.
+
+\begin{Desc}
+\item[Rules for Key Names]\end{Desc}
+When using Elektra to store your application's configuration and state, please keep in mind the following rules:\begin{itemize}
+\item You are not allowed to create keys right under {\tt system} or {\tt user}.\item You are not allowed to create folder keys right under {\tt system} or {\tt user}. They are reserved for very essential OS subsystems.\item The keys for your application, called say {\em MyApp\/}, should be created under {\tt system/sw/MyApp} and/or {\tt user/sw/MyApp}.\item It is suggested to make your application look for default keys under {\tt system/sw/MyApp/current} and/or {\tt user/sw/MyApp/current}. This way, from a sysadmin perspective, it will be possible to copy the {\tt system/sw/MyApp/current} tree to something like {\tt system/sw/MyApp/old}, and keep system clean and organized.\item $\backslash$0 must not occur in names.\item / is the seperator. \end{itemize}
+
+
+\subsection{Function Documentation}
+\index{keyname@{keyname}!keyAddBaseName@{keyAddBaseName}}
+\index{keyAddBaseName@{keyAddBaseName}!keyname@{keyname}}
+\subsubsection[keyAddBaseName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyAddBaseName (Key $\ast$ {\em key}, \/  const char $\ast$ {\em baseName})}\label{group__keyname_ga942091fc4bd5c2699e49ddc50829524}
+
+
+Adds {\tt baseName} to the current key name.
+
+Assumes that {\tt key} is a directory. {\tt baseName} is appended to it. The function adds {\tt '/'} if needed while concatenating.
+
+So if {\tt key} has name {\tt \char`\"{}system/dir1/dir2\char`\"{}} and this method is called with {\tt baseName} {\tt \char`\"{}mykey\char`\"{}}, the resulting key will have name {\tt \char`\"{}system/dir1/dir2/mykey\char`\"{}}.
+
+When baseName is 0 or \char`\"{}\char`\"{} nothing will happen and the size of the name is returned.
+
+\begin{Desc}
+\item[Warning:]You should not change a keys name once it belongs to a keyset. See \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} for more information.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em baseName}]the string to append to the name \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the size in bytes of the new key name including the ending NULL 
+
+-1 if the key had no name 
+
+-1 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetBaseName()}{p.}{group__keyname_g6e804bd453f98c28b0ff51430d1df407} 
+
+\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} to set a new name. \end{Desc}
+\index{keyname@{keyname}!keyBaseName@{keyBaseName}}
+\index{keyBaseName@{keyBaseName}!keyname@{keyname}}
+\subsubsection[keyBaseName]{\setlength{\rightskip}{0pt plus 5cm}const char$\ast$ keyBaseName (const Key $\ast$ {\em key})}\label{group__keyname_gaff35e7ca8af5560c47e662ceb9465f5}
+
+
+Returns a pointer to the real internal key name where the {\tt basename} starts.
+
+This is a much more efficient version of \doxyref{keyGetBaseName()}{p.}{group__keyname_g0992d26bcfca767cb8e77053a483eb64} and you should use it if you are responsible enough to not mess up things. The name might change or even point to a wrong place after a \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}. If you need a copy of the basename consider to use \doxyref{keyGetBaseName()}{p.}{group__keyname_g0992d26bcfca767cb8e77053a483eb64}.
+
+\doxyref{keyBaseName()}{p.}{group__keyname_gaff35e7ca8af5560c47e662ceb9465f5} returns \char`\"{}\char`\"{} when there is no keyBaseName. The reason is 
+
+\begin{Code}\begin{verbatim}key=keyNew(0);
+keySetName(key,"");
+keyName(key); // you would expect "" here
+keySetName(key,"user");
+keyName(key); // you would expect "" here
+keyDel(key);
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Note:]Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \doxyref{keyBaseName()}{p.}{group__keyname_gaff35e7ca8af5560c47e662ceb9465f5} method to set a new value. Use \doxyref{keySetBaseName()}{p.}{group__keyname_g6e804bd453f98c28b0ff51430d1df407} instead.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the object to obtain the basename from \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to the basename 
+
+\char`\"{}\char`\"{} on null pointer or when the key has no name 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetBaseName()}{p.}{group__keyname_g0992d26bcfca767cb8e77053a483eb64}, \doxyref{keyGetBaseNameSize()}{p.}{group__keyname_g1a0b76c5d9e5367c7e72211e6c63d43a} 
+
+\doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} to get a pointer to the name 
+
+\doxyref{keyOwner()}{p.}{group__keyname_gf6485fb8599714b6bbd830cf915ffea5} to get a pointer to the owner \end{Desc}
+\index{keyname@{keyname}!keyGetBaseName@{keyGetBaseName}}
+\index{keyGetBaseName@{keyGetBaseName}!keyname@{keyname}}
+\subsubsection[keyGetBaseName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetBaseName (const Key $\ast$ {\em key}, \/  char $\ast$ {\em returned}, \/  size\_\-t {\em maxSize})}\label{group__keyname_g0992d26bcfca767cb8e77053a483eb64}
+
+
+Calculate the basename of a key name and put it in {\tt returned} finalizing the string with NULL.
+
+Some examples:\begin{itemize}
+\item basename of {\tt system/some/keyname} is {\tt keyname} \item basename of {\tt \char`\"{}user/tmp/some key\char`\"{}} is {\tt \char`\"{}some key\char`\"{}} \end{itemize}
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key to extract basename from \item[{\em returned}]a pre-allocated buffer to store the basename \item[{\em maxSize}]size of the {\tt returned} buffer \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes copied to {\tt returned} 
+
+1 on empty name 
+
+-1 on NULL pointers 
+
+-1 when maxSize is 0 or larger than SSIZE\_\-MAX \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyBaseName()}{p.}{group__keyname_gaff35e7ca8af5560c47e662ceb9465f5}, \doxyref{keyGetBaseNameSize()}{p.}{group__keyname_g1a0b76c5d9e5367c7e72211e6c63d43a} 
+
+\doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513}, \doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}, \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} \end{Desc}
+\index{keyname@{keyname}!keyGetBaseNameSize@{keyGetBaseNameSize}}
+\index{keyGetBaseNameSize@{keyGetBaseNameSize}!keyname@{keyname}}
+\subsubsection[keyGetBaseNameSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetBaseNameSize (const Key $\ast$ {\em key})}\label{group__keyname_g1a0b76c5d9e5367c7e72211e6c63d43a}
+
+
+Calculates number of bytes needed to store basename of {\tt key}.
+
+Key names that have only root names (e.g. {\tt \char`\"{}system\char`\"{}} or {\tt \char`\"{}user\char`\"{}} or {\tt \char`\"{}user:domain\char`\"{}} ) does not have basenames, thus the function will return 1 bytes to store \char`\"{}\char`\"{}.
+
+Basenames are denoted as:\begin{itemize}
+\item {\tt system/some/thing/basename} -$>$ {\tt basename} \item {\tt user:domain/some/thing/base$\backslash$/name} $>$ {\tt base$\backslash$/name} \end{itemize}
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]size in bytes of {\tt key's} basename including ending NULL \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyBaseName()}{p.}{group__keyname_gaff35e7ca8af5560c47e662ceb9465f5}, \doxyref{keyGetBaseName()}{p.}{group__keyname_g0992d26bcfca767cb8e77053a483eb64} 
+
+\doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513}, \doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}, \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} \end{Desc}
+\index{keyname@{keyname}!keyGetFullName@{keyGetFullName}}
+\index{keyGetFullName@{keyGetFullName}!keyname@{keyname}}
+\subsubsection[keyGetFullName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetFullName (const Key $\ast$ {\em key}, \/  char $\ast$ {\em returnedName}, \/  size\_\-t {\em maxSize})}\label{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c}
+
+
+Get key full name, including the user domain name.
+
+\begin{Desc}
+\item[Returns:]number of bytes written 
+
+1 on empty name 
+
+-1 on NULL pointers 
+
+-1 if maxSize is 0 or larger than SSIZE\_\-MAX \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object \item[{\em returnedName}]pre-allocated memory to write the key name \item[{\em maxSize}]maximum number of bytes that will fit in returnedName, including the final NULL \end{description}
+\end{Desc}
+\index{keyname@{keyname}!keyGetFullNameSize@{keyGetFullNameSize}}
+\index{keyGetFullNameSize@{keyGetFullNameSize}!keyname@{keyname}}
+\subsubsection[keyGetFullNameSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetFullNameSize (const Key $\ast$ {\em key})}\label{group__keyname_gb65dc9d43d3ee08d5e936a20ebbddd23}
+
+
+Bytes needed to store the key name including user domain and ending NULL.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes needed to store key name including user domain 
+
+1 on empty name 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c}, \doxyref{keyGetNameSize()}{p.}{group__keyname_gbdbcfa51ed8a387e47ead207affa2d2e} \end{Desc}
+\index{keyname@{keyname}!keyGetName@{keyGetName}}
+\index{keyGetName@{keyGetName}!keyname@{keyname}}
+\subsubsection[keyGetName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetName (const Key $\ast$ {\em key}, \/  char $\ast$ {\em returnedName}, \/  size\_\-t {\em maxSize})}\label{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}
+
+
+Get abbreviated key name (without owner name).
+
+When there is not enough space to write the name, nothing will be written and -1 will be returned.
+
+maxSize is limited to SSIZE\_\-MAX. When this value is exceeded -1 will be returned. The reason for that is that any value higher is just a negative return value passed by accident. Of course malloc is not as failure tolerant and will try to allocate.
+
+\begin{Desc}
+\item[Returns:]number of bytes written to {\tt returnedName} 
+
+1 when only a null was written 
+
+-1 when keyname is longer then maxSize or 0 or any NULL pointer \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em returnedName}]pre-allocated memory to write the key name \item[{\em maxSize}]maximum number of bytes that will fit in returnedName, including the final NULL \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetNameSize()}{p.}{group__keyname_gbdbcfa51ed8a387e47ead207affa2d2e}, \doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c}, \doxyref{keyGetFullNameSize()}{p.}{group__keyname_gb65dc9d43d3ee08d5e936a20ebbddd23} \end{Desc}
+\index{keyname@{keyname}!keyGetNameSize@{keyGetNameSize}}
+\index{keyGetNameSize@{keyGetNameSize}!keyname@{keyname}}
+\subsubsection[keyGetNameSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetNameSize (const Key $\ast$ {\em key})}\label{group__keyname_gbdbcfa51ed8a387e47ead207affa2d2e}
+
+
+Bytes needed to store the key name without owner.
+
+For an empty key name you need one byte to store the ending NULL. For that reason 1 is returned.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes needed, including ending NULL, to store key name without owner 
+
+1 if there is is no key Name 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}, \doxyref{keyGetFullNameSize()}{p.}{group__keyname_gb65dc9d43d3ee08d5e936a20ebbddd23} \end{Desc}
+\index{keyname@{keyname}!keyGetOwner@{keyGetOwner}}
+\index{keyGetOwner@{keyGetOwner}!keyname@{keyname}}
+\subsubsection[keyGetOwner]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetOwner (const Key $\ast$ {\em key}, \/  char $\ast$ {\em returned}, \/  size\_\-t {\em maxSize})}\label{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a}
+
+
+Return the owner of the key.\begin{itemize}
+\item Given {\tt user:someuser/}..... return {\tt someuser} \item Given {\tt user:some.user/}.... return {\tt some.user} \item Given {\tt user/}.... return the current user\end{itemize}
+
+
+Only {\tt user/}... keys have a owner. For {\tt system/}... keys (that doesn't have a key owner) an empty string (\char`\"{}\char`\"{}) is returned.
+
+Although usually the same, the owner of a key is not related to its UID. Owner are related to WHERE the key is stored on disk, while UIDs are related to mode controls of a key.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the object to work with \item[{\em returned}]a pre-allocated space to store the owner \item[{\em maxSize}]maximum number of bytes that fit returned \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to buffer 
+
+1 if there is no owner 
+
+-1 on NULL pointers 
+
+-1 when maxSize is 0, larger than SSIZE\_\-MAX or too small for ownername \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keySetOwner()}{p.}{group__keyname_ga899d9f0251cb98a89761ef112910eca}, \doxyref{keyOwner()}{p.}{group__keyname_gf6485fb8599714b6bbd830cf915ffea5}, \doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c} \end{Desc}
+\index{keyname@{keyname}!keyGetOwnerSize@{keyGetOwnerSize}}
+\index{keyGetOwnerSize@{keyGetOwnerSize}!keyname@{keyname}}
+\subsubsection[keyGetOwnerSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetOwnerSize (const Key $\ast$ {\em key})}\label{group__keyname_g4a4561895741ba2ad10acf007c188593}
+
+
+Return the size of the owner of the Key with concluding 0.
+
+The returned number can be used to allocate a string. 1 will returned on an empty owner to store the concluding 0 on using \doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a}.
+
+
+
+\begin{Code}\begin{verbatim}char * buffer;
+buffer = malloc (keyGetOwnerSize (key));
+// use buffer and keyGetOwnerSize (key) for maxSize
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Note:]that -1 might be returned on null pointer, so when you directly allocate afterwards its best to check if you will pass a null pointer before.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes 
+
+1 if there is no owner 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a} \end{Desc}
+\index{keyname@{keyname}!keyName@{keyName}}
+\index{keyName@{keyName}!keyname@{keyname}}
+\subsubsection[keyName]{\setlength{\rightskip}{0pt plus 5cm}const char$\ast$ keyName (const Key $\ast$ {\em key})}\label{group__keyname_g8e805c726a60da921d3736cda7813513}
+
+
+Returns a pointer to the abbreviated real internal {\tt key} name.
+
+This is a much more efficient version of \doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be} and can use it if you are responsible enough to not mess up things. You are not allowed to change anything in the returned array. The content of that string may change after \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} and similar functions. If you need a copy of the name, consider using \doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}.
+
+The name will be without owner, see \doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c} if you need the name with its owner.
+
+\doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} returns \char`\"{}\char`\"{} when there is no keyName. The reason is 
+
+\begin{Code}\begin{verbatim}key=keyNew(0);
+keySetName(key,"");
+keyName(key); // you would expect "" here
+keyDel(key);
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Note:]Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} method to set a new value. Use \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} instead.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to the keyname which must not be changed. 
+
+\char`\"{}\char`\"{} when there is no (a empty) keyname 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetNameSize()}{p.}{group__keyname_gbdbcfa51ed8a387e47ead207affa2d2e} for the string length 
+
+\doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c}, \doxyref{keyGetFullNameSize()}{p.}{group__keyname_gb65dc9d43d3ee08d5e936a20ebbddd23} to get the full name 
+
+\doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be} as alternative to get a copy 
+
+\doxyref{keyOwner()}{p.}{group__keyname_gf6485fb8599714b6bbd830cf915ffea5} to get a pointer to owner \end{Desc}
+\index{keyname@{keyname}!keyOwner@{keyOwner}}
+\index{keyOwner@{keyOwner}!keyname@{keyname}}
+\subsubsection[keyOwner]{\setlength{\rightskip}{0pt plus 5cm}const char$\ast$ keyOwner (const Key $\ast$ {\em key})}\label{group__keyname_gf6485fb8599714b6bbd830cf915ffea5}
+
+
+Return a pointer to the real internal {\tt key} owner.
+
+This is a much more efficient version of \doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a} and you should use it if you are responsible enough to not mess up things. You are not allowed to modify the returned string in any way. If you need a copy of the string, consider to use \doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a} instead.
+
+\doxyref{keyOwner()}{p.}{group__keyname_gf6485fb8599714b6bbd830cf915ffea5} returns \char`\"{}\char`\"{} when there is no keyOwner. The reason is 
+
+\begin{Code}\begin{verbatim}key=keyNew(0);
+keySetOwner(key,"");
+keyOwner(key); // you would expect "" here
+keySetOwner(key,"system");
+keyOwner(key); // you would expect "" here
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Note:]Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \doxyref{keyOwner()}{p.}{group__keyname_gf6485fb8599714b6bbd830cf915ffea5} method to set a new value. Use \doxyref{keySetOwner()}{p.}{group__keyname_ga899d9f0251cb98a89761ef112910eca} instead.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to internal owner 
+
+\char`\"{}\char`\"{} when there is no (a empty) owner 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetOwnerSize()}{p.}{group__keyname_g4a4561895741ba2ad10acf007c188593} for the size of the string with concluding 0 
+
+\doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a}, \doxyref{keySetOwner()}{p.}{group__keyname_ga899d9f0251cb98a89761ef112910eca} 
+
+\doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} for name without owner 
+
+\doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c} for name with owner \end{Desc}
+\index{keyname@{keyname}!keySetBaseName@{keySetBaseName}}
+\index{keySetBaseName@{keySetBaseName}!keyname@{keyname}}
+\subsubsection[keySetBaseName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keySetBaseName (Key $\ast$ {\em key}, \/  const char $\ast$ {\em baseName})}\label{group__keyname_g6e804bd453f98c28b0ff51430d1df407}
+
+
+Sets {\tt baseName} as the new basename for {\tt key}.
+
+All text after the last {\tt '/'} in the {\tt key} keyname is erased and {\tt baseName} is appended.
+
+So lets suppose {\tt key} has name {\tt \char`\"{}system/dir1/dir2/key1\char`\"{}}. If {\tt baseName} is {\tt \char`\"{}key2\char`\"{}}, the resulting key name will be {\tt \char`\"{}system/dir1/dir2/key2\char`\"{}}. If {\tt baseName} is empty or NULL, the resulting key name will be {\tt \char`\"{}system/dir1/dir2\char`\"{}}.
+
+\begin{Desc}
+\item[Warning:]You should not change a keys name once it belongs to a keyset. See \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} for more information.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em baseName}]the string used to overwrite the basename of the key \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the size in bytes of the new key name 
+
+-1 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyAddBaseName()}{p.}{group__keyname_ga942091fc4bd5c2699e49ddc50829524} 
+
+\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} to set a new name \end{Desc}
+\index{keyname@{keyname}!keySetName@{keySetName}}
+\index{keySetName@{keySetName}!keyname@{keyname}}
+\subsubsection[keySetName]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keySetName (Key $\ast$ {\em key}, \/  const char $\ast$ {\em newName})}\label{group__keyname_g7699091610e7f3f43d2949514a4b35d9}
+
+
+Set a new name to a key.
+
+A valid name is of the forms:\begin{itemize}
+\item {\tt system/something} \item {\tt user/something} \item {\tt user:username/something} \end{itemize}
+
+
+The last form has explicitly set the owner, to let the library know in which user folder to save the key. A owner is a user name. If not defined (the second form) current user is calculated and used as default.
+
+You should always follow the guidelines for key tree structure creation.
+
+A private copy of the key name will be stored, and the {\tt newName} parameter can be freed after this call.
+
+.., . and / will be handled correctly. A valid name will be build out of the (valid) name what you pass, e.g. user///sw/../sw//././MyApp -$>$ user/sw/MyApp
+
+On invalid names, NULL or \char`\"{}\char`\"{} the name will be \char`\"{}\char`\"{} afterwards.
+
+\begin{Desc}
+\item[Warning:]You should not change a keys name once it belongs to a keyset. See \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} for more information.\end{Desc}
+\begin{Desc}
+\item[Returns:]size in bytes of this new key name including ending NULL 
+
+-1 if {\tt newName} is empty or invalid or any NULL pointer \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em newName}]the new key name \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}, \doxyref{keySetOwner()}{p.}{group__keyname_ga899d9f0251cb98a89761ef112910eca} 
+
+\doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}, \doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c}, \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} 
+
+\doxyref{keySetBaseName()}{p.}{group__keyname_g6e804bd453f98c28b0ff51430d1df407}, \doxyref{keyAddBaseName()}{p.}{group__keyname_ga942091fc4bd5c2699e49ddc50829524} to manipulate a name \end{Desc}
+\index{keyname@{keyname}!keySetOwner@{keySetOwner}}
+\index{keySetOwner@{keySetOwner}!keyname@{keyname}}
+\subsubsection[keySetOwner]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keySetOwner (Key $\ast$ {\em key}, \/  const char $\ast$ {\em owner})}\label{group__keyname_ga899d9f0251cb98a89761ef112910eca}
+
+
+Set the owner of a key.
+
+A owner is a name of a system user related to a UID. The owner decides on which location on the disc the key goes.
+
+A private copy is stored, so the passed parameter can be freed after the call.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em owner}]the owner (or user name) \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually saved including final NULL 
+
+1 when owner is freed (by setting 0 or \char`\"{}\char`\"{}) 
+
+-1 on null pointer or memory problems \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keyGetOwner()}{p.}{group__keyname_g6d612841c829a638a8fbbbd4a31cc54a}, \doxyref{keyGetFullName()}{p.}{group__keyname_gaba1494a5ffc976e0e56c43f4334a23c} \end{Desc}
diff --git a/doc/elektra-api/latex/group__keyset.tex b/doc/elektra-api/latex/group__keyset.tex
new file mode 100644 (file)
index 0000000..db25d2a
--- /dev/null
@@ -0,0 +1,798 @@
+\section{KeySet :: Class Methods}
+\label{group__keyset}\index{KeySet :: Class Methods@{KeySet :: Class Methods}}
+Methods to manipulate KeySets.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+KeySet $\ast$ {\bf ksNew} (size\_\-t alloc,...)
+\item 
+KeySet $\ast$ {\bf ksDup} (const KeySet $\ast$source)
+\item 
+int {\bf ksCopy} (KeySet $\ast$dest, const KeySet $\ast$source)
+\item 
+int {\bf ksDel} (KeySet $\ast$ks)
+\item 
+void {\bf ksSort} (KeySet $\ast$ks)
+\item 
+ssize\_\-t {\bf ksGetSize} (const KeySet $\ast$ks)
+\item 
+ssize\_\-t {\bf ksAppendKey} (KeySet $\ast$ks, Key $\ast$toAppend)
+\item 
+ssize\_\-t {\bf ksAppend} (KeySet $\ast$ks, const KeySet $\ast$toAppend)
+\item 
+Key $\ast$ {\bf ksPop} (KeySet $\ast$ks)
+\item 
+int {\bf ksRewind} (KeySet $\ast$ks)
+\item 
+Key $\ast$ {\bf ksNext} (KeySet $\ast$ks)
+\item 
+Key $\ast$ {\bf ksCurrent} (const KeySet $\ast$ks)
+\item 
+Key $\ast$ {\bf ksHead} (const KeySet $\ast$ks)
+\item 
+Key $\ast$ {\bf ksTail} (const KeySet $\ast$ks)
+\item 
+cursor\_\-t {\bf ksGetCursor} (const KeySet $\ast$ks)
+\item 
+int {\bf ksSetCursor} (KeySet $\ast$ks, cursor\_\-t cursor)
+\item 
+Key $\ast$ {\bf ksLookup} (KeySet $\ast$ks, Key $\ast$key, option\_\-t options)
+\item 
+Key $\ast$ {\bf ksLookupByName} (KeySet $\ast$ks, const char $\ast$name, option\_\-t options)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to manipulate KeySets. 
+
+A KeySet is a unsorted set of keys.
+
+Terminate with ksNew(0) or ksNew(20, ..., KS\_\-END) This is because there is a list of Key$\ast$ required and KS\_\-END has the length of (Key$\ast$).
+
+It can be implemented in various ways like a linked list or with a dynamically allocated array.
+
+With \doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c} you can create a new KeySet.
+
+You can add keys with \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45} in the keyset. \doxyref{ksGetSize()}{p.}{group__keyset_g7474ad6b0a0fa969dbdf267ba5770eee} tells you the current size of the keyset.
+
+With \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70} and \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} you can navigate through the keyset. Don't expect any particular order, but it is assured that you will get every key of the set.
+
+KeySets have an \doxyref{internal cursor }{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}. This is used for \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} and \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}.
+
+KeySet has a fundamental meaning inside elektra. It makes it possible to get and store many keys at once inside the database. In addition to that the class can be used as high level datastructure in applications. With \doxyref{ksLookupByName()}{p.}{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1} it is possible to fetch easily specific keys out of the list of keys.
+
+You can easily create and iterate keys: 
+
+\begin{Code}\begin{verbatim}#include <kdb.h>
+
+// create a new keyset with 3 keys
+// with a hint that about 20 keys will be inside
+KeySet *myConfig = ksNew(20,
+        keyNew ("user/name1", 0),
+        keyNew ("user/name2", 0),
+        keyNew ("user/name3", 0),
+        KS_END);
+// append a key in the keyset
+ksAppendKey(myConfig, keyNew("user/name4", 0));
+
+Key *current;
+ksRewind(myConfig);
+while ((current=ksNext(myConfig))!=0) {
+        printf("Key name is %s.\n", keyName (current));
+}
+ksDel (myConfig); // delete keyset and all keys appended
+\end{verbatim}
+\end{Code}
+
+
+\subsection{Function Documentation}
+\index{keyset@{keyset}!ksAppend@{ksAppend}}
+\index{ksAppend@{ksAppend}!keyset@{keyset}}
+\subsubsection[ksAppend]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t ksAppend (KeySet $\ast$ {\em ks}, \/  const KeySet $\ast$ {\em toAppend})}\label{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7}
+
+
+Append all {\tt toAppend} contained keys to the end of the {\tt ks}.
+
+{\tt toAppend} KeySet will be left unchanged.
+
+Makes the keyset dirty, see \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}.
+
+\begin{Desc}
+\item[Returns:]the size of the KeySet after transfer 
+
+-1 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the KeySet that will receive the keys \item[{\em toAppend}]the KeySet that provides the keys that will be transfered \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}, ksInsert(), ksInsertKeys() \end{Desc}
+\index{keyset@{keyset}!ksAppendKey@{ksAppendKey}}
+\index{ksAppendKey@{ksAppendKey}!keyset@{keyset}}
+\subsubsection[ksAppendKey]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t ksAppendKey (KeySet $\ast$ {\em ks}, \/  Key $\ast$ {\em toAppend})}\label{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}
+
+
+Appends a Key to the end of {\tt ks}.
+
+A pointer to the key will be stored, and not a private copy. So a future \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8} on {\tt ks} may \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} the {\tt toAppend} object, see \doxyref{keyGetRef()}{p.}{group__key_g4aabc4272506dd63161db2bbb42de8ae}.
+
+The reference counter of the key will be incremented, and thus toAppend is not const.
+
+The KeySet internal cursor is not moved.
+
+Makes the keyset dirty, see \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}.
+
+\begin{Desc}
+\item[Returns:]the size of the KeySet after insertion 
+
+-1 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]KeySet that will receive the key \item[{\em toAppend}]Key that will be appended to ks \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]ksInsert(), ksInsertKeys(), \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7}, \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf}, \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8} 
+
+\doxyref{keyIncRef()}{p.}{group__key_g6970a6f254d67af7e39f8e469bb162f1} \end{Desc}
+\index{keyset@{keyset}!ksCopy@{ksCopy}}
+\index{ksCopy@{ksCopy}!keyset@{keyset}}
+\subsubsection[ksCopy]{\setlength{\rightskip}{0pt plus 5cm}int ksCopy (KeySet $\ast$ {\em dest}, \/  const KeySet $\ast$ {\em source})}\label{group__keyset_gba1f1dbea191f4d7e7eb3e4296ae7d5e}
+
+
+Copy a keyset.
+
+Most often you may want a duplicate of a keyset, see \doxyref{ksDup()}{p.}{group__keyset_gc59e4b328245463f1451f68d5106151c} or append keys, see \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7}. But in some situations you need to copy a keyset to a existing keyset, for that this function exists.
+
+You can also use it to clear a keyset when you pass a NULL pointer as {\tt source}.
+
+Note that all keys in {\tt dest} will be deleted. Afterwards the content of the source will be added to the destination and the \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} is set properly in {\tt dest}.
+
+A flat copy is made, so the keys will not be duplicated, but there reference counter is updated, so both keysets need to be \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8}.
+
+
+
+\begin{Code}\begin{verbatim}int f (KeySet *ks)
+{
+        KeySet *c = ksNew (20, ..., KS_END);
+        // c receives keys
+        ksCopy (ks, c); // pass the keyset to the caller
+
+        ksDel (c);
+}       // caller needs to ksDel (ks)
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em source}]has to be an initialized source KeySet or NULL \item[{\em dest}]has to be an initialized KeySet where to write the keys \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 on success 
+
+0 if dest was cleared successfully (source is NULL) 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c}, \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8}, \doxyref{ksDup()}{p.}{group__keyset_gc59e4b328245463f1451f68d5106151c} 
+
+\doxyref{keyCopy()}{p.}{group__key_g6a12cbbe656a1ad9f41b8c681d7a2f92} for copying keys \end{Desc}
+\index{keyset@{keyset}!ksCurrent@{ksCurrent}}
+\index{ksCurrent@{ksCurrent}!keyset@{keyset}}
+\subsubsection[ksCurrent]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksCurrent (const KeySet $\ast$ {\em ks})}\label{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}
+
+
+Return the current Key.
+
+The pointer is NULL if you reached the end or after \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}.
+
+\begin{Desc}
+\item[Note:]You must not delete the key or change the key, use \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69} if you want to delete it.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]pointer to the Key pointed by {\tt ks's} cursor 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9}, \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70} 
+
+kdbMonitorKeys() for a usage example \end{Desc}
+\index{keyset@{keyset}!ksDel@{ksDel}}
+\index{ksDel@{ksDel}!keyset@{keyset}}
+\subsubsection[ksDel]{\setlength{\rightskip}{0pt plus 5cm}int ksDel (KeySet $\ast$ {\em ks})}\label{group__keyset_g27e5c16473b02a422238c8d970db7ac8}
+
+
+A destructor for KeySet objects.
+
+Cleans all internal dynamic attributes, decrement all reference pointers to all keys and then \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} all contained Keys, and free()s the release the KeySet object memory (that was previously allocated by \doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c}).
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 when the keyset was freed 
+
+-1 on null pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c} \end{Desc}
+\index{keyset@{keyset}!ksDup@{ksDup}}
+\index{ksDup@{ksDup}!keyset@{keyset}}
+\subsubsection[ksDup]{\setlength{\rightskip}{0pt plus 5cm}KeySet$\ast$ ksDup (const KeySet $\ast$ {\em source})}\label{group__keyset_gc59e4b328245463f1451f68d5106151c}
+
+
+Return a duplicate of a keyset.
+
+Objects created with \doxyref{ksDup()}{p.}{group__keyset_gc59e4b328245463f1451f68d5106151c} must be destroyed with \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8}.
+
+Memory will be allocated as needed for dynamic properties, so you need to \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8} the returned pointer.
+
+A flat copy is made, so the keys will not be duplicated, but there reference counter is updated, so both keysets need \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8}.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em source}]has to be an initializised source KeySet \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a flat copy of source on success 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c}, \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8} 
+
+\doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3} for \doxyref{Key :: Basic Methods}{p.}{group__key} duplication \end{Desc}
+\index{keyset@{keyset}!ksGetCursor@{ksGetCursor}}
+\index{ksGetCursor@{ksGetCursor}!keyset@{keyset}}
+\subsubsection[ksGetCursor]{\setlength{\rightskip}{0pt plus 5cm}cursor\_\-t ksGetCursor (const KeySet $\ast$ {\em ks})}\label{group__keyset_gffe507ab9281c322eb16c3e992075d29}
+
+
+Get the KeySet internal cursor.
+
+Use it to get the cursor of the actual position.
+
+\begin{Desc}
+\item[Warning:]Cursors are getting invalid when the key was \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69}ed or \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} with KDB\_\-O\_\-POP was used.\end{Desc}
+\subsection{Read ahead}\label{group__keyset_readahead}
+With the cursors it is possible to read ahead in a keyset:
+
+
+
+\begin{Code}\begin{verbatim}cursor_t jump;
+ksRewind (ks);
+while ((key = keyNext (ks))!=0)
+{
+        // now mark this key
+        jump = ksGetCursor(ks);
+
+        //code..
+        keyNext (ks); // now browse on
+        // use ksCurrent(ks) to check the keys
+        //code..
+
+        // jump back to the position marked before
+        ksSetCursor(ks, jump);
+}
+\end{verbatim}
+\end{Code}
+
+\subsection{Restoring state}\label{group__keyset_restore}
+It can also be used to restore the state of a keyset in a function
+
+
+
+\begin{Code}\begin{verbatim}int f (KeySet *ks)
+{
+        cursor_t state = ksGetCursor(ks);
+
+        // work with keyset
+
+        // now bring the keyset to the state before
+        ksSetCursor (ks, state);
+}
+\end{verbatim}
+\end{Code}
+
+
+
+It is of course possible to make the KeySet const and cast its const away to set the cursor. Another way to achieve the same is to \doxyref{ksDup()}{p.}{group__keyset_gc59e4b328245463f1451f68d5106151c} the keyset, but it is not as efficient.
+
+An invalid cursor will be returned directly after \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}. When you set an invalid cursor \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} is 0 and \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} == \doxyref{ksHead()}{p.}{group__keyset_ge7dbf3aef70e67b5328475eb3d1f92f5}.
+
+\begin{Desc}
+\item[Note:]Only use a cursor for the same keyset which it was made for.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a valid cursor on success 
+
+an invalid cursor on NULL pointer or after \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70} \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9}, \doxyref{ksSetCursor()}{p.}{group__keyset_gd94c9ffaa3e8034564c0712fd407c345} \end{Desc}
+\index{keyset@{keyset}!ksGetSize@{ksGetSize}}
+\index{ksGetSize@{ksGetSize}!keyset@{keyset}}
+\subsubsection[ksGetSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t ksGetSize (const KeySet $\ast$ {\em ks})}\label{group__keyset_g7474ad6b0a0fa969dbdf267ba5770eee}
+
+
+Return the number of keys that {\tt ks} contains.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of keys that {\tt ks} contains. 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]ksNew(0), \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8} \end{Desc}
+\index{keyset@{keyset}!ksHead@{ksHead}}
+\index{ksHead@{ksHead}!keyset@{keyset}}
+\subsubsection[ksHead]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksHead (const KeySet $\ast$ {\em ks})}\label{group__keyset_ge7dbf3aef70e67b5328475eb3d1f92f5}
+
+
+Return the first key in the KeySet.
+
+The KeySets cursor will not be effected.
+
+If \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}==ksHead() you know you are on the first key.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the first Key of a keyset 
+
+0 on NULL pointer or empty keyset \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksTail()}{p.}{group__keyset_gdca442c4ab43cf532b15091d7711559e} for the last \doxyref{Key :: Basic Methods}{p.}{group__key} 
+
+\doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}, \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} and \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} for iterating over the \doxyref{KeySet :: Class Methods}{p.}{group__keyset} \end{Desc}
+\index{keyset@{keyset}!ksLookup@{ksLookup}}
+\index{ksLookup@{ksLookup}!keyset@{keyset}}
+\subsubsection[ksLookup]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksLookup (KeySet $\ast$ {\em ks}, \/  Key $\ast$ {\em key}, \/  option\_\-t {\em options})}\label{group__keyset_ga34fc43a081e6b01e4120daa6c112004}
+
+
+Look for a Key contained in {\tt ks} that matches the name of the {\tt key}.\subsection{Introduction}\label{group__keyset_Introduction}
+{\tt \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004}} is designed to let you work with entirely pre-loaded KeySets, so instead of \doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f}, key by key, the idea is to fully \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} for your application root key and process it all at once with {\tt \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004}}.
+
+This function is very efficient by using binary search. Together with \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} which can you load the whole configuration with only some communication to backends you can write very effective but short code for configuration.\subsection{Usage}\label{group__keyset_Usage}
+If found, {\tt ks} internal cursor will be positioned in the matched key (also accessible by \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}), and a pointer to the Key is returned. If not found, {\tt ks} internal cursor will not move, and a NULL pointer is returned.
+
+Cascading is done if the first character is a /. This leads to ignoring the prefix like system/ and user/. 
+
+\begin{Code}\begin{verbatim}        if (kdbGet(handle, "user/myapp", myConfig, 0 ) == -1)
+                ErrorHandler ("Could not get Keys");
+
+        if (kdbGet(handle, "system/myapp", myConfig, 0 ) == -1)
+                ErrorHandler ("Could not get Keys");
+
+        if ((myKey = ksLookup(myConfig, key, 0)) == NULL)
+                ErrorHandler ("Could not Lookup Key");
+\end{verbatim}
+\end{Code}
+
+
+
+This is the way multi user Programs should get there configuration and search after the values. It is guaranteed that more namespaces can be added easily and that all values can be set by admin and user.\subsubsection{KDB\_\-O\_\-NOALL}\label{group__keyset_KDB_O_NOALL}
+When KDB\_\-O\_\-NOALL is set the keyset will be only searched from \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} to \doxyref{ksTail()}{p.}{group__keyset_gdca442c4ab43cf532b15091d7711559e}. You need to \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70} the keyset yourself. \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} is always set properly after searching a key, so you can go on searching another key after the found key.
+
+When KDB\_\-O\_\-NOALL is not set the cursor will stay untouched and all keys are considered. A much more efficient binary search will be used then.
+
+So if you change keys, e.g. rename (\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}) or remove (\doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984}) them make sure to sort the keyset with \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}. When the keyset is dirty, see ksNeedSort() it will be sorted automatically when needed.\subsubsection{KDB\_\-O\_\-POP}\label{group__keyset_KDB_O_POP}
+When KDB\_\-O\_\-POP is set the key which was found will be \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69}ed. \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} will not be changed, only iff \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} is the searched key, then the keyset will be \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}ed.
+
+\begin{Desc}
+\item[Note:]Note that \doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984} keys won't be found after the first time the keyset is resorted. Lookup automatically sorts the keyset, if needed, but it can't find it out when only keys are changed, not the keyset.
+
+Like in \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69} the popped key always needs to be \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} afterwards, even if it is appended to another keyset.\end{Desc}
+\begin{Desc}
+\item[Warning:]All cursors on the keyset will be invalid iff you use KDB\_\-O\_\-POP, so don't use this if you rely on a cursor, see \doxyref{ksGetCursor()}{p.}{group__keyset_gffe507ab9281c322eb16c3e992075d29}.\end{Desc}
+\begin{Desc}
+\item[Note:]Never use \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} with KDB\_\-O\_\-POP and \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45} or \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7} together in a loop. Otherwise \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} will need to resort the keyset every iteration and spend 99.96\% of the time in \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} (benchmarked with above 500k iterations).\end{Desc}
+You can solve this problem by using KDB\_\-O\_\-NOALL, risking you have to iterate n$^\wedge$2 instead of n.
+
+The more elegant way is to separate the keyset you use for \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} and \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}: 
+
+\begin{Code}\begin{verbatim}int f(KeySet *iterator, KeySet *lookup)
+{
+        KeySet *append = ksNew (ksGetSize(lookup), KS_END);
+        Key *key;
+        Key *current;
+
+        ksRewind(iterator);
+        while (current=ksNext(iterator))
+        {
+                key = ksLookup (lookup, current, KDB_O_POP);
+                // do something...
+                ksAppendKey(append, key); // now append it to append, not lookup!
+                keyDel (key); // make sure to ALWAYS delete poped keys.
+        }
+        ksAppend(lookup, append);
+        // now lookup needs to be sorted only once, append never
+        ksDel (append);
+}
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]where to look for \item[{\em key}]the key object you are looking for \item[{\em options}]some {\tt KDB\_\-O\_\-$\ast$} option bits:\begin{itemize}
+\item {\tt KDB\_\-O\_\-NOCASE} \par
+ Lookup ignoring case.\item {\tt KDB\_\-O\_\-WITHOWNER} \par
+ Also consider correct owner.\item {\tt KDB\_\-O\_\-NOALL} \par
+ Only search from \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} to end of keyset, see above text.\item {\tt KDB\_\-O\_\-POP} \par
+ Pop the key which was found.\item {\tt KDB\_\-O\_\-SORT} \par
+ Force sorting before searching, see \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}. Together with KDB\_\-O\_\-NOALL the search will start from beginning. \end{itemize}
+\end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]pointer to the Key found, 0 otherwise 
+
+0 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksLookupByName()}{p.}{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1} to search by a name given by a string 
+
+\doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}, \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}, \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} for iterating over a \doxyref{KeySet :: Class Methods}{p.}{group__keyset} 
+
+\doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} to understand how \doxyref{KeySet :: Class Methods}{p.}{group__keyset} sort themself \end{Desc}
+\index{keyset@{keyset}!ksLookupByName@{ksLookupByName}}
+\index{ksLookupByName@{ksLookupByName}!keyset@{keyset}}
+\subsubsection[ksLookupByName]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksLookupByName (KeySet $\ast$ {\em ks}, \/  const char $\ast$ {\em name}, \/  option\_\-t {\em options})}\label{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1}
+
+
+Look for a Key contained in {\tt ks} that matches {\tt name}.
+
+{\tt \doxyref{ksLookupByName()}{p.}{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1}} is designed to let you work with entirely pre-loaded KeySets, so instead of \doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f}, key by key, the idea is to fully \doxyref{kdbGetByName()}{p.}{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2} for your application root key and process it all at once with {\tt \doxyref{ksLookupByName()}{p.}{group__keyset_gd2e30fb6d4739d917c5abb2ac2f9c1a1}}.
+
+This function is very efficient by using binary search. Together with \doxyref{kdbGetByName()}{p.}{group__kdbhighlevel_g3013d5768bbcf3c34652f489151940e2} which can you load the whole configuration with only some communication to backends you can write very effective but short code for configuration.
+
+If found, {\tt ks} internal cursor will be positioned in the matched key (also accessible by \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}), and a pointer to the Key is returned. If not found, {\tt ks} internal cursor will not move, and a NULL pointer is returned.\subsection{Cascading}\label{group__keyset_cascading}
+Cascading is done if the first character is a /. This leads to ignoring the prefix like system/ and user/. 
+
+\begin{Code}\begin{verbatim}        if (kdbGetByName(handle, "/sw/myapp/current", myConfig, 0 ) == -1)
+                ErrorHandler ("Could not get Keys");
+
+        if ((myKey = ksLookupByName (myConfig, "/myapp/current/key", 0)) == NULL)
+                ErrorHandler ("Could not Lookup Key");
+\end{verbatim}
+\end{Code}
+
+
+
+This is the way multi user Programs should get there configuration and search after the values. It is guaranteed that more namespaces can be added easily and that all values can be set by admin and user.\subsection{Full Search}\label{group__keyset_fullsearch}
+When KDB\_\-O\_\-NOALL is set the keyset will be only searched from \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} to \doxyref{ksTail()}{p.}{group__keyset_gdca442c4ab43cf532b15091d7711559e}. You need to \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70} the keyset yourself. \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} is always set properly after searching a key, so you can go on searching another key after the found key.
+
+When KDB\_\-O\_\-NOALL is not set the cursor will stay untouched and all keys are considered. A much more efficient binary search will be used then.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]where to look for \item[{\em name}]key name you are looking for \item[{\em options}]some {\tt KDB\_\-O\_\-$\ast$} option bits:\begin{itemize}
+\item {\tt KDB\_\-O\_\-NOCASE} \par
+ Lookup ignoring case.\item {\tt KDB\_\-O\_\-WITHOWNER} \par
+ Also consider correct owner.\item {\tt KDB\_\-O\_\-NOALL} \par
+ Only search from \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} to end of keyset, see above text.\item {\tt KDB\_\-O\_\-POP} \par
+ Pop the key which was found.\item {\tt KDB\_\-O\_\-SORT} \par
+ Force sorting before searching, see \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c}. Together with KDB\_\-O\_\-NOALL the search will start from beginning.\end{itemize}
+\end{description}
+\end{Desc}
+Currently no options supported. \begin{Desc}
+\item[Returns:]pointer to the Key found, 0 otherwise 
+
+0 on NULL pointers \end{Desc}
+\begin{Desc}
+\item[See also:]keyCompare() for very powerfull Key lookups in KeySets 
+
+\doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}, \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}, \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} \end{Desc}
+\index{keyset@{keyset}!ksNew@{ksNew}}
+\index{ksNew@{ksNew}!keyset@{keyset}}
+\subsubsection[ksNew]{\setlength{\rightskip}{0pt plus 5cm}KeySet$\ast$ ksNew (size\_\-t {\em alloc}, \/   {\em ...})}\label{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c}
+
+
+Allocate, initialize and return a new KeySet object.
+
+Objects created with \doxyref{ksNew()}{p.}{group__keyset_g671e1aaee3ae9dc13b4834a4ddbd2c3c} must be destroyed with \doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8}.
+
+You can use a various long list of parameters to preload the keyset with a list of keys. Either your first and only parameter is 0 or your last parameter must be KEY\_\-END.
+
+For most uses 
+
+\begin{Code}\begin{verbatim}KeySet *keys = ksNew(0);
+// work with it
+ksDel (keys);
+\end{verbatim}
+\end{Code}
+
+ goes ok, the alloc size will be 16, defined in kdbprivate.h. The alloc size will be doubled whenever size reaches alloc size, so it also performs out large keysets.
+
+But if you have any clue how large your keyset may be you should read the next statements.
+
+If you want a keyset with length 15 (because you know of your application that you normally need about 12 up to 14 keys), use: 
+
+\begin{Code}\begin{verbatim}KeySet * keys = ksNew (15, KS_END);
+// work with it
+ksDel (keys);
+\end{verbatim}
+\end{Code}
+
+
+
+If you start having 3 keys, and your application needs approximately 200-500 keys, you can use: 
+
+\begin{Code}\begin{verbatim}KeySet * config = ksNew (500,
+        keyNew ("user/sw/app/fixedConfiguration/key1", KEY_SWITCH_VALUE, "value1", 0),
+        keyNew ("user/sw/app/fixedConfiguration/key2", KEY_SWITCH_VALUE, "value2", 0),
+        keyNew ("user/sw/app/fixedConfiguration/key3", KEY_SWITCH_VALUE, "value3", 0),
+        KS_END); // don't forget the KS_END at the end!
+// work with it
+ksDel (config);
+\end{verbatim}
+\end{Code}
+
+ Alloc size is 500, the size of the keyset will be 3 after ksNew. This means the keyset will reallocate when appending more than 497 keys.
+
+The main benefit of taking a list of variant length parameters is to be able to have one C-Statement for any possible KeySet.
+
+Due to ABI compatibility, the {\tt KeySet} structure is only declared in kdb.h, and not defined. So you can only declare {\tt pointers} to {\tt KeySets} in your program. See {\tt http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html\#AEN135}
+
+\begin{Desc}
+\item[See also:]\doxyref{ksDel()}{p.}{group__keyset_g27e5c16473b02a422238c8d970db7ac8} to free the \doxyref{KeySet :: Class Methods}{p.}{group__keyset} afterwards 
+
+\doxyref{ksDup()}{p.}{group__keyset_gc59e4b328245463f1451f68d5106151c} to duplicate an existing \doxyref{KeySet :: Class Methods}{p.}{group__keyset} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em alloc}]gives a hint for the size how many Keys may be stored initially \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a ready to use KeySet object 
+
+0 on memory error \end{Desc}
+\index{keyset@{keyset}!ksNext@{ksNext}}
+\index{ksNext@{ksNext}!keyset@{keyset}}
+\subsubsection[ksNext]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksNext (KeySet $\ast$ {\em ks})}\label{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9}
+
+
+Returns the next Key in a KeySet.
+
+KeySets have an internal cursor that can be reset with \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}. Every time \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} is called the cursor is incremented and the new current Key is returned.
+
+You'll get a NULL pointer if the key after the end of the KeySet was reached. It will set the cursor to the beginning of the KeySet and the next time the first key is returned.
+
+The {\tt ks} internal cursor will be changed, so it is not const.
+
+\begin{Desc}
+\item[Note:]You must not delete or change the key, use \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69} if you want to delete it.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the new current Key 
+
+0 when the end is reached 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}, \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} \end{Desc}
+\index{keyset@{keyset}!ksPop@{ksPop}}
+\index{ksPop@{ksPop}!keyset@{keyset}}
+\subsubsection[ksPop]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksPop (KeySet $\ast$ {\em ks})}\label{group__keyset_ge42530b04defb772059de0600159cf69}
+
+
+Remove and return the last key of {\tt ks}.
+
+The reference counter will be decremented by one.
+
+The KeySets cursor will not be effected if it did not point to the popped key.
+
+\begin{Desc}
+\item[Note:]You need to \doxyref{keyDel()}{p.}{group__key_g3df95bbc2494e3e6703ece5639be5bb1} the key afterwards, if you don't append it to another keyset. It has the same semantics like a key allocated with \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} or \doxyref{keyDup()}{p.}{group__key_ge6ec6a60cc4b8c1463fa08623d056ce3}.\end{Desc}
+
+
+\begin{Code}\begin{verbatim}ks1=ksNew(0);
+ks2=ksNew(0);
+
+k1=keyNew(0); // ref counter 0
+ksAppendKey(ks1, k1); // ref counter 1
+ksAppendKey(ks2, k1); // ref counter 2
+
+k1=ksPop (ks1); // ref counter 1
+k1=ksPop (ks2); // ref counter 0, like after keyNew()
+
+ksAppendKey(ks1, k1); // ref counter 1
+
+ksDel (ks1); // key is deleted too
+ksDel (ks2);
+ *
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Returns:]the last key of {\tt ks} 
+
+NULL if {\tt ks} is empty or on NULL pointer \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]KeySet to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}, \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7} 
+
+commandList() for an example \end{Desc}
+\index{keyset@{keyset}!ksRewind@{ksRewind}}
+\index{ksRewind@{ksRewind}!keyset@{keyset}}
+\subsubsection[ksRewind]{\setlength{\rightskip}{0pt plus 5cm}int ksRewind (KeySet $\ast$ {\em ks})}\label{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}
+
+
+Rewinds the KeySet internal cursor.
+
+Use it to set the cursor to the beginning of the KeySet. \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} will then always return NULL afterwards. So you want to \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} first.
+
+
+
+\begin{Code}\begin{verbatim}ksRewind (ks);
+while ((key = keyNext (ks))!=0) {}
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 on success 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9}, \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} \end{Desc}
+\index{keyset@{keyset}!ksSetCursor@{ksSetCursor}}
+\index{ksSetCursor@{ksSetCursor}!keyset@{keyset}}
+\subsubsection[ksSetCursor]{\setlength{\rightskip}{0pt plus 5cm}int ksSetCursor (KeySet $\ast$ {\em ks}, \/  cursor\_\-t {\em cursor})}\label{group__keyset_gd94c9ffaa3e8034564c0712fd407c345}
+
+
+Set the KeySet internal cursor.
+
+Use it to set the cursor to a stored position. \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} will then be the position which you got with.
+
+\begin{Desc}
+\item[Warning:]Cursors may get invalid when the key was \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69}ed or \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} was used together with KDB\_\-O\_\-POP.\end{Desc}
+
+
+\begin{Code}\begin{verbatim}cursor_t cursor;
+..
+// key now in any position here
+cursor = ksGetCursor (ks);
+while ((key = keyNext (ks))!=0) {}
+ksSetCursor (ks, cursor); // reset state
+ksCurrent(ks); // in same position as before
+\end{verbatim}
+\end{Code}
+
+
+
+An invalid cursor will set the keyset to its beginning like \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}. When you set an invalid cursor \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} is 0 and \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} == \doxyref{ksHead()}{p.}{group__keyset_ge7dbf3aef70e67b5328475eb3d1f92f5}.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em cursor}]the cursor to use \item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]0 when the keyset is \doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}ed 
+
+1 otherwise 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9}, \doxyref{ksGetCursor()}{p.}{group__keyset_gffe507ab9281c322eb16c3e992075d29} \end{Desc}
+\index{keyset@{keyset}!ksSort@{ksSort}}
+\index{ksSort@{ksSort}!keyset@{keyset}}
+\subsubsection[ksSort]{\setlength{\rightskip}{0pt plus 5cm}void ksSort (KeySet $\ast$ {\em ks})}\label{group__keyset_g023554d395ccf2319a3807b8b5d2530c}
+
+
+Sorts a KeySet alphabetically by Key name.
+
+You need \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} only in few cases directly.\subsection{Don't need to sort before using ksLookup}\label{group__keyset_sortlookup}
+You don't need it if you just just \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and subsequent \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004}. 
+
+\begin{Code}\begin{verbatim}KeySet *ks = ksNew(0);
+kdbGet(h, ks, k, 0);
+// ksPop(), ksAppend() and ksAppendKey() allowed here
+ksLookup(ks, s, 0); // you dont need to sort ks
+\end{verbatim}
+\end{Code}
+
+
+
+This is because the KeySet tracks if it needs to be sorted and \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} will sort when needed.\subsection{Sort when iterating}\label{group__keyset_sortiterate}
+Before you iterate over a keyset you have to sort it, if you need it in a sorted way.
+
+To achieve that you can pass option\_\-t::KDB\_\-O\_\-SORT to \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}. Then you will receive a already sorted keyset over which you can iterate. 
+
+\begin{Code}\begin{verbatim}KeySet *ks = ksNew(0);
+kdbGet(h, ks, k, KDB_O_SORT);
+// no changes to keyset allowed
+ksRewind(ks);
+// now you can iterate over a sorted keyset
+\end{verbatim}
+\end{Code}
+
+
+
+Its of course also possible to use \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} once, because it will sort the keyset too.\subsection{Sort when changing key}\label{group__keyset_sortkey}
+\begin{Desc}
+\item[Warning:]You should not use \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9} or \doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984} when a key belongs to a keyset. When you are doing this, you always need to {\tt manually} sort {\tt all} keysets where the key was before using \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} (otherwise \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} won't find that keys), \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} or \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} methods.\end{Desc}
+When you change a key's name or its remove status the order which was previously correctly is then wrong. The keyset does not recognize this, so the programmer has to take care that \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} is called before any operation which needs a sorted keyset (which are all \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004}, all \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and all \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} functions).
+
+\begin{Desc}
+\item[Note:]You can remember that easily that all functions which get options require one of the following:\begin{itemize}
+\item that you did not manipulate a keys name or a remove status\item that you pass KDB\_\-O\_\-SORT when you know that you manipulated at least one key\item that you \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} yourself after manipulating keys\end{itemize}
+\end{Desc}
+\subsection{Dirty KeySet}\label{group__keyset_dirty}
+When you use \doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7}, \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}, \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69} the keyset is dirty afterwards, which means that it needs to be sorted. This is done automatically using a \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} method and in ksGet() or ksSet() (All methods which accept options).
+
+It won't be done if you just iterate over the keyset, so you might use a \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} or \doxyref{ksSort()}{p.}{group__keyset_g023554d395ccf2319a3807b8b5d2530c} first. \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} will be more efficient in that case, because it will only sort when needed. Don't pass KDB\_\-O\_\-NOALL (it will deactivate the sorting feature), see \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} for more information.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]KeySet to be sorted \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}, \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}, \doxyref{ksLookup()}{p.}{group__keyset_ga34fc43a081e6b01e4120daa6c112004} for some functions which may need sorting before they are called. (All functions which take options as arguments) 
+
+\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keySetBaseName()}{p.}{group__keyname_g6e804bd453f98c28b0ff51430d1df407}, \doxyref{keyAddBaseName()}{p.}{group__keyname_ga942091fc4bd5c2699e49ddc50829524} and \doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984} for all methods which change the sorting state where the \doxyref{KeySet :: Class Methods}{p.}{group__keyset} can't track the change. 
+
+\doxyref{ksAppend()}{p.}{group__keyset_g21eb9c3a14a604ee3a8bdc779232e7b7}, \doxyref{ksAppendKey()}{p.}{group__keyset_ga5a1d467a4d71041edce68ea7748ce45}, \doxyref{ksPop()}{p.}{group__keyset_ge42530b04defb772059de0600159cf69} for all methods which make a \doxyref{KeySet :: Class Methods}{p.}{group__keyset} dirty. \end{Desc}
+\index{keyset@{keyset}!ksTail@{ksTail}}
+\index{ksTail@{ksTail}!keyset@{keyset}}
+\subsubsection[ksTail]{\setlength{\rightskip}{0pt plus 5cm}Key$\ast$ ksTail (const KeySet $\ast$ {\em ks})}\label{group__keyset_gdca442c4ab43cf532b15091d7711559e}
+
+
+Return the last key in the KeySet.
+
+The KeySets cursor will not be effected.
+
+If \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094}==ksTail() you know you are on the last key. \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} will return a NULL pointer afterwards.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the last Key of a keyset 
+
+0 on NULL pointer or empty keyset \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksHead()}{p.}{group__keyset_ge7dbf3aef70e67b5328475eb3d1f92f5} for the first \doxyref{Key :: Basic Methods}{p.}{group__key} 
+
+\doxyref{ksRewind()}{p.}{group__keyset_gbe793ff51f1728e3429c84a8a9086b70}, \doxyref{ksCurrent()}{p.}{group__keyset_g4287b9416912c5f2ab9c195cb74fb094} and \doxyref{ksNext()}{p.}{group__keyset_g317321c9065b5a4b3e33fe1c399bcec9} for iterating over the \doxyref{KeySet :: Class Methods}{p.}{group__keyset} \end{Desc}
diff --git a/doc/elektra-api/latex/group__keytest.tex b/doc/elektra-api/latex/group__keytest.tex
new file mode 100644 (file)
index 0000000..f8ed551
--- /dev/null
@@ -0,0 +1,324 @@
+\section{Key :: Methods for Making Tests}
+\label{group__keytest}\index{Key :: Methods for Making Tests@{Key :: Methods for Making Tests}}
+Methods to do various tests on Keys.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf keyNeedStat} (const Key $\ast$key)
+\item 
+int {\bf keyNeedSync} (const Key $\ast$key)
+\item 
+int {\bf keyNeedRemove} (const Key $\ast$key)
+\item 
+int {\bf keyIsSystem} (const Key $\ast$key)
+\item 
+int {\bf keyIsUser} (const Key $\ast$key)
+\item 
+int {\bf keyIsBelow} (const Key $\ast$key, const Key $\ast$check)
+\item 
+int {\bf keyIsDirectBelow} (const Key $\ast$key, const Key $\ast$check)
+\item 
+int {\bf keyIsInactive} (const Key $\ast$key)
+\item 
+int {\bf keyIsDir} (const Key $\ast$key)
+\item 
+int {\bf keyIsBinary} (const Key $\ast$key)
+\item 
+int {\bf keyIsString} (const Key $\ast$key)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to do various tests on Keys. 
+
+To use them: 
+
+\begin{Code}\begin{verbatim}#include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+\subsection{Function Documentation}
+\index{keytest@{keytest}!keyIsBelow@{keyIsBelow}}
+\index{keyIsBelow@{keyIsBelow}!keytest@{keytest}}
+\subsubsection[keyIsBelow]{\setlength{\rightskip}{0pt plus 5cm}int keyIsBelow (const Key $\ast$ {\em key}, \/  const Key $\ast$ {\em check})}\label{group__keytest_g03332b5d97c76a4fd2640aca4762b8df}
+
+
+Check if the key check is below the key key or not.
+
+
+
+\begin{Code}\begin{verbatim}Example:
+key user/sw/app
+check user/sw/app/key
+
+returns true because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+returns also true because check is indirect below key
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em check}]the key to find the relative position of \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if check is below key 
+
+0 if it is not below or if it is the same key \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be}, \doxyref{keyIsDirectBelow()}{p.}{group__keytest_g4f175aafd98948ce6c774f3bd92b72ca} \end{Desc}
+\index{keytest@{keytest}!keyIsBinary@{keyIsBinary}}
+\index{keyIsBinary@{keyIsBinary}!keytest@{keytest}}
+\subsubsection[keyIsBinary]{\setlength{\rightskip}{0pt plus 5cm}int keyIsBinary (const Key $\ast$ {\em key})}\label{group__keytest_g9526b371087564e43e3dff8ad0dac949}
+
+
+Check if a key is binary type.
+
+The function checks if the keytype is in the range between KEY\_\-TYPE\_\-BINARY and less than excluding KEY\_\-TYPE\_\-STRING. Then it will be interpreted as binary.
+
+Make sure to use this function and don't test the binary type another way to ensure compatibility and to write less error prone programs.
+
+\begin{Desc}
+\item[Returns:]1 if it is binary 
+
+0 if it is not 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetType()}{p.}{group__keymeta_gb92003db4b938594df48807c16766bf7} for more information on types 
+
+\doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84}, \doxyref{keySetBinary()}{p.}{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key to check \end{description}
+\end{Desc}
+\index{keytest@{keytest}!keyIsDir@{keyIsDir}}
+\index{keyIsDir@{keyIsDir}!keytest@{keytest}}
+\subsubsection[keyIsDir]{\setlength{\rightskip}{0pt plus 5cm}int keyIsDir (const Key $\ast$ {\em key})}\label{group__keytest_gc0a10c602d52a35f81347e8a32312017}
+
+
+Check if a key is directory key.
+
+Folder keys may also have value and comment. They are discern by having a executable bit set.
+
+If any executable bit is set it will be recognized as a directory.
+
+\begin{Desc}
+\item[Note:]keyIsDir may return true even though you can't access the directory.\end{Desc}
+To know if you can access the directory, you need to check, if your\begin{itemize}
+\item user ID is equal the key's user ID and the mode \& 100 is true\item group ID is equal the key's group ID and the mode \& 010 is true\item mode \& 001 is true\end{itemize}
+
+
+Accessing does not mean that you can get any value or comments below, see \doxyref{Mode}{p.}{group__backend_mode} for more information.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if key is a directory, 0 otherwise 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetDir()}{p.}{group__keymeta_gae575bd86a628a15ee45baa860522e75}, \doxyref{keySetMode()}{p.}{group__keymeta_g8803037e35b9da1ce492323a88ff6bc3} \end{Desc}
+\index{keytest@{keytest}!keyIsDirectBelow@{keyIsDirectBelow}}
+\index{keyIsDirectBelow@{keyIsDirectBelow}!keytest@{keytest}}
+\subsubsection[keyIsDirectBelow]{\setlength{\rightskip}{0pt plus 5cm}int keyIsDirectBelow (const Key $\ast$ {\em key}, \/  const Key $\ast$ {\em check})}\label{group__keytest_g4f175aafd98948ce6c774f3bd92b72ca}
+
+
+Check if the key check is direct below the key key or not.
+
+
+
+\begin{Code}\begin{verbatim}Example:
+key user/sw/app
+check user/sw/app/key
+
+returns true because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+does not return true, because there is only a indirect relation
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em check}]the key to find the relative position of \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if check is below key 
+
+0 if it is not below or if it is the same key 
+
+-1 on null pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyIsBelow()}{p.}{group__keytest_g03332b5d97c76a4fd2640aca4762b8df}, \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keyGetName()}{p.}{group__keyname_gb29a850168d9b31c9529e90cf9ab68be} \end{Desc}
+\index{keytest@{keytest}!keyIsInactive@{keyIsInactive}}
+\index{keyIsInactive@{keyIsInactive}!keytest@{keytest}}
+\subsubsection[keyIsInactive]{\setlength{\rightskip}{0pt plus 5cm}int keyIsInactive (const Key $\ast$ {\em key})}\label{group__keytest_ga25f699f592031c1a0abc1504d14e13e}
+
+
+Check whether a key is inactive or not.
+
+In elektra terminology any key is inactive if the it's basename starts with '.'. Inactive keys must not have any meaning to applications, they are reserved for users and administrators.
+
+To remove a whole hierarchy in elektra, don't forget to pass option\_\-t::KDB\_\-O\_\-INACTIVE to \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} to receive the inactive keys in order to remove them.
+
+Otherwise you should not fetch these keys.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if the key is inactive, 0 otherwise 
+
+-1 on NULL pointer or when key has no name \end{Desc}
+\index{keytest@{keytest}!keyIsString@{keyIsString}}
+\index{keyIsString@{keyIsString}!keytest@{keytest}}
+\subsubsection[keyIsString]{\setlength{\rightskip}{0pt plus 5cm}int keyIsString (const Key $\ast$ {\em key})}\label{group__keytest_gea7670778abd07fee0fe8ac12a149190}
+
+
+Check if a key is string type.
+
+The function checks if the keytype is larger or equal KEY\_\-TYPE\_\-STRING. Then it will be considered as string type.
+
+Make sure to use this function and don't test the string type another way to ensure compatibility and to write less error prone programs.
+
+\begin{Desc}
+\item[Returns:]1 if it is string 
+
+0 if it is not 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keySetType}{p.}{group__keymeta_gb92003db4b938594df48807c16766bf7} for more information on types 
+
+\doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778}, \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key to check \end{description}
+\end{Desc}
+\index{keytest@{keytest}!keyIsSystem@{keyIsSystem}}
+\index{keyIsSystem@{keyIsSystem}!keytest@{keytest}}
+\subsubsection[keyIsSystem]{\setlength{\rightskip}{0pt plus 5cm}int keyIsSystem (const Key $\ast$ {\em key})}\label{group__keytest_gfe49cfb61c2accb3073131c23a56fb14}
+
+
+Check whether a key is under the {\tt system} namespace or not
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if key name begins with {\tt system}, 0 otherwise 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyIsUser()}{p.}{group__keytest_g373acc20c6209357045891f4b0c70041}, \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} \end{Desc}
+\index{keytest@{keytest}!keyIsUser@{keyIsUser}}
+\index{keyIsUser@{keyIsUser}!keytest@{keytest}}
+\subsubsection[keyIsUser]{\setlength{\rightskip}{0pt plus 5cm}int keyIsUser (const Key $\ast$ {\em key})}\label{group__keytest_g373acc20c6209357045891f4b0c70041}
+
+
+Check whether a key is under the {\tt user} namespace or not.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if key name begins with {\tt user}, 0 otherwise 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyIsSystem()}{p.}{group__keytest_gfe49cfb61c2accb3073131c23a56fb14}, \doxyref{keySetName()}{p.}{group__keyname_g7699091610e7f3f43d2949514a4b35d9}, \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513} \end{Desc}
+\index{keytest@{keytest}!keyNeedRemove@{keyNeedRemove}}
+\index{keyNeedRemove@{keyNeedRemove}!keytest@{keytest}}
+\subsubsection[keyNeedRemove]{\setlength{\rightskip}{0pt plus 5cm}int keyNeedRemove (const Key $\ast$ {\em key})}\label{group__keytest_gae91159815480fbb3b3d9d7fa85e77b9}
+
+
+Ask if key is marked for permanent remove.
+
+Ask if the key will be removed instead of writing in the key database when doing \doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20} or \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}.
+
+\begin{Desc}
+\item[See also:]\doxyref{keyRemove()}{p.}{group__keymeta_g6e14e5f1de26e1318100631a149f2984} 
+
+\doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}, \doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20}, \doxyref{kdbRemove()}{p.}{group__kdbhighlevel_gf9adbbeb3f49c63fb2f89930445c8060} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if it is marked, 0 otherwise 
+
+-1 on NULL pointer \end{Desc}
+\index{keytest@{keytest}!keyNeedStat@{keyNeedStat}}
+\index{keyNeedStat@{keyNeedStat}!keytest@{keytest}}
+\subsubsection[keyNeedStat]{\setlength{\rightskip}{0pt plus 5cm}int keyNeedStat (const Key $\ast$ {\em key})}\label{group__keytest_g3908b6511648a950f37cd0005bfea5d5}
+
+
+Ask if key is marked for stat only.
+
+Ask if the key will be stat instead of get it from the key database completely doing \doxyref{kdbGetKey()}{p.}{group__kdbhighlevel_ga62877888f0cad395898859395e6635f} or \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5}. This is useful if you are not interested in the value, comment or key type.
+
+\begin{Desc}
+\item[See also:]\doxyref{keyStat()}{p.}{group__keymeta_gb8189add5e562bdb148675ee595bd95b}, \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if it is marked, 0 otherwise 
+
+-1 on NULL pointer \end{Desc}
+\index{keytest@{keytest}!keyNeedSync@{keyNeedSync}}
+\index{keyNeedSync@{keyNeedSync}!keytest@{keytest}}
+\subsubsection[keyNeedSync]{\setlength{\rightskip}{0pt plus 5cm}int keyNeedSync (const Key $\ast$ {\em key})}\label{group__keytest_gf247df0de7aca04b32ef80e39ef12950}
+
+
+Test if a key needs to be synced to backend storage.
+
+If any key modification took place the key will be flagged with KEY\_\-FLAG\_\-SYNC so that \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} knows which keys were modified and which not.
+
+After \doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} the flag will normally be set, but after \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571} the flag will be removed. When you modify the key the flag will be set again.
+
+In your application you can make use of that flag to know if you changed something in a key after a \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} or \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}.
+
+\begin{Desc}
+\item[Note:]Note that also changes in the meta data will set that flag.\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyNew()}{p.}{group__key_gf6893c038b3ebee90c73a9ea8356bebf} \end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 if {\tt key} was changed in memory, 0 otherwise 
+
+-1 on NULL pointer \end{Desc}
diff --git a/doc/elektra-api/latex/group__keyvalue.tex b/doc/elektra-api/latex/group__keyvalue.tex
new file mode 100644 (file)
index 0000000..c4d6d21
--- /dev/null
@@ -0,0 +1,456 @@
+\section{Key :: Value Manipulation Methods}
+\label{group__keyvalue}\index{Key :: Value Manipulation Methods@{Key :: Value Manipulation Methods}}
+Methods to do various operations on Key values.  
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+const void $\ast$ {\bf keyValue} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetValueSize} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetString} (const Key $\ast$key, char $\ast$returnedString, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf keySetString} (Key $\ast$key, const char $\ast$newStringValue)
+\item 
+ssize\_\-t {\bf keyGetBinary} (const Key $\ast$key, void $\ast$returnedBinary, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf keySetBinary} (Key $\ast$key, const void $\ast$newBinary, size\_\-t dataSize)
+\item 
+const char $\ast$ {\bf keyComment} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetCommentSize} (const Key $\ast$key)
+\item 
+ssize\_\-t {\bf keyGetComment} (const Key $\ast$key, char $\ast$returnedComment, size\_\-t maxSize)
+\item 
+ssize\_\-t {\bf keySetComment} (Key $\ast$key, const char $\ast$newComment)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to do various operations on Key values. 
+
+A key can contain a value in different format. The most likely situation is, that the value is interpreted as text. Use \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} for that. You can save any Unicode Symbols and Elektra will take care that you get the same back, independent of your current environment.
+
+In some situations this idea fails. When you need exactly the same value back without any interpretation of the characters, there is \doxyref{keySetBinary()}{p.}{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7}. If you use that, its very likely that your Configuration is not according to the standard. Also for Numbers, Booleans and Date you should use \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778}. To do so, you might use strtod() strtol() and then atol() or atof() to convert back.
+
+To use them: 
+
+\begin{Code}\begin{verbatim}#include <kdb.h>
+\end{verbatim}
+\end{Code}
+
+
+\subsection{Function Documentation}
+\index{keyvalue@{keyvalue}!keyComment@{keyComment}}
+\index{keyComment@{keyComment}!keyvalue@{keyvalue}}
+\subsubsection[keyComment]{\setlength{\rightskip}{0pt plus 5cm}const char$\ast$ keyComment (const Key $\ast$ {\em key})}\label{group__keyvalue_gc89fd319783b3457db45b4c09e55274a}
+
+
+Return a pointer to the real internal {\tt key} comment.
+
+This is a much more efficient version of \doxyref{keyGetComment()}{p.}{group__keyvalue_gfb89735689929ff717cc9f2d0d0b46a2} and you should use it if you are responsible enough to not mess up things. You are not allowed to change anything in the memory region the returned pointer points to.
+
+\doxyref{keyComment()}{p.}{group__keyvalue_gc89fd319783b3457db45b4c09e55274a} returns \char`\"{}\char`\"{} when there is no keyComment. The reason is 
+
+\begin{Code}\begin{verbatim}key=keyNew(0);
+keySetComment(key,"");
+keyComment(key); // you would expect "" here
+keyDel(key);
+\end{verbatim}
+\end{Code}
+
+
+
+See \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1} for more information on comments.
+
+\begin{Desc}
+\item[Note:]Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \doxyref{keyComment()}{p.}{group__keyvalue_gc89fd319783b3457db45b4c09e55274a} method to set a new value. Use \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1} instead.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to the internal managed comment 
+
+\char`\"{}\char`\"{} when there is no comment 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetCommentSize()}{p.}{group__keyvalue_g0dd737fadc16d4cf16720d17f066a9d3} for size and \doxyref{keyGetComment()}{p.}{group__keyvalue_gfb89735689929ff717cc9f2d0d0b46a2} as alternative \end{Desc}
+\index{keyvalue@{keyvalue}!keyGetBinary@{keyGetBinary}}
+\index{keyGetBinary@{keyGetBinary}!keyvalue@{keyvalue}}
+\subsubsection[keyGetBinary]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetBinary (const Key $\ast$ {\em key}, \/  void $\ast$ {\em returnedBinary}, \/  size\_\-t {\em maxSize})}\label{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84}
+
+
+Get the value of a key as a binary.
+
+If the type is not binary -1 will be returned.
+
+When the binary data is empty (this is not the same as \char`\"{}\char`\"{}!) 0 will be returned and the returnedBinary will not be changed.
+
+For string values see \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} and \doxyref{keyIsString()}{p.}{group__keytest_gea7670778abd07fee0fe8ac12a149190}.
+
+When the returnedBinary is to small to hold the data (its maximum size is given by maxSize), the returnedBinary will not be changed and -1 is returned.
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}Key *key = keyNew ("user/keyname", KEY_TYPE, KEY_TYPE_BINARY, KEY_END);
+char buffer[300];
+
+if (keyGetBinary(key,buffer,sizeof(buffer)) == -1)
+{
+        // handle error
+}
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the object to gather the value from \item[{\em returnedBinary}]pre-allocated memory to store a copy of the key value \item[{\em maxSize}]number of bytes of pre-allocated memory in {\tt returnedBinary} \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually copied to {\tt returnedBinary} 
+
+0 if the binary is empty 
+
+-1 on NULL pointers 
+
+-1 when maxSize is 0, too small to hold the value or larger than SSIZE\_\-MAX 
+
+-1 on typing error when the key is not binary \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af}, \doxyref{keyGetValueSize()}{p.}{group__keyvalue_ge326672fffb7474abfe9baf53b73217e}, \doxyref{keySetBinary()}{p.}{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7} 
+
+\doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} and \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} as preferred alternative to binary 
+
+\doxyref{keyIsBinary()}{p.}{group__keytest_g9526b371087564e43e3dff8ad0dac949} to see how to check for binary type \end{Desc}
+\index{keyvalue@{keyvalue}!keyGetComment@{keyGetComment}}
+\index{keyGetComment@{keyGetComment}!keyvalue@{keyvalue}}
+\subsubsection[keyGetComment]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetComment (const Key $\ast$ {\em key}, \/  char $\ast$ {\em returnedComment}, \/  size\_\-t {\em maxSize})}\label{group__keyvalue_gfb89735689929ff717cc9f2d0d0b46a2}
+
+
+Get the key comment.\subsection{Comments}\label{group__keyvalue_comment}
+A Key comment is description for humans what this key is for. It may be a textual explanation of valid values, when and why a user or administrator changed the key or any other text that helps the user or administrator related to that key.
+
+Don't depend on a comment in your program. A user is always allowed to remove or change it in any way he wants to. But you are allowed or even encouraged to always show the content of the comment to the user and allow him to change it.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em returnedComment}]pre-allocated memory to copy the comments to \item[{\em maxSize}]number of bytes that will fit returnedComment \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually copied to {\tt returnedString}, including final NULL 
+
+1 if the string is empty 
+
+-1 on NULL pointer 
+
+-1 if maxSize is 0, not enough to store the comment or when larger then SSIZE\_\-MAX \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetCommentSize()}{p.}{group__keyvalue_g0dd737fadc16d4cf16720d17f066a9d3}, \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1} \end{Desc}
+\index{keyvalue@{keyvalue}!keyGetCommentSize@{keyGetCommentSize}}
+\index{keyGetCommentSize@{keyGetCommentSize}!keyvalue@{keyvalue}}
+\subsubsection[keyGetCommentSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetCommentSize (const Key $\ast$ {\em key})}\label{group__keyvalue_g0dd737fadc16d4cf16720d17f066a9d3}
+
+
+Calculates number of bytes needed to store a key comment, including final NULL.
+
+Use this method to know to size for allocated memory to retrieve a key comment.
+
+See \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1} for more information on comments.
+
+For an empty key name you need one byte to store the ending NULL. For that reason 1 is returned.
+
+
+
+\begin{Code}\begin{verbatim}char *buffer;
+buffer = malloc (keyGetCommentSize (key));
+// use this buffer to store the comment
+// pass keyGetCommentSize (key) for maxSize
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes needed 
+
+1 if there is no comment 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetComment()}{p.}{group__keyvalue_gfb89735689929ff717cc9f2d0d0b46a2}, \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1} \end{Desc}
+\index{keyvalue@{keyvalue}!keyGetString@{keyGetString}}
+\index{keyGetString@{keyGetString}!keyvalue@{keyvalue}}
+\subsubsection[keyGetString]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetString (const Key $\ast$ {\em key}, \/  char $\ast$ {\em returnedString}, \/  size\_\-t {\em maxSize})}\label{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778}
+
+
+Get the value of a key as a string.
+
+When there is no value inside the string, 1 will be returned and the returnedString will be empty \char`\"{}\char`\"{} to avoid programming errors that old strings are shown to the user.
+
+For binary values see \doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84} and \doxyref{keyIsBinary()}{p.}{group__keytest_g9526b371087564e43e3dff8ad0dac949}.
+
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}Key *key = keyNew ("user/keyname", KEY_END);
+char buffer[300];
+
+if (keyGetString(key,buffer,sizeof(buffer)) == -1)
+{
+        // handle error
+} else {
+        printf ("buffer: %s\n", buffer);
+}
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the object to gather the value from \item[{\em returnedString}]pre-allocated memory to store a copy of the key value \item[{\em maxSize}]number of bytes of allocated memory in {\tt returnedString} \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually copied to {\tt returnedString}, including final NULL 
+
+1 if the string is empty 
+
+-1 on NULL pointer 
+
+-1 on type mismatch 
+
+maxSize is 0, too small for string or is larger than SSIZE\_\-MAX \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af}, \doxyref{keyGetValueSize()}{p.}{group__keyvalue_ge326672fffb7474abfe9baf53b73217e}, \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} 
+
+\doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84} for working with binary data \end{Desc}
+\index{keyvalue@{keyvalue}!keyGetValueSize@{keyGetValueSize}}
+\index{keyGetValueSize@{keyGetValueSize}!keyvalue@{keyvalue}}
+\subsubsection[keyGetValueSize]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyGetValueSize (const Key $\ast$ {\em key})}\label{group__keyvalue_ge326672fffb7474abfe9baf53b73217e}
+
+
+Returns the number of bytes needed to store the key value, including the NULL terminator.
+
+It returns the correct size, independent of the Key Type. If it is a binary there might be '$\backslash$0' values in it.
+
+For an empty string you need one byte to store the ending NULL. For that reason 1 is returned. This is not true for binary data, so there might be returned 0 too.
+
+A binary key has no '$\backslash$0' termination. String types have it, so to there length will be added 1 to have enough space to store it.
+
+This method can be used with malloc() before \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} or \doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84} is called.
+
+
+
+\begin{Code}\begin{verbatim}char *buffer;
+buffer = malloc (keyGetValueSize (key));
+// use this buffer to store the value (binary or string)
+// pass keyGetValueSize (key) for maxSize
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes needed to store the key value 
+
+1 when there is no data and type is not binary 
+
+0 when there is no data and type is binary 
+
+-1 on null pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778}, \doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84}, \doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af} \end{Desc}
+\index{keyvalue@{keyvalue}!keySetBinary@{keySetBinary}}
+\index{keySetBinary@{keySetBinary}!keyvalue@{keyvalue}}
+\subsubsection[keySetBinary]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keySetBinary (Key $\ast$ {\em key}, \/  const void $\ast$ {\em newBinary}, \/  size\_\-t {\em dataSize})}\label{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7}
+
+
+Set the value of a key as a binary.
+
+A private copy of {\tt newBinary} will allocated and saved inside {\tt key}, so the parameter can be deallocated after the call.
+
+The {\tt filesys} backend, when used through a \doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20}, will make the value be kdbbEncoded into a human readable hex-digit text format.
+
+Consider using a string key instead.
+
+When newBinary is a NULL pointer the binary will be freed and 0 will be returned.
+
+\begin{Desc}
+\item[Note:]When the type of the key is already a binary type it won't be changed.\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the object on which to set the value \item[{\em newBinary}]is a pointer to any binary data or NULL to free the previous set data \item[{\em dataSize}]number of bytes to copy from {\tt newBinary} \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually copied to internal struct storage 
+
+0 when the internal binary was freed 
+
+-1 on NULL pointer 
+
+-1 when dataSize is 0 (but newBinary not NULL) or larger than SSIZE\_\-MAX \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84} 
+
+\doxyref{keyIsBinary()}{p.}{group__keytest_g9526b371087564e43e3dff8ad0dac949} to check if the type is binary 
+
+\doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} and \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} as preferred alternative to binary \end{Desc}
+\index{keyvalue@{keyvalue}!keySetComment@{keySetComment}}
+\index{keySetComment@{keySetComment}!keyvalue@{keyvalue}}
+\subsubsection[keySetComment]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keySetComment (Key $\ast$ {\em key}, \/  const char $\ast$ {\em newComment})}\label{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1}
+
+
+Set a comment for a key.
+
+A key comment is like a configuration file comment. See \doxyref{keySetComment()}{p.}{group__keyvalue_g8863a877a84fa46e6017fe72e49b89c1} for more information.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em newComment}]the comment, that can be freed after this call. \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually saved including final NULL 
+
+1 when the comment was freed 
+
+-1 on NULL pointer or memory problems \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetComment()}{p.}{group__keyvalue_gfb89735689929ff717cc9f2d0d0b46a2} \end{Desc}
+\index{keyvalue@{keyvalue}!keySetString@{keySetString}}
+\index{keySetString@{keySetString}!keyvalue@{keyvalue}}
+\subsubsection[keySetString]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keySetString (Key $\ast$ {\em key}, \/  const char $\ast$ {\em newStringValue})}\label{group__keyvalue_g622bde1eb0e0c4994728331326340ef2}
+
+
+Set the value for {\tt key} as {\tt newStringValue}.
+
+The function will allocate and save a private copy of {\tt newStringValue}, so the parameter can be freed after the call.
+
+String values will be saved in backend storage, when \doxyref{kdbSetKey()}{p.}{group__kdbhighlevel_g23b2f5fead4cddeb5542051a197ddc20} will be called, in UTF-8 universal encoding, regardless of the program's current encoding, when compiled with --enable-iconv.
+
+The type will be set to KEY\_\-TYPE\_\-STRING. When the type of the key is already a string type it won't be changed.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key to set the string value \item[{\em newStringValue}]NULL-terminated text string to be set as {\tt key's} value \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]the number of bytes actually saved in private struct including final NULL 
+
+-1 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778}, \doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af} \end{Desc}
+\index{keyvalue@{keyvalue}!keyValue@{keyValue}}
+\index{keyValue@{keyValue}!keyvalue@{keyvalue}}
+\subsubsection[keyValue]{\setlength{\rightskip}{0pt plus 5cm}const void$\ast$ keyValue (const Key $\ast$ {\em key})}\label{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af}
+
+
+Return a pointer to the real internal {\tt key} value.
+
+This is a much more efficient version of \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} \doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84}, and you should use it if you are responsible enough to not mess up things. You are not allowed to modify anything in the returned string. If you need a copy of the Value, consider to use \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778} or \doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84} instead.\subsection{String Handling}\label{group__keyvalue_string}
+If {\tt key} is string (\doxyref{keyIsString()}{p.}{group__keytest_gea7670778abd07fee0fe8ac12a149190}), you may cast the returned as a {\tt \char`\"{}char $\ast$\char`\"{}} because you'll get a NULL terminated regular string.
+
+\doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af} returns \char`\"{}\char`\"{} in string mode when there is no value. The reason is 
+
+\begin{Code}\begin{verbatim}key=keyNew(0);
+keySetString(key,"");
+keyValue(key); // you would expect "" here
+keyDel(key);
+\end{verbatim}
+\end{Code}
+
+\subsection{Binary Data Handling}\label{group__keyvalue_binary}
+If the data is binary, the size of the value must be determined by \doxyref{keyGetValueSize()}{p.}{group__keyvalue_ge326672fffb7474abfe9baf53b73217e}, any strlen() operations are not suitable to determine the size.
+
+\doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af} returns 0 in binary mode when there is no value. The reason is 
+
+\begin{Code}\begin{verbatim}int i=23;
+key=keyNew(0);
+keySetBinary(key, 0, 0);
+keyValue(key); // you would expect 0 here
+
+keySetBinary(key,"", 1);
+keyValue(key); // you would expect "" (a pointer to '\0') here
+
+keySetBinary(key, (void*)&i, 4);
+(int*)keyValue(key); // you would expect a pointer to (int)23 here
+keyDel(key);
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Note:]Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \doxyref{keyValue()}{p.}{group__keyvalue_g6f29609c5da53c6dc26a98678d5752af} method to set a new value. Use \doxyref{keySetString()}{p.}{group__keyvalue_g622bde1eb0e0c4994728331326340ef2} or \doxyref{keySetBinary()}{p.}{group__keyvalue_ga50a5358fd328d373a45f395fa1b99e7} instead.\end{Desc}
+\begin{Desc}
+\item[Warning:]Binary keys will return a NULL pointer when there is no data in contrast to \doxyref{keyName()}{p.}{group__keyname_g8e805c726a60da921d3736cda7813513}, \doxyref{keyBaseName()}{p.}{group__keyname_gaff35e7ca8af5560c47e662ceb9465f5}, \doxyref{keyOwner()}{p.}{group__keyname_gf6485fb8599714b6bbd830cf915ffea5} and \doxyref{keyComment()}{p.}{group__keyvalue_gc89fd319783b3457db45b4c09e55274a}. For string value the behaviour is the same.\end{Desc}
+\begin{Desc}
+\item[Example:]
+
+\begin{Code}\begin{verbatim}KDB *handle = kdbOpen();
+KeySet *ks=ksNew(0);
+Key *current=0;
+
+kdbGetByName(handle,ks,"system/sw/my",KDB_O_SORT|KDB_O_RECURSIVE);
+
+ksRewind(ks);
+while(current=ksNext(ks)) {
+        size_t size=0;
+        
+        if (keyIsBin(current)) {
+                size=keyGetValueSize(current);
+                printf("Key %s has a value of size %d bytes. Value: <BINARY>\nComment: %s",
+                        keyName(current),
+                        size,
+                        keyComment(current));
+        } else {
+                size=kdbiStrLen((char *)keyValue(current));
+                printf("Key %s has a value of size %d bytes. Value: %s\nComment: %s",
+                        keyName(current),
+                        size,
+                        (char *)keyValue(current),
+                        keyComment(current));
+        }
+}
+
+ksDel (ks);
+kdbClose (handle);
+\end{verbatim}
+\end{Code}
+
+\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]a pointer to internal value 
+
+\char`\"{}\char`\"{} when there is no data and key is not binary 
+
+0 where there is no data and key is binary 
+
+0 on NULL pointer \end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyGetValueSize()}{p.}{group__keyvalue_ge326672fffb7474abfe9baf53b73217e}, \doxyref{keyGetString()}{p.}{group__keyvalue_g41b9fac5ccddafe407fc0ae1e2eb8778}, \doxyref{keyGetBinary()}{p.}{group__keyvalue_g4c0d8a4a11174197699c231e0b5c3c84} \end{Desc}
diff --git a/doc/elektra-api/latex/group__stream.tex b/doc/elektra-api/latex/group__stream.tex
new file mode 100644 (file)
index 0000000..190893f
--- /dev/null
@@ -0,0 +1,399 @@
+\section{Streaming}
+\label{group__stream}\index{Streaming@{Streaming}}
+Methods to output, generate and toXML Keys and Keysets.  
+\subsection*{Enumerations}
+\begin{CompactItemize}
+\item 
+enum {\bf KDBStream} \{ \par
+{\bf KDB\_\-O\_\-SHOWMETA} = 0xF0, 
+{\bf KDB\_\-O\_\-SHOWFLAGS} = 1$<$$<$14, 
+{\bf KDB\_\-O\_\-SHOWINDICES} = 1$<$$<$15, 
+{\bf KDB\_\-O\_\-CONDENSED} = 1$<$$<$16, 
+\par
+{\bf KDB\_\-O\_\-NUMBER} = 1$<$$<$17, 
+{\bf KDB\_\-O\_\-HEADER} = 1$<$$<$18, 
+{\bf KDB\_\-O\_\-FULLNAME} = 1$<$$<$19, 
+{\bf KDB\_\-O\_\-HIER} = 1$<$$<$20
+ \}
+\end{CompactItemize}
+\subsection*{Functions}
+\begin{CompactItemize}
+\item 
+int {\bf ksFromXMLfile} (KeySet $\ast$ks, const char $\ast$filename)
+\item 
+int {\bf ksFromXML} (KeySet $\ast$ks, int fd)
+\item 
+ssize\_\-t {\bf keyToStream} (const Key $\ast$key, FILE $\ast$stream, option\_\-t options)
+\item 
+ssize\_\-t {\bf keyToStreamBasename} (const Key $\ast$key, FILE $\ast$stream, const char $\ast$parent, const size\_\-t parentSize, option\_\-t options)
+\item 
+ssize\_\-t {\bf ksToStream} (const KeySet $\ast$ks, FILE $\ast$stream, option\_\-t options)
+\item 
+int {\bf keyOutput} (const Key $\ast$k, FILE $\ast$stream, option\_\-t options)
+\item 
+int {\bf ksOutput} (const KeySet $\ast$ks, FILE $\ast$stream, option\_\-t options)
+\item 
+int {\bf keyGenerate} (const Key $\ast$key, FILE $\ast$stream, option\_\-t options)
+\item 
+int {\bf ksGenerate} (const KeySet $\ast$ks, FILE $\ast$stream, option\_\-t options)
+\end{CompactItemize}
+
+
+\subsection{Detailed Description}
+Methods to output, generate and toXML Keys and Keysets. 
+
+Here are some functions that are in a separate library because they depend on non-basic libraries as libxml. Link against kdbtools library if your program won't be installed in /bin, or is not essential in early boot stages.
+
+It is also possible to have a dynamic linkage, see the sourcecode from kdb-tool or testing framework how to achieve that.
+
+Output prints keys line per line, meaned to be read by humans.\begin{itemize}
+\item \doxyref{keyOutput()}{p.}{group__stream_g4eb79e22d81302283ef9acda19b372f5}\item \doxyref{ksOutput()}{p.}{group__stream_g45ae77a71186960d05d4e39f47184cb1}\end{itemize}
+
+
+Generate prints keys and keysets as C-Structs, meaned to be read by a C-Compiler.\begin{itemize}
+\item \doxyref{keyGenerate()}{p.}{group__stream_g7522c446271b79d62db8e52157495f40}\item \doxyref{ksGenerate()}{p.}{group__stream_g4a4eb7b7cfea5e2faef99ac331eb15e2}\end{itemize}
+
+
+toXML prints keys and keysets as XML, meaned to be used as exchange format.\begin{itemize}
+\item \doxyref{keyToStream()}{p.}{group__stream_gba86ec51ba1abc125372c5519abd45a9}\item \doxyref{ksToStream()}{p.}{group__stream_g7766ffe8c534fa7154a74fd26bd974f3}\end{itemize}
+
+
+To use them: 
+
+\begin{Code}\begin{verbatim}#include <kdbtools.h>
+\end{verbatim}
+\end{Code}
+
+
+\subsection{Enumeration Type Documentation}
+\index{stream@{stream}!KDBStream@{KDBStream}}
+\index{KDBStream@{KDBStream}!stream@{stream}}
+\subsubsection[KDBStream]{\setlength{\rightskip}{0pt plus 5cm}enum {\bf KDBStream}}\label{group__stream_g0f01b410963104a39a8c0109c339d1cd}
+
+
+Options to change the default behavior of streaming.
+
+On default the streaming options only output the names of the given keysets. If you want more information, header, metainfo, compressed output, full names, values or comments you will find the appropriate options here.
+
+For full influence of value, comment and metadata shown, use these options together with keyswitch\_\-t. All bits of meta-information ORed together are KDB\_\-O\_\-SHOWMETA.
+
+For more information about the flags, consult the documentation of the streaming methods.
+
+These options can be ORed. That is the $|$-Operator in C.
+
+It uses the values defined in keyswitch\_\-t too, so it starts with 14.
+
+\begin{Desc}
+\item[See also:]kdbGetChildKeys() 
+
+\doxyref{ksToStream()}{p.}{group__stream_g7766ffe8c534fa7154a74fd26bd974f3} 
+
+\doxyref{keyToStream()}{p.}{group__stream_gba86ec51ba1abc125372c5519abd45a9} \end{Desc}
+\begin{Desc}
+\item[Enumerator: ]\par
+\begin{description}
+\index{KDB\_\-O\_\-SHOWMETA@{KDB\_\-O\_\-SHOWMETA}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-SHOWMETA@{KDB\_\-O\_\-SHOWMETA}}\item[{\em 
+KDB\_\-O\_\-SHOWMETA\label{group__stream_gg0f01b410963104a39a8c0109c339d1cdf108bbeaf014509dccbe4098a828d71a}
+}]Show all metadata (type, uid, gid, mode) \index{KDB\_\-O\_\-SHOWFLAGS@{KDB\_\-O\_\-SHOWFLAGS}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-SHOWFLAGS@{KDB\_\-O\_\-SHOWFLAGS}}\item[{\em 
+KDB\_\-O\_\-SHOWFLAGS\label{group__stream_gg0f01b410963104a39a8c0109c339d1cd1f8f6dde6172b89e07c3a3be222da2c6}
+}]Show all flags \index{KDB\_\-O\_\-SHOWINDICES@{KDB\_\-O\_\-SHOWINDICES}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-SHOWINDICES@{KDB\_\-O\_\-SHOWINDICES}}\item[{\em 
+KDB\_\-O\_\-SHOWINDICES\label{group__stream_gg0f01b410963104a39a8c0109c339d1cd7ed84255e173af98655d16c480e324bf}
+}]Show the indices for the entries \index{KDB\_\-O\_\-CONDENSED@{KDB\_\-O\_\-CONDENSED}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-CONDENSED@{KDB\_\-O\_\-CONDENSED}}\item[{\em 
+KDB\_\-O\_\-CONDENSED\label{group__stream_gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2}
+}]Spare any whitespaces and do not group visually together. \index{KDB\_\-O\_\-NUMBER@{KDB\_\-O\_\-NUMBER}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-NUMBER@{KDB\_\-O\_\-NUMBER}}\item[{\em 
+KDB\_\-O\_\-NUMBER\label{group__stream_gg0f01b410963104a39a8c0109c339d1cd7df0465c4d5d6c3e063860e7ce6205d9}
+}]Use a number intead of user and group name. \index{KDB\_\-O\_\-HEADER@{KDB\_\-O\_\-HEADER}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-HEADER@{KDB\_\-O\_\-HEADER}}\item[{\em 
+KDB\_\-O\_\-HEADER\label{group__stream_gg0f01b410963104a39a8c0109c339d1cdb7831d9df99114658de58990c5158d29}
+}]Show also the header of the document. \index{KDB\_\-O\_\-FULLNAME@{KDB\_\-O\_\-FULLNAME}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-FULLNAME@{KDB\_\-O\_\-FULLNAME}}\item[{\em 
+KDB\_\-O\_\-FULLNAME\label{group__stream_gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d}
+}]Export {\tt user} keys using full name. \index{KDB\_\-O\_\-HIER@{KDB\_\-O\_\-HIER}!stream@{stream}}\index{stream@{stream}!KDB\_\-O\_\-HIER@{KDB\_\-O\_\-HIER}}\item[{\em 
+KDB\_\-O\_\-HIER\label{group__stream_gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856}
+}]Export to the new hierarchical XML representation using key basename. See \doxyref{ksToStream()}{p.}{group__stream_g7766ffe8c534fa7154a74fd26bd974f3}. \end{description}
+\end{Desc}
+
+
+
+\subsection{Function Documentation}
+\index{stream@{stream}!keyGenerate@{keyGenerate}}
+\index{keyGenerate@{keyGenerate}!stream@{stream}}
+\subsubsection[keyGenerate]{\setlength{\rightskip}{0pt plus 5cm}int keyGenerate (const Key $\ast$ {\em key}, \/  FILE $\ast$ {\em stream}, \/  option\_\-t {\em options})}\label{group__stream_g7522c446271b79d62db8e52157495f40}
+
+
+Generate a C-Style key and stream it.
+
+This keyset can be used to include as c-code for applikations using elektra.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em stream}]the file pointer where to send the stream \item[{\em options}]KDB\_\-O\_\-SHOWINDICES, KDB\_\-O\_\-IGNORE\_\-COMMENT, KDB\_\-O\_\-SHOWINFO \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 on success \end{Desc}
+\index{stream@{stream}!keyOutput@{keyOutput}}
+\index{keyOutput@{keyOutput}!stream@{stream}}
+\subsubsection[keyOutput]{\setlength{\rightskip}{0pt plus 5cm}int keyOutput (const Key $\ast$ {\em k}, \/  FILE $\ast$ {\em stream}, \/  option\_\-t {\em options})}\label{group__stream_g4eb79e22d81302283ef9acda19b372f5}
+
+
+Output every information of a single key depending on options.
+
+The format is not very strict and only intend to be read by human eyes for debugging purposes. Don't rely on the format in your applications.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em k}]the key object to work with \item[{\em stream}]the file pointer where to send the stream \item[{\em options}]see text above \end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksOutput()}{p.}{group__stream_g45ae77a71186960d05d4e39f47184cb1} \end{Desc}
+\begin{Desc}
+\item[Returns:]1 on success 
+
+-1 on allocation errors \end{Desc}
+\index{stream@{stream}!keyToStream@{keyToStream}}
+\index{keyToStream@{keyToStream}!stream@{stream}}
+\subsubsection[keyToStream]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyToStream (const Key $\ast$ {\em key}, \/  FILE $\ast$ {\em stream}, \/  option\_\-t {\em options})}\label{group__stream_gba86ec51ba1abc125372c5519abd45a9}
+
+
+Prints an XML representation of the key.
+
+String generated is of the form: 
+
+\footnotesize\begin{verbatim}
+       <key name="system/sw/xorg/Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>\end{verbatim}
+\normalsize
+
+
+
+
+\footnotesize\begin{verbatim}
+       <key parent="system/sw/xorg/Monitor/Monitor0" basename="Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>\end{verbatim}
+\normalsize
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em stream}]where to write output: a file or stdout \item[{\em options}]Some option\_\-t ORed:\begin{itemize}
+\item {\tt option\_\-t::KDB\_\-O\_\-NUMBERS} \par
+ Do not convert UID and GID into user and group names\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-CONDENSED}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2}} \par
+ Less human readable, more condensed output\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-FULLNAME}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d}} \par
+ The {\tt user} keys are exported with their full names (including user domains)\end{itemize}
+\end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{ksToStream()}{p.}{group__stream_g7766ffe8c534fa7154a74fd26bd974f3} \end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to output \end{Desc}
+\index{stream@{stream}!keyToStreamBasename@{keyToStreamBasename}}
+\index{keyToStreamBasename@{keyToStreamBasename}!stream@{stream}}
+\subsubsection[keyToStreamBasename]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t keyToStreamBasename (const Key $\ast$ {\em key}, \/  FILE $\ast$ {\em stream}, \/  const char $\ast$ {\em parent}, \/  const size\_\-t {\em parentSize}, \/  option\_\-t {\em options})}\label{group__stream_g32b9dcebc110c4bf69d9c08ca9f96400}
+
+
+Same as \doxyref{keyToStream()}{p.}{group__stream_gba86ec51ba1abc125372c5519abd45a9} but tries to strip {\tt parentSize} bytes from {\tt key} name if it matches {\tt parent} .
+
+Taking the example from \doxyref{keyToStream()}{p.}{group__stream_gba86ec51ba1abc125372c5519abd45a9}, if {\tt parent} is {\tt \char`\"{}system/sw/xorg\char`\"{}}, the generated string is of the form: 
+
+\footnotesize\begin{verbatim}
+       <key basename="Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>\end{verbatim}
+\normalsize
+
+
+It usefull to produce more human readable XML output of a key when it is being represented in a context that defines the parent key name. For example:
+
+
+
+\footnotesize\begin{verbatim}
+       <keyset parent="user/sw">
+               <key basename="kdbedit"..../>
+               <key basename="phototools"..../>
+               <key basename="myapp"..../>
+       </keyset>\end{verbatim}
+\normalsize
+
+
+In the bove example, each {\tt $<$key$>$} entry was generated by a call to \doxyref{keyToStreamBasename()}{p.}{group__stream_g32b9dcebc110c4bf69d9c08ca9f96400} having {\tt \char`\"{}user/sw\char`\"{}} as {\tt parent} .
+
+This method is used when \doxyref{ksToStream()}{p.}{group__stream_g7766ffe8c534fa7154a74fd26bd974f3} is called with \doxyref{KDBOption::KDB\_\-O\_\-HIER}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856} option.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em key}]the key object to work with \item[{\em stream}]the FILE where to send the stream \item[{\em parentSize}]the maximum size of {\tt parent} that will be used. If 0, the entire {\tt parent} will be used. \item[{\em parent}]the string (or part of it, defined by {\tt parentSize} ) that will be used to strip from the key name. \item[{\em options}]Some option\_\-t ORed:\begin{itemize}
+\item {\tt option\_\-t::KDB\_\-O\_\-NUMBERS} \par
+ Do not convert UID and GID into user and group names\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-CONDENSED}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2}} \par
+ Less human readable, more condensed output\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-FULLNAME}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d}} \par
+ The {\tt user} keys are exported with their full names (including user domains)\end{itemize}
+\end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to output \end{Desc}
+\index{stream@{stream}!ksFromXML@{ksFromXML}}
+\index{ksFromXML@{ksFromXML}!stream@{stream}}
+\subsubsection[ksFromXML]{\setlength{\rightskip}{0pt plus 5cm}int ksFromXML (KeySet $\ast$ {\em ks}, \/  int {\em fd})}\label{group__stream_g2fb7bd97dbf4fcaeb7d75efb6cad45d8}
+
+
+FIXME: not working when fd is stdin Given a file descriptor (that can be {\tt stdin}) for an XML file, validate schema, process nodes, convert and save it in the {\tt ks} KeySet.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]keyset \item[{\em fd}]Filedeskriptor?? should be FILE$\ast$ \end{description}
+\end{Desc}
+\index{stream@{stream}!ksFromXMLfile@{ksFromXMLfile}}
+\index{ksFromXMLfile@{ksFromXMLfile}!stream@{stream}}
+\subsubsection[ksFromXMLfile]{\setlength{\rightskip}{0pt plus 5cm}int ksFromXMLfile (KeySet $\ast$ {\em ks}, \/  const char $\ast$ {\em filename})}\label{group__stream_g68078d5ed73459629e01a228503e6821}
+
+
+Given an XML {\tt filename}, open it, validate schema, process nodes, convert and save it in the {\tt ks} KeySet.
+
+Currently, the XML file can have many root {\tt $<$keyset$>$} and {\tt $<$key$>$} nodes. They will all be reduced to simple keys returned in {\tt ks}.
+
+To check if the xml file is valid (best before you read from it): 
+
+\begin{Code}\begin{verbatim}#include 
+char schemaPath[513];
+schemaPath[0]=0;
+ret=kdbGetString(handle, KDB_SCHEMA_PATH_KEY,schemaPath,sizeof(schemaPath));
+
+if (ret==0) ret = isValidXML(filename,schemaPath);
+else ret = isValidXML(filename,KDB_SCHEMA_PATH); 
+\end{verbatim}
+\end{Code}
+
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset \item[{\em filename}]the file to parse \end{description}
+\end{Desc}
+\index{stream@{stream}!ksGenerate@{ksGenerate}}
+\index{ksGenerate@{ksGenerate}!stream@{stream}}
+\subsubsection[ksGenerate]{\setlength{\rightskip}{0pt plus 5cm}int ksGenerate (const KeySet $\ast$ {\em ks}, \/  FILE $\ast$ {\em stream}, \/  option\_\-t {\em options})}\label{group__stream_g4a4eb7b7cfea5e2faef99ac331eb15e2}
+
+
+Generate a C-Style keyset and stream it.
+
+This keyset can be used to include as c-code for applikations using elektra.
+
+The options takes the same options as \doxyref{kdbGet()}{p.}{group__kdb_g37b44bda1b83bc0144916bf21a86c1b5} and \doxyref{kdbSet()}{p.}{group__kdb_g953cf29721e6000c2516cd6b5d36f571}.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset to work with \item[{\em stream}]the file pointer where to send the stream \item[{\em options}]which keys not to output \end{description}
+\end{Desc}
+\begin{Desc}
+\item[Returns:]1 on success \end{Desc}
+\index{stream@{stream}!ksOutput@{ksOutput}}
+\index{ksOutput@{ksOutput}!stream@{stream}}
+\subsubsection[ksOutput]{\setlength{\rightskip}{0pt plus 5cm}int ksOutput (const KeySet $\ast$ {\em ks}, \/  FILE $\ast$ {\em stream}, \/  option\_\-t {\em options})}\label{group__stream_g45ae77a71186960d05d4e39f47184cb1}
+
+
+Output all information of a keyset.
+
+The format is not very strict and only intend to be read by human eyes for debugging purposes. Don't rely on the format in your applications.
+
+Keys are printed line per line with \doxyref{keyOutput()}{p.}{group__stream_g4eb79e22d81302283ef9acda19b372f5}.
+
+The same options as \doxyref{keyOutput()}{p.}{group__stream_g4eb79e22d81302283ef9acda19b372f5} are taken and passed to them.
+
+Additional KDB\_\-O\_\-HEADER will print the number of keys as first line.
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the keyset to work with \item[{\em stream}]the file pointer where to send the stream \item[{\em options}]\end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyOutput()}{p.}{group__stream_g4eb79e22d81302283ef9acda19b372f5} \end{Desc}
+\begin{Desc}
+\item[Returns:]1 on success 
+
+-1 on allocation errors \end{Desc}
+\index{stream@{stream}!ksToStream@{ksToStream}}
+\index{ksToStream@{ksToStream}!stream@{stream}}
+\subsubsection[ksToStream]{\setlength{\rightskip}{0pt plus 5cm}ssize\_\-t ksToStream (const KeySet $\ast$ {\em ks}, \/  FILE $\ast$ {\em stream}, \/  option\_\-t {\em options})}\label{group__stream_g7766ffe8c534fa7154a74fd26bd974f3}
+
+
+Writes to {\tt stream} an XML version of the {\tt ks} object.
+
+String generated is of the form: 
+
+\footnotesize\begin{verbatim}
+<keyset>
+<key name=...>...</key>
+<key name=...>...</key>
+<key name=...>...</key>
+
+</keyset>
+ * \end{verbatim}
+\normalsize
+
+
+or if KDB\_\-O\_\-HIER is used, the form will be: 
+
+\footnotesize\begin{verbatim}
+<keyset parent="user/smallest/parent/name">
+
+<key basename=...>...</key>
+<key name=...>...</key> <!-- a key thats not under this keyset's parent -->
+<key basename=...>...</key>
+
+</keyset>
+ * \end{verbatim}
+\normalsize
+
+
+KDB\_\-O\_\-HEADER will additionally generate a header like: 
+
+\footnotesize\begin{verbatim}
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of n keys. -->
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd">
+ * \end{verbatim}
+\normalsize
+
+
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]the KeySet to serialize \item[{\em stream}]where to write output: a file or stdout \item[{\em options}]accepted option\_\-t ORed:\begin{itemize}
+\item {\tt option\_\-t::KDB\_\-O\_\-NUMBERS} \par
+ Do not convert UID and GID into user and group names.\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-FULLNAME}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cd51691743e0c71a19d885bfd39b696b3d}} \par
+ The {\tt user} keys are exported with their full names (including user domains)\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-CONDENSED}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cdf13abb6e6831a159418bd71ef05cfdd2}} \par
+ Less human readable, more condensed output.\item {\tt option\_\-t::KDB\_\-O\_\-XMLHEADERS} \par
+ Exclude the correct XML headers in the output. If not used, the $<$?xml?$>$ and schema info inside the $<$keyset$>$ object will not be generated.\item {\tt \doxyref{option\_\-t::KDB\_\-O\_\-HIER}{p.}{group__stream_gg0f01b410963104a39a8c0109c339d1cdc1ce72f70ae166f80235020660c1c856}} \par
+ Will generate a $<$keyset$>$ node containing a {\tt parent} attribute, and $<$key$>$ nodes with a {\tt basename} relative to that {\tt parent}. The {\tt parent} is calculated by taking the smallest key name in the keyset, so it is a good idea to have only related keys on the keyset. Otherwise, a valid consistent XML document still will be generated with regular absolute {\tt name} attribute for the $<$key$>$ nodes, due to a clever \doxyref{keyToStreamBasename()}{p.}{group__stream_g32b9dcebc110c4bf69d9c08ca9f96400} implementation.\end{itemize}
+\end{description}
+\end{Desc}
+\begin{Desc}
+\item[See also:]\doxyref{keyToStream()}{p.}{group__stream_gba86ec51ba1abc125372c5519abd45a9} 
+
+commandList() for usage example \end{Desc}
+\begin{Desc}
+\item[Returns:]number of bytes written to output, or -1 if some error occurs\end{Desc}
+\begin{Desc}
+\item[Parameters:]
+\begin{description}
+\item[{\em ks}]The keyset to output \item[{\em stream}]the file pointer where to send the stream \item[{\em options}]see above text \end{description}
+\end{Desc}
diff --git a/doc/elektra-api/latex/modules.tex b/doc/elektra-api/latex/modules.tex
new file mode 100644 (file)
index 0000000..8c9e06b
--- /dev/null
@@ -0,0 +1,16 @@
+\section{Modules}
+Here is a list of all modules:\begin{CompactList}
+\item \contentsline{section}{KDB :: High Level methods}{\pageref{group__kdbhighlevel}}{}
+\item \contentsline{section}{KDB :: Low Level Methods}{\pageref{group__kdb}}{}
+\item \contentsline{section}{KDB Backends :: Backend Helper for Elektra}{\pageref{group__backendhelper}}{}
+\item \contentsline{section}{KDB Backends :: Elektra framework for pluggable backends}{\pageref{group__backend}}{}
+\item \contentsline{section}{KDB Backends :: Internal Helper for Elektra}{\pageref{group__internal}}{}
+\item \contentsline{section}{KDB Backends :: KDB access functions}{\pageref{group__backendhandle}}{}
+\item \contentsline{section}{Key :: Basic Methods}{\pageref{group__key}}{}
+\item \contentsline{section}{Key :: Meta Info Manipulation Methods}{\pageref{group__keymeta}}{}
+\item \contentsline{section}{Key :: Methods for Making Tests}{\pageref{group__keytest}}{}
+\item \contentsline{section}{Key :: Name Manipulation Methods}{\pageref{group__keyname}}{}
+\item \contentsline{section}{Key :: Value Manipulation Methods}{\pageref{group__keyvalue}}{}
+\item \contentsline{section}{KeySet :: Class Methods}{\pageref{group__keyset}}{}
+\item \contentsline{section}{Streaming}{\pageref{group__stream}}{}
+\end{CompactList}
diff --git a/doc/elektra-api/latex/refman.tex b/doc/elektra-api/latex/refman.tex
new file mode 100644 (file)
index 0000000..ad61d6e
--- /dev/null
@@ -0,0 +1,51 @@
+\documentclass[a4paper]{book}
+\usepackage{a4wide}
+\usepackage{makeidx}
+\usepackage{fancyhdr}
+\usepackage{graphicx}
+\usepackage{multicol}
+\usepackage{float}
+\usepackage{textcomp}
+\usepackage{alltt}
+\usepackage[utf8]{inputenc}
+\usepackage{doxygen}
+\makeindex
+\setcounter{tocdepth}{3}
+\renewcommand{\footrulewidth}{0.4pt}
+\begin{document}
+\begin{titlepage}
+\vspace*{7cm}
+\begin{center}
+{\Large Elektra Projekt }\\
+\vspace*{1cm}
+{\large Generated by Doxygen 1.5.6}\\
+\vspace*{0.5cm}
+{\small Tue Jun 30 14:43:54 2009}\\
+\end{center}
+\end{titlepage}
+\clearemptydoublepage
+\pagenumbering{roman}
+\tableofcontents
+\clearemptydoublepage
+\pagenumbering{arabic}
+\chapter{Error}
+\label{err}
+\input{err}
+\chapter{Module Index}
+\input{modules}
+\chapter{Module Documentation}
+\input{group__kdb}
+\include{group__backendhandle}
+\include{group__kdbhighlevel}
+\include{group__key}
+\include{group__keymeta}
+\include{group__keyname}
+\include{group__keyset}
+\include{group__keytest}
+\include{group__keyvalue}
+\include{group__backendhelper}
+\include{group__internal}
+\include{group__stream}
+\include{group__backend}
+\printindex
+\end{document}
diff --git a/doc/elektra-api/man/man3/backend.3 b/doc/elektra-api/man/man3/backend.3
new file mode 100644 (file)
index 0000000..9c5c64a
--- /dev/null
@@ -0,0 +1,653 @@
+.TH "KDB Backends :: Elektra framework for pluggable backends" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KDB Backends :: Elektra framework for pluggable backends \- The tactics to create pluggable backends to libelektra.so.  
+
+.PP
+.SS "Enumerations"
+
+.in +1c
+.ti -1c
+.RI "enum \fBbackend_t\fP { \fBKDB_BE_OPEN\fP = 1, \fBKDB_BE_CLOSE\fP = 1<<1, \fBKDB_BE_GET\fP = 1<<2, \fBKDB_BE_SET\fP = 1<<3, \fBKDB_BE_VERSION\fP = 1<<4, \fBKDB_BE_DESCRIPTION\fP = 1<<5, \fBKDB_BE_AUTHOR\fP = 1<<6, \fBKDB_BE_LICENCE\fP = 1<<7, \fBKDB_BE_END\fP = 0 }"
+.br
+.in -1c
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "KDB * \fBkdbBackendExport\fP (const char *backendName,...)"
+.br
+.ti -1c
+.RI "int \fBkdbOpen_backend\fP (KDB *handle)"
+.br
+.ti -1c
+.RI "int \fBkdbClose_backend\fP (KDB *handle)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbGet_backend\fP (KDB *handle, KeySet *returned, const Key *parentKey)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbSet_backend\fP (KDB *handle, KeySet *returned, const Key *parentKey)"
+.br
+.ti -1c
+.RI "\fBKDBEXPORT\fP (backend)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+The tactics to create pluggable backends to libelektra.so. 
+.PP
+.SH "Introduction"
+.PP
+\fBSince:\fP
+.RS 4
+Since version 0.4.9, Elektra can dynamically load different key storage backends.
+.PP
+Since version 0.7.0 Elektra can have multiple storage backends, called just backends henceforth, at once for different purposes.
+.RE
+.PP
+\fBDefinition: You refers to the implementation of the function in this specification.\fP.RS 4
+If you read the documentation about \fBkdbGet_backend()\fP, then the caller is \fBkdbGet()\fP which is the only function which can and will call (invoke) you. The Preconditions will always be met by the caller, you can count on them. But you (as said before we speak about the function) need to take care that all Postconditions are met.
+.RE
+.PP
+.SS "Overview"
+The methods of class KDB that are backend dependent are only \fBkdbOpen_backend()\fP, \fBkdbClose_backend()\fP, \fBkdbGet_backend()\fP, \fBkdbSet_backend()\fP and \fBKDBEXPORT()\fP to export these methods. A backend must implement each of them. A detailed specification of these methods and methods needed in that context follows in this Documentation Module.
+.PP
+The other KDB methods are higher level. They use the above methods to do their job, and generally don't have to be reimplemented for a different backend, but there might be a solution to do so for higher performance in future. kdbh* methods are for access to the internals of KDB, which will be passed to all functions.
+.SS "Include Files"
+The backend implementation must include: 
+.PP
+.nf
+#include <kdbbackend.h>
+
+.fi
+.PP
+ to have direct access to the structs, which is currently needed to access the capability structure.
+.PP
+Don't include kdb.h, it will be automatically included and some macros will avoid redefining structs where you have more insight from a backend than you would normally have. Additionally you get the declaration of all functions described here, except the one you have to implement.
+.SS "Dynamic Mounting"
+An elektrified program will use elektra/libelektra-default.so as its default backend. This backend provides the system/ hierarchy and some base configuration in system/elektra for elektra itself. Everything below system/ and the other hierarchies can be stored in any different backend. This is allowed through the technique mounting. A backend can be mounted to any path except system/ and system/elektra.
+.PP
+A backends is guaranteed to be loaded whenever calling \fBkdbGet()\fP or \fBkdbSet()\fP requires the backend, but may already be loaded at \fBkdbOpen()\fP. It might be loaded explizit by \fBkdbMount()\fP at any time after \fBkdbOpen()\fP. Backends get a chance to initialize by calling \fBkdbOpen_backend()\fP whenever they are loaded.
+.PP
+Using \fBkdbUnmount()\fP a backend may closed during runtime. All backends will be closed when \fBkdbClose()\fP is called. Backends might be unloaded after some time of inactivity or other reasons. After loading backends get a chance to cleanup by calling \fBkdbClose_backend()\fP.
+.PP
+That means it is not guaranteed that the backend live the whole time nor it will be loaded only one time. A tactic to handle this well is to build stateless backends referring to \fBkdbGet_backend()\fP and \fBkdbSet_backend()\fP. That means that there is no more information present than in the storage itself. Be aware that you must not have any global variables in your backend. Read more about that in \fBkdbOpen_backend()\fP. But to be stateless you also have to consider not to store any other than caching information into \fBkdbhGetBackendData()\fP. I repeat: it must be possible to restore everything dynamically stored without exception.
+.SS "Library Names"
+Elektra source code or development package provides a skeleton and Makefile to implement a backend. Copy src/backends/template to have a good starting point. See the CODING document to know how to integrate the backend in the build system or how to compile it external.
+.PP
+A backend is defined by a single name, for example \fCBACKENDNAME\fP, that causes libelektra.so look for its library as \fClibelektra-BACKENDNAME.so\fP.
+.PP
+\fBExample of a complete backend:\fP.RS 4
+
+.PP
+.nf
+//
+// This is my implementation for an Elektra backend storage.
+//
+// To compile it:
+// $ cc -fpic `pkg-config --cflags elektra` -o myback.o -c myback.c
+// $ cc -shared -fpic `pkg-config --libs elektra` -o libelektra-myback.so myback.o
+//
+// To use it:
+// $ preload mount myback system/myback myback /tmp/nofile
+// $ kdb ls system/myback
+// $ kdb set system/myback/key 'value'
+// $ kdb get system/myback/key
+//
+
+#include <kdbbackend.h>
+
+#define BACKENDNAME 'backend'
+
+
+int kdbOpen_backend(KDB *handle) {...}
+int kdbClose_backend(KDB *handle) {...}
+int kdbGet_backend(KDB handle, KeySet *returned, Key *key) {...}
+int kdbSet_backend(KDB handle, KeySet *returned, Key *key) {...}
+
+KDBEXPORT(backend) {
+        return kdbBackendExport(BACKENDNAME,
+                KDB_BE_OPEN,  &kdbOpen_backend,
+                KDB_BE_CLOSE, &kdbClose_backend,
+                KDB_BE_GET,   &kdbGet_backend,
+                KDB_BE_SET,   &kdbSet_backend,
+                KDB_BE_END);
+}
+
+.fi
+.PP
+.RE
+.PP
+In the example, the *_backend() methods can have other random names, since you'll correctly pass them later to \fBkdbBackendExport()\fP. It is recommended to use names according to your backendname to avoid name clashes. Be aware that every symbol name in the linked application must be unique.
+.PP
+Don't copy above example out, use src/backends/template, it does compile as-is and does some initialization and cleanup already.
+.PP
+Elektra source code tree includes several backend implementations https://svn.libelektra.org/svn/elektra/trunk/src/backends/ that can also be used as a reference.
+.SH "Details"
+.PP
+.SS "Introduction"
+Capabilities may make your live much easier. If it is impossible, very hard or would impact performance badly you may leave out some parts described here, but need to declare that you have done so with capabilites.
+.PP
+It is allowed to provide additional information, even if you declared you don't have it. If you declare that you are capable of doing something, you must provide it without exceptions.
+.SS "Owner"
+You need to set the owner of keys by \fBkeySetOwner()\fP. Owner is the name to whom a specific key of the user/ hierarchy belongs. If you declare kdbcGetnoOwner() you need not to set the owner of the keys. It also means that even if you want to get keys from another user hierarchy you get yours.
+.SS "Values"
+Values are the central information of keys next to the name describing what informations it holds. Parse them out of your backend and put them into the key with \fBkeySetString()\fP. The information will be duplicated, so you might need to free() your string. Don't try to directly access key->data, things may change there and your backend might be compiled with a different libc than elektra. If you support types, you might want to use keySetRaw() to not change the key type. If you don't support values for all keys declare kdbcGetnoValue().
+.SS "IDs"
+You need to set uid respective gid for any key not having the uid and gid of the current process. This will be set by default in every key. You can do it with \fBkeySetUID()\fP and \fBkeySetGID()\fP. Declaring kdbcGetnoUID() and kdbcGetnoGID() you need not set uid and gid.
+.SS "Mode"
+Mode shows what can be done with the key having or not having the above uid and gid. Use \fBkeySetMode()\fP to set the correct mode description, read the description in \fBkeySetMode()\fP for the semantics of the 3 octal representation. Declaring kdbcGetnoMode() means mode will remain default.
+.PP
+The very related method \fBkeySetDir()\fP sets the executable bits of mode. Even if your backend does not support mode, it might support directories, meaning that keys have the mode 0664 or 0775 for directories. Declaring kdbcGetnoDir() means that the backend is flat, no key will be true for \fBkeyIsDir()\fP and so can't have any subkeys.
+.SS "Timing"
+Keys should have exact timing information of their modification and access times. Use \fBkeySetATime()\fP, \fBkeySetMTime()\fP and \fBkeySetCTime()\fP to store appropriate information. ATime need to be stored in database, if you stat a key the backend need to return the time \fBkdbGet()\fP was last used for the keys. If you don't support this, declare kdbcGetnoATime() and simple store time(0) in the atime. This must be the same for every key for a single \fBkdbGet_backend()\fP. If you only stat keys with \fBkdbGet()\fP, see below, then the access time should not be updated. MTime is the last modification time of value or comment. If you don't support this, declare kdbcGetnoMTime() and simple store time(0) in the mtime. This must be the same for every key for a single \fBkdbGet_backend()\fP. CTime is the last change time of any metadata or add/remove of subkeys. If you don't support this, declare kdbcGetnoCTime() and simple store time(0) in the ctime. This must be the same for every key for a single \fBkdbGet_backend()\fP.
+.SS "Types"
+Keys having value and comment can be one of two fundamental types, string or binary, both called value. While string is a null terminated utf8 character sequence, binary is any data of a specific length. Be sure to use \fBkeySetString()\fP for string and \fBkeySetBinary()\fP if you want to store binary data. If you do not support one of these, be sure to declare kdbcGetnoBinary() or kdbcGetnoString(), if you don't support both make sure to also declare kdbcGetnoValue().
+.PP
+Using keySetRaw() does not set the type, be sure to use \fBkeySetType()\fP afterwards. This can be KEY_TYPE_STRING and KEY_TYPE_BINARY or any other type in type_t, leading to same results as explained above, but also any other number in the range of type_t. Declare kdbcGetnoTypes() when your backend does not support arbitrary types. 
+.SH "Enumeration Type Documentation"
+.PP 
+.SS "enum \fBbackend_t\fP"
+.PP
+Switches to denote the backend methods. Used in calls to \fBkdbBackendExport()\fP. 
+.PP
+\fBEnumerator: \fP
+.in +1c
+.TP
+\fB\fIKDB_BE_OPEN \fP\fP
+Next arg is backend for \fBkdbOpen()\fP 
+.TP
+\fB\fIKDB_BE_CLOSE \fP\fP
+Next arg is backend for \fBkdbClose()\fP 
+.TP
+\fB\fIKDB_BE_GET \fP\fP
+Next arg is backend for \fBkdbGet()\fP 
+.TP
+\fB\fIKDB_BE_SET \fP\fP
+Next arg is backend for \fBkdbSet()\fP 
+.TP
+\fB\fIKDB_BE_VERSION \fP\fP
+Next arg is char * for Version 
+.TP
+\fB\fIKDB_BE_DESCRIPTION \fP\fP
+Next arg is char * for Description 
+.TP
+\fB\fIKDB_BE_AUTHOR \fP\fP
+Next arg is char * for Author 
+.TP
+\fB\fIKDB_BE_LICENCE \fP\fP
+Next arg is char * for Licence 
+.TP
+\fB\fIKDB_BE_END \fP\fP
+End of arguments 
+.SH "Function Documentation"
+.PP 
+.SS "KDB* kdbBackendExport (const char * backendName,  ...)"
+.PP
+This function must be called by a backend's kdbBackendFactory() to define the backend's methods that will be exported.
+.PP
+See \fBKDBEXPORT()\fP how to use it for backends.
+.PP
+The order and number of arguments are flexible (as in \fBkeyNew()\fP and \fBksNew()\fP) to let libelektra.so evolve without breaking its ABI compatibility with backends. So for each method a backend must export, there is a flag defined by \fBbackend_t\fP. Each flag tells \fBkdbBackendExport()\fP which method comes next. A backend can have no implementation for a few methods that have default inefficient high-level implementations and to use these defaults, simply don't pass anything to \fBkdbBackendExport()\fP about them.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIbackendName\fP a simple name for this backend 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+an object that contains all backend informations needed by libelektra.so 
+.RE
+.PP
+
+.SS "int kdbClose_backend (KDB * handle)"
+.PP
+Finalize the backend. Called prior to unloading the backend dynamic module. Should ensure that no functions or static/global variables from the module will ever be accessed again.
+.PP
+Make sure to free all memory that your backend requested at runtime.
+.PP
+Specifically make sure to capDel() all capabilites and free your backendData in \fBkdbhGetBackendData()\fP.
+.PP
+After this call, libelektra.so will unload the backend library, so this is the point to shutdown any affairs with the storage.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success, anything else otherwise. 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbClose()\fP 
+.RE
+.PP
+
+.SS "KDBEXPORT (backend)"
+.PP
+All KDB methods implemented by the backend can have random names, except kdbBackendFactory(). This is the single symbol that will be looked up when loading the backend, and the first method of the backend implementation that will be called.
+.PP
+Its purpose is to publish the exported methods for libelektra.so. The implementation inside the provided skeleton is usually enough: simply call \fBkdbBackendExport()\fP with all methods that must be exported.
+.PP
+The first paramter is the name of the backend. Then every backend must have: \fCKDB_BE_OPEN\fP, \fCKDB_BE_CLOSE\fP, \fCKDB_BE_GET\fP and \fCKDB_BE_SET\fP 
+.PP
+You might also give following information by char *: \fCKDB_BE_VERSION\fP, \fCKDB_BE_AUTHOR\fP, \fCKDB_BE_LICENCE\fP and \fCKDB_BE_DESCRIPTION\fP 
+.PP
+You must use static 'char arrays' in a read only segment. Don't allocate storage, it won't be freed.
+.PP
+With capability you can get that information on runtime from any backend with kdbGetCapability().
+.PP
+The last parameter must be \fCKDB_BE_END\fP.
+.PP
+\fBReturns:\fP
+.RS 4
+\fBkdbBackendExport()\fP with the above described parameters. 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbBackendExport()\fP for an example 
+.PP
+kdbOpenBackend() 
+.RE
+.PP
+
+.SS "ssize_t kdbGet_backend (KDB * handle, KeySet * returned, const Key * parentKey)"
+.PP
+Retrieve information from a permanent storage to construct a keyset.
+.SH "Introduction"
+.PP
+This function does everything related to get keys out from a backend. There is only one function for that purpose to make implementation and locking much easier.
+.PP
+The keyset \fCreturned\fP needs to be filled with information so that the application using elektra can access it. See the live cycle of a comment to understand: 
+.PP
+.nf
+kdbGet_backend(KDB *handle, KeySet *returned, Key *parentKey)
+{
+        // the task of kdbGet_backend is to retrieve the comment out of the permanent storage
+        Key *key = keyDup (parentKey); // generate a new key to hold the information
+        char *comment;
+        loadfromdisc (comment);
+        keySetComment (key, comment, size); // set the information
+        ksAppendKey(returned, key);
+}
+
+// Now return to kdbGet
+int kdbGet(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+        kdbGet_backend (handle, keyset, 0);
+        // postprocess the keyset and return it
+}
+
+// Now return to usercode, waiting for the comment
+void usercode (Key *key)
+{
+        kdbGet (handle, keyset, parentKey, 0);
+        key = ksCurrent (keyset, key); // lookup the key from the keyset
+        keyGetComment (key); // now the usercode retrieves the comment
+}
+
+.fi
+.PP
+ Of course not only the comment, but all information of every key in the keyset \fCreturned\fP need to be fetched from permanent storage and stored in the key. So this specification needs to give an exhaustive list of information present in a key.
+.SH "Conditions"
+.PP
+\fBPrecondition:\fP
+.RS 4
+The caller \fBkdbGet()\fP will make sure before you are called that the parentKey:
+.IP "\(bu" 2
+is a valid key (means that it is a system or user key).
+.IP "\(bu" 2
+is below (see \fBkeyIsBelow()\fP) your mountpoint and that your backend is responsible for it.
+.IP "\(bu" 2
+has \fBkeyNeedStat()\fP set when you should only stat keys (see later). and that the returned:
+.IP "\(bu" 2
+is a valid keyset.
+.IP "\(bu" 2
+has \fCall\fP keys with the flag KEY_FLAG_SYNC set.
+.IP "\(bu" 2
+\fCmay\fP has keys with the flag KEY_FLAG_STAT set.
+.IP "\(bu" 2
+contains only valid keys direct below (see \fBkeyIsDirectBelow()\fP) your parentKey. That also means, that the parentKey will not be in that keyset.
+.IP "\(bu" 2
+have keyIsStat() set when the value/comment information is not necessary.
+.IP "\(bu" 2
+is in a sorted order, see \fBksSort()\fP. and that the handle:
+.IP "  \(bu" 4
+is a valid KDB for your backend.
+.IP "  \(bu" 4
+that kdbhGetBackendHandle() contains the same handle for lifetime \fBkdbOpen_backend()\fP until \fBkdbClose_backend()\fP was called.
+.PP
+
+.PP
+.PP
+The caller \fBkdbGet()\fP will make sure that afterwards you were called, whenever the user requested it with the options, that:
+.IP "\(bu" 2
+hidden keys they will be thrown away.
+.IP "\(bu" 2
+dirs or only dirs \fBkdbGet()\fP will remove the other.
+.IP "\(bu" 2
+you will be called again recursively with all subdirectories.
+.IP "\(bu" 2
+the keyset will be sorted when needed.
+.IP "\(bu" 2
+the keys in returned having KEY_FLAG_SYNC will be sorted out.
+.PP
+.RE
+.PP
+\fBInvariant:\fP
+.RS 4
+There are no global variables and \fBkdbhGetBackendData()\fP only stores information which can be regenerated any time. The handle is the same when it is the same backend.
+.RE
+.PP
+\fBPostcondition:\fP
+.RS 4
+The keyset \fCreturned\fP has the \fCparentKey\fP and all keys direct below (\fBkeyIsDirectBelow()\fP) with all information from the storage. Make sure to return all keys, all directories and also all hidden keys. If some of them are not wished, the caller \fBkdbGet()\fP will drop these keys, see above.
+.RE
+.PP
+.SH "Details"
+.PP
+Now lets look at an example how the typical \fBkdbGet_backend()\fP might be implemented. To explain we introduce some pseudo functions which do all the work with the storage (which is of course 90% of the work for a real backend):
+.IP "\(bu" 2
+find_key() gets an key out from the storage and memorize the position.
+.IP "\(bu" 2
+next_key() will find the next key and return it (with the name).
+.IP "\(bu" 2
+fetch_key() gets out all information of a key from storage (details see below example). It removes the \fBkeyNeedStat()\fP and \fBkeyNeedSync()\fP flag afterwards.
+.IP "\(bu" 2
+stat_key() gets all meta information (everything but value and comment). It removes the key \fBkeyNeedSync()\fP flag afterwards. returns the next key out from the storage. The typical loop now will be like: 
+.PP
+.nf
+ssize_t kdbGet_backend(KDB *handle, KeySet *update, const Key *parentKey) {
+        Key * current;
+        KeySet *returned = ksNew(ksGetSize(update)*2, KS_END);
+
+        find_key (parentKey);
+        current = keyDup (parentKey);
+        if (keyNeedStat(parentKey))
+        {
+                current = stat_key(current);
+        } else {
+                current = fetch_key(current);
+        }
+        clear_bit (KEY_FLAG_SYNC, current->flags);
+        ksAppendKey(returned, current);
+
+        while ((current = next_key()) != 0)
+        {
+                // search if key was passed in update by caller
+                Key * tmp = ksLookup (update, current, KDB_O_WITHOWNER|KDB_O_POP);
+                if (tmp) current = tmp; // key was passed, so use it
+                if (keyNeedStat(parentKey) || keyNeedStat(current))
+                {
+                        current = stat_key (current);
+                        set_bit (KEY_FLAG_STAT, current->flags);
+                } else {
+                        current = fetch_key(current);
+                }
+                clear_bit (KEY_FLAG_SYNC, current->flags);
+                ksAppendKey(returned, current);
+                // TODO: delete lookup key
+        }
+
+        if (error_happened())
+        {
+                errno = restore_errno();
+                return -1;
+        }
+
+        ksClear (update); // the rest of update keys is not in storage anymore
+        ksAppend(update, returned); // append the keys
+        ksDel (returned);
+
+        return nr_keys();
+}
+
+.fi
+.PP
+
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+- returned and update are separated, for details why see \fBksLookup()\fP
+.IP "\(bu" 2
+the bit KEY_FLAG_SYNC is always cleared, see postconditions
+.PP
+.RE
+.PP
+So your mission is simple: Search the \fCparentKey\fP and add it and then search all keys below and add them too, of course with all requested information (which is only depended on \fBkeyNeedStat()\fP).
+.SH "Stat"
+.PP
+Sometimes value and comment are not of interest, but metadata. To avoid a potential time-consuming \fBkdbGet()\fP you can \fBkeyNeedStat()\fP the \fCparentKey\fP. If the backend supports a less time-consuming method to just get names and metadata, implement it, otherwise declare kdbcGetnoStat().
+.PP
+The implementation works as follows: When the \fCparentKey\fP has \fBkeyNeedStat()\fP set, all keys need to be stated instead of getting them. So the keys you \fBksAppendKey()\fP don't have a value nor a comment and make sure that KEY_FLAG_SYNC is not set, but \fBkeyNeedStat()\fP must be set for all keys which are only stated.
+.PP
+The keys in \fCreturned\fP may already have \fBkeyNeedStat()\fP set. These keys must keep the status \fBkeyNeedStat()\fP and you don't need to get the value and comment. See the example above for code.
+.SH "Updating"
+.PP
+To get all keys out of the storage over and over again can be very inefficient. You might know a more efficient method to know if the key needs update or not, e.g. by stating it or by an external time stamp info. In that case you can make use of \fCreturned\fP KeySet. There are following possibilities:
+.IP "\(bu" 2
+The key is in returned and up to date. You just need to remove the KEY_FLAG_SYNC flag.
+.IP "\(bu" 2
+The key is in returned but \fBkeyNeedStat()\fP is true. You just need to stat the key and remove the KEY_FLAG_SYNC flag and set the KEY_FLAG_STAT flag.
+.IP "\(bu" 2
+The key is in returned, \fBkeyNeedStat()\fP is false (for the key and the \fCparentKey\fP) and you know that the key has changed. You need to fully retrieve the key and remove the KEY_FLAG_SYNC flag.
+.IP "\(bu" 2
+The key is not in returned, the \fCparentKey\fP has \fBkeyNeedStat()\fP. You just need to stat the key. Make sure that KEY_FLAG_SYNC is not set, but KEY_FLAG_STAT needs to be set. Append the key to \fCreturned\fP.
+.IP "\(bu" 2
+The key is not in returned and the \fCparentKey\fP \fBkeyNeedStat()\fP is false. You need to fully retrieve the key out of storage, clear KEY_FLAG_STAT and KEY_FLAG_SYNC and \fBksAppendKey()\fP it to the \fCreturned\fP keyset.
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+You must clear the flag KEY_FLAG_SYNC at the very last point where no more modification on the key will take place, because any modification on the key will set the KEY_FLAG_SYNC flag again. With that \fBkeyNeedSync()\fP will return true and the caller will sort this key out.
+.RE
+.PP
+.SH "only Full Get"
+.PP
+In some backends it is not useful to get only a part of the configuration, because getting all keys would take as long as getting some. For this situation, you can declare onlyFullGet, see kdbcGetonlyFullGet().
+.PP
+The only valid call for your backend is then that \fCparentKey\fP equals the \fCmountpoint\fP. For all other \fCparentKey\fP you must, add nothing and just return 0.
+.PP
+.PP
+.nf
+if (strcmp (keyName(kdbhGetMountpoint(handle)), keyName(parentKey))) return 0;
+.fi
+.PP
+.PP
+If the \fCparentKey\fP is your mountpoint you will of course fetch all keys, and not only the keys direct below the \fCparentKey\fP. So \fCreturned\fP is valid iff:
+.IP "\(bu" 2
+every key is below ( \fBkeyIsBelow()\fP) the parentKey
+.IP "\(bu" 2
+every key has a direct parent (\fBkeyIsDirectBelow()\fP) in the keyset
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+This statement is only valid for backends with kdbcGetonlyFullGet() set.
+.PP
+If any calls you use change errno, make sure to restore the old errno.
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbGet()\fP for caller.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIreturned\fP contains a keyset where the function need to append the keys got from the storage. There might be also some keys inside it, see conditions. You may use them to support efficient updating of keys, see \fBUpdating\fP. 
+.br
+\fIparentKey\fP contains the information below which key the keys should be gotten.
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+Return how many keys you added.
+.PP
+-1 on failure, the current key in returned shows the position. In normal execution cases a positive value will be returned. But in some cases you are not able to get keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added in 0.7.1) 
+.RE
+.PP
+
+.SS "int kdbOpen_backend (KDB * handle)"
+.PP
+Initialize the backend. This is the first method kdbOpenBackend() calls after dynamically loading the backend library.
+.PP
+This method is responsible of:
+.IP "\(bu" 2
+backend's specific configuration gathering
+.IP "\(bu" 2
+all backend's internal structs initialization
+.IP "\(bu" 2
+initial setup of all I/O details such as opening a file, connecting to a database, etc
+.PP
+.PP
+If your backend does not support all aspects described in \fBkdbGet_backend()\fP and \fBkdbSet_backend()\fP you need capabilities to export this information. Per default you declare to be fully compliant to the specification given here, to change it get a pointer to KDBCap structure by using \fBkdbhGetCapability()\fP.
+.PP
+You may also read the configuration you can get with \fBkdbhGetConfig()\fP and transform it into other structures used by your backend.
+.PP
+But be aware that you don't have any global variables. If you do your backend will not be threadsafe. You can use \fBkdbhSetBackendData()\fP and \fBkdbhGetBackendData()\fP to store and get any information related to your backend.
+.PP
+The correct substitute for global variables will be: 
+.PP
+.nf
+struct _GlobalData{ int global; };
+typedef struct _GlobalData GlobalData;
+int kdbOpen_backend(KDB *handle) {
+        PasswdData *data;
+        data=malloc(sizeof(PasswdData));
+        data.global = 20;
+        kdbhSetBackendData(handle,data);
+}
+
+.fi
+.PP
+.PP
+Make sure to free everything in \fBkdbClose_backend()\fP.
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbOpen()\fP 
+.RE
+.PP
+
+.SS "ssize_t kdbSet_backend (KDB * handle, KeySet * returned, const Key * parentKey)"
+.PP
+Store a keyset permanently.
+.PP
+This function does everything related to set and remove keys in a backend. There is only one function for that purpose to make implementation and locking much easier.
+.PP
+The keyset \fCreturned\fP was filled in with information from the application using elektra and the task of this function is to store it in a permanent way so that a subsequent call of \fBkdbGet_backend()\fP can rebuild the keyset as it was before. See the live cycle of a comment to understand: 
+.PP
+.nf
+void usercode (Key *key)
+{
+        keySetComment (key, 'mycomment'); // the usercode stores a comment for the key
+        ksAppendKey(keyset, key); // append the key to the keyset
+        kdbSet (handle, keyset, 0, 0);
+}
+
+// so now kdbSet is called
+int kdbSet(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+        // find appropriate backend
+        kdbSet_backend (handle, keyset, 0); // the keyset with the key will be passed to this function
+}
+
+// so now kdbSet_backend(), which is the function described here, is called
+kdbSet_backend(KDB *handle, KeySet *keyset, Key *parentKey)
+{
+        // the task of kdbSet_backend is now to store the comment
+        Key *key = ksCurrent (keyset); // get out the key where the user set the comment before
+        char *comment = allocate(size);
+        keyGetComment (key, comment, size);
+        savetodisc (comment);
+}
+
+.fi
+.PP
+ Of course not only the comment, but all information of every key in the keyset \fCreturned\fP need to be stored permanetly. So this specification needs to give an exhaustive list of information present in a key.
+.PP
+\fBPrecondition:\fP
+.RS 4
+The keyset \fCreturned\fP holds all keys which must be saved permanently for this keyset. The keyset is sorted and rewinded. All keys having children must be true for \fBkeyIsDir()\fP.
+.PP
+The \fCparentKey\fP is the key which is the ancestor for all other keys in the keyset. The first key of the keyset \fCreturned\fP has the same keyname. The parentKey is below the mountpoint, see \fBkdbhGetMountpoint()\fP.
+.PP
+The caller kdbSet will fulfill following parts:
+.IP "\(bu" 2
+If the user does not want hidden keys they will be thrown away. All keys in \fCreturned\fP need to be stored permanently.
+.IP "\(bu" 2
+If the user does not want dirs or only dirs \fBkdbGet()\fP will remove the other.
+.IP "\(bu" 2
+Sorting of the keyset. It is not important in which order the keys are appended. So make sure to set all keys, all directories and also all hidden keys. If some of them are not wished, the caller \fBkdbSet()\fP will sort them out.
+.PP
+.RE
+.PP
+\fBInvariant:\fP
+.RS 4
+There are no global variables and \fBkdbhGetBackendData()\fP only stores information which can be regenerated any time. The handle is the same when it is the same backend.
+.RE
+.PP
+\fBPostcondition:\fP
+.RS 4
+The information of the keyset \fCreturned\fP is stored permanently.
+.RE
+.PP
+When some keys have KEY_FLAG_REMOVE set, that means return true for \fBkeyNeedRemove()\fP, remove the keys instead of getting them. In this case the sorting order will be the reverse way, first will be the children, then the parentKey when iterating over the KeySet returned.
+.PP
+Lock your permanent storage in an exclusive way, no access of a concurrent \fBkdbSet_backend()\fP or \fBkdbGet_backend()\fP is possible and these methods block until the function has finished. Otherwise declare kdbcGetnoLock().
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbSet()\fP for caller.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIreturned\fP contains a keyset with relevant keys 
+.br
+\fIparentKey\fP contains the information where to set the keys
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+When everything works gracefully return the number of keys you set. The cursor position and the keys remaining in the keyset are not important.
+.PP
+Return 0 on success with no changed key in database
+.PP
+Return -1 on failure.
+.RE
+.PP
+\fBNote:\fP
+.RS 4
+If any calls you use change errno, make sure to restore the old errno.
+.RE
+.PP
+\fBError\fP
+.RS 4
+In normal execution cases a positive value will be returned. But in some cases you are not able to set keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added with 0.7.1)
+.RE
+.PP
+You also have to make sure that \fBksGetCursor()\fP shows to the position where the error appeared. 
diff --git a/doc/elektra-api/man/man3/backendhandle.3 b/doc/elektra-api/man/man3/backendhandle.3
new file mode 100644 (file)
index 0000000..8788a87
--- /dev/null
@@ -0,0 +1,292 @@
+.TH "KDB Backends :: KDB access functions" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KDB Backends :: KDB access functions \- Methods to access the backend handle.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "void * \fBkdbhSetBackendData\fP (KDB *handle, void *data)"
+.br
+.ti -1c
+.RI "void * \fBkdbhGetBackendData\fP (const KDB *handle)"
+.br
+.ti -1c
+.RI "KDBCap * \fBkdbhSetCapability\fP (KDB *handle, KDBCap *cap)"
+.br
+.ti -1c
+.RI "KDBCap * \fBkdbhGetCapability\fP (const KDB *handle)"
+.br
+.ti -1c
+.RI "Trie * \fBkdbhGetTrie\fP (const KDB *handle)"
+.br
+.ti -1c
+.RI "void \fBkdbhSetTrie\fP (KDB *handle, Trie *trie)"
+.br
+.ti -1c
+.RI "const Key * \fBkdbhGetMountpoint\fP (KDB *handle)"
+.br
+.ti -1c
+.RI "void \fBkdbhSetMountpoint\fP (KDB *handle, const Key *mountpoint)"
+.br
+.ti -1c
+.RI "KeySet * \fBkdbhGetConfig\fP (KDB *handle)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to access the backend handle. 
+.PP
+To use them: 
+.PP
+.nf
+ #include <kdb.h>
+
+.fi
+.PP
+.PP
+These functions provide access to the information stored in Backend Handles. 
+.SH "Function Documentation"
+.PP 
+.SS "void* kdbhGetBackendData (const KDB * handle)"
+.PP
+Get the previously set backend-specific \fCdata\fP from the \fChandle\fP.
+.PP
+This is useful when your backend have a backend-global context or environment.
+.PP
+This method will probably be called everytime one of your kdb*() implementations is called. And if you change something inside the data, you don't have to \fBkdbhSetBackendData()\fP again, bacause you are manipulating your data, and not a copy of it.
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+struct MyBackendData {
+ int context1;
+ int context2;
+};
+
+int kdbOpen_mybackend(KDB *handle) {
+        struct MyBackendData *context;
+
+        context=malloc(sizeof(struct MyBackendData));
+        // a random initialization...
+        context->context1=1;
+        context->context2=2;
+
+        kdbhSetBackendData(*handle,context);
+
+        return 0;
+}
+
+int kdbGetKey_maybackend(KDB handle) {
+        struct MyBackendData *context;
+
+        context=kdbhGetBackendData(handle);
+
+        // No do something with the context
+        . . .
+
+        return 0;
+}
+
+.fi
+.PP
+.RE
+.PP
+On the \fBkdbClose()\fP implementation of your backend, you must remember to free all resources associated to your data.
+.PP
+\fBExample of kdbClose() implementation that correctly cleans the context:\fP.RS 4
+
+.PP
+.nf
+int kdbClose_mybackend(KDB &handle) {
+        struct MyBackendData *context;
+
+        context=kdbhGetBackendData(handle);
+        free(context);
+
+        return 0;
+}
+
+.fi
+.PP
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to the data previously set be \fBkdbhSetBackendData()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+
+.SS "KDBCap* kdbhGetCapability (const KDB * handle)"
+.PP
+Gets capability for handle.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+The backend name set in \fChandle\fP. 
+.RE
+.PP
+
+.SS "KeySet* kdbhGetConfig (KDB * handle)"
+.PP
+Returns configuration for handle.
+.PP
+Every backend may have its own configuration using a Keyset.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the keyset containing configuration for a backend 
+.RE
+.PP
+
+.SS "const Key* kdbhGetMountpoint (KDB * handle)"
+.PP
+Gets mountpoint for handle.
+.PP
+Every mounted backend has a specific mountpoint where it is mounted. You may need to know where you were mounted inside a backend to calculate relative pathes.
+.PP
+The \fBkeyName()\fP is where the backend is mounted, keyString() gives the name of which backend is mounted.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbhSetMountpoint()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+The Key containing the mountpoint. 
+.RE
+.PP
+
+.SS "Trie* kdbhGetTrie (const KDB * handle)"
+.PP
+Gets trie for handle.
+.PP
+The trie is a datastructure containing the mounted backends.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbhSetTrie()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+The backend name set in \fChandle\fP. 
+.RE
+.PP
+
+.SS "void* kdbhSetBackendData (KDB * handle, void * data)"
+.PP
+Set some backend-specific \fCdata\fP in the \fChandle\fP.
+.PP
+This is useful when your backend have a backend-global context or environment.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIdata\fP a pointer to general data specific to a backend implementation. 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbhGetBackendData()\fP 
+.RE
+.PP
+
+.SS "KDBCap* kdbhSetCapability (KDB * handle, KDBCap * cap)"
+.PP
+Sets capabilty for handle.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIcap\fP a pointer to capability structure 
+.br
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+The backend name set in \fChandle\fP. 
+.RE
+.PP
+
+.SS "void kdbhSetMountpoint (KDB * handle, const Key * mountpoint)"
+.PP
+Sets mountpoint for handle.
+.PP
+You must not change the mountpoint inside your backend, it was set correctly already for you.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fImountpoint\fP the key containing as name where backend is mounted and as value the backendname 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbhGetMountpoint()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+nothing 
+.RE
+.PP
+
+.SS "void kdbhSetTrie (KDB * handle, Trie * trie)"
+.PP
+Sets trie for handle.
+.PP
+The trie is a datastructure containing the mounted backends. This must not done inside backends, it was set correctly already for you.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fItrie\fP the datastructure referencing to the other handles of backends 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbhGetTrie()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+nothing 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/backendhelper.3 b/doc/elektra-api/man/man3/backendhelper.3
new file mode 100644 (file)
index 0000000..596a625
--- /dev/null
@@ -0,0 +1,433 @@
+.TH "KDB Backends :: Backend Helper for Elektra" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KDB Backends :: Backend Helper for Elektra \- Backend helper Methods for Elektra and Backends.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBkdbbWriteLock\fP (FILE *f)"
+.br
+.ti -1c
+.RI "int \fBkdbbReadLock\fP (FILE *f)"
+.br
+.ti -1c
+.RI "int \fBkdbbUnlock\fP (FILE *f)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbbEncode\fP (void *kdbbDecoded, size_t size, char *returned)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbbDecode\fP (char *kdbbEncoded, void *returned)"
+.br
+.ti -1c
+.RI "int \fBkdbbNeedsUTF8Conversion\fP ()"
+.br
+.ti -1c
+.RI "int \fBkdbbUTF8Engine\fP (int direction, char **string, size_t *inputOutputByteSize)"
+.br
+.ti -1c
+.RI "int \fBkdbbEncodeChar\fP (char c, char *buffer, size_t bufSize)"
+.br
+.ti -1c
+.RI "int \fBkdbbDecodeChar\fP (const char *from, char *into)"
+.br
+.ti -1c
+.RI "int \fBkdbbFilenameToKeyName\fP (const char *string, char *buffer, int bufSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbbGetFullKeyName\fP (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned)"
+.br
+.ti -1c
+.RI "int \fBkdbbKeyNameToRelativeFilename\fP (const char *string, char *buffer, size_t bufSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbbKeyCalcRelativeFilename\fP (const Key *key, char *relativeFilename, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbbGetFullFilename\fP (KDB *handle, const Key *forKey, char *returned, size_t maxSize)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Backend helper Methods for Elektra and Backends. 
+.PP
+To use them: 
+.PP
+.nf
+ #include <kdbbackend.h>
+
+.fi
+.PP
+.PP
+These backend helper methods provide functionality commonly used by backends to make backend development easier and to provide the same behaviour between backends. 
+.SH "Function Documentation"
+.PP 
+.SS "ssize_t kdbbDecode (char * kdbbEncoded, void * returned)"
+.PP
+UnkdbbEncodes a buffer of ASCII hexadecimal values into a byte stream.
+.PP
+The allowed format for the hexadecimal values is just a stream of pairs of plain hex-digits, all together or space-separated.
+.PP
+The \fCreturned\fP data won't be bigger than half the size of the source \fCkdbbEncoded\fP data.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkdbbEncoded\fP the source of ASCII hexadecimal digits. 
+.br
+\fIreturned\fP preallocated destination for the kdbbDecoded data. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the amount of bytes kdbbDecoded 
+.PP
+-1 on failure 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbbEncode()\fP 
+.RE
+.PP
+
+.SS "int kdbbDecodeChar (const char * from, char * into)"
+.PP
+Char decoding.
+.PP
+Decode one char from 25, 2B, 2F, 2C following RFC 2396 or copy char untouched if different.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIfrom\fP String containing sequence to decode 
+.br
+\fIinto\fP Decoded char 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+: Positive size of byte read from 'from' for decoding the sequence if sucess or -1 if error (into untouched)
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbbEncodeChar\fP
+.RE
+.PP
+NOTE: No '\\0' is added at the end of buffer. 
+.SS "ssize_t kdbbEncode (void * kdbbDecoded, size_t size, char * returned)"
+.PP
+Encodes a buffer of data onto hexadecimal ASCII.
+.PP
+The resulting data is made up of pairs of ASCII hex-digits, space- and newline-separated. This is the counterpart of \fBkdbbDecode()\fP.
+.PP
+The \fCreturned\fP must allocated prior you call this function and won't be bigger than 3 times the size of the source \fCkdbbDecoded\fP + 1 byte.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkdbbDecoded\fP the source buffer. 
+.br
+\fIsize\fP the size of the source buffer in bytes. 
+.br
+\fIreturned\fP the preallocated destination for the ASCII-kdbbEncoded data. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the amount of bytes used in the resulting kdbbEncoded buffer. 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbbDecode()\fP 
+.RE
+.PP
+
+.SS "int kdbbEncodeChar (char c, char * buffer, size_t bufSize)"
+.PP
+Char encoding.
+.PP
+Encode '/', '\\', '', '+', ' ' char following RFC 2396 or copy char untouched if different.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIc\fP Char to kdbbEncode 
+.br
+\fIbuffer\fP string wich will contain kdbbEncoded char 
+.br
+\fIbufSize\fP Size of the buffer 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+: Size of the kdbbEncoded string if success or -1 if error * (then buffer is untouched)
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+kdbiDecodeChar
+.RE
+.PP
+NOTE: No '\\0' is added at the end of buffer. 
+.SS "int kdbbFilenameToKeyName (const char * string, char * buffer, int bufSize)"
+.PP
+Translate a relative file name to a key name applying decoding.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIstring\fP Filename 
+.br
+\fIbuffer\fP decoded keyName 
+.br
+\fIbufSize\fP Size of buffer 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbbKeyNameToRelativeFilename\fP 
+.RE
+.PP
+
+.SS "ssize_t kdbbGetFullFilename (KDB * handle, const Key * forKey, char * returned, size_t maxSize)"
+.PP
+Calculate the real file name for a key.
+.PP
+system/ keys will get the prefix KDB_DB_SYSTEM
+.PP
+For the user/ keys the algorithm works as follow: 1.) When the override environment KDB_HOME exists the configuration will be searched below KDB_HOME/KDB_DB_USER 2.) When the owner of the key exists in the elektra user database steps a.) and b.) will be tested: a.) The specific value for configuration storage of the user below system/users/<owner>/kdb b.) The home variable in system/users/<owner>/home will be merged together with KDB_DB_USER 3.) When the environment HOME exists the configuration will be searched below HOME/KDB_DB_USER 4.) Otherwise the KDB_DB_HOME/<owner>/KDB_DB_USER will be used
+.PP
+\fBParameters:\fP
+.RS 4
+\fIforKey\fP the key object to work with 
+.br
+\fIhandle\fP the kdb handle to work with 
+.br
+\fIreturned\fP the buffer to return the calculated filename 
+.br
+\fImaxSize\fP maximum number of bytes that fit the buffer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+kdbCalcRelativeFilename() 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to the buffer, or 0 on error
+.PP
+length of returned string on success 
+.PP
+-1 on failure 
+.RE
+.PP
+
+.SS "ssize_t kdbbGetFullKeyName (KDB * handle, const char * forFilename, const Key * parentKey, Key * returned)"
+.PP
+Calculates the keyname out of a relative filename.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP The kdb handle to work with 
+.br
+\fIforFilename\fP needs to be the a null terminated string containing the relative filename 
+.br
+\fIparentKey\fP is the key above the key which will be returned 
+.br
+\fIreturned\fP The proper keyname and owner will be stored in returned. A valid key must be passed. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to the buffer, or 0 on error
+.PP
+length of returned string on success 
+.PP
+-1 on failure 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbbKeyNameToRelativeFilename()\fP 
+.RE
+.PP
+
+.SS "ssize_t kdbbKeyCalcRelativeFilename (const Key * key, char * relativeFilename, size_t maxSize)"
+.PP
+This is a helper to kdbGetFullFilename()
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP has the relevant name for the relative filename 
+.br
+\fIrelativeFilename\fP the buffer to return the calculated filename 
+.br
+\fImaxSize\fP maximum number of bytes that fit the buffer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+kdbGetFullFilename() 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to the buffer 
+.PP
+-1 on failure 
+.RE
+.PP
+
+.SS "int kdbbKeyNameToRelativeFilename (const char * string, char * buffer, size_t bufSize)"
+.PP
+Translate a key name to a relative file name applying encoding.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIstring\fP Keyname 
+.br
+\fIbuffer\fP kdbbEncoded filename 
+.br
+\fIbufSize\fP Size of buffer 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+Number of byte written in buffer on success, 
+.PP
+-1 on failure
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbbKeyNameToRelativeFilename\fP 
+.RE
+.PP
+
+.SS "int kdbbNeedsUTF8Conversion (void)"
+.PP
+Checks if UTF-8 conversion is needed in current context. if nl_langinfo() is not available, no conversion is ever needed. If iconv usage is disabled there is no need to check if we need to convert. Furthermore, some systems have nl_langinfo(), but lacks ability to get CODESET through it. Look at the comments by the \fBkdbbUTF8Engine()\fP function for more information.
+.PP
+\fBReturns:\fP
+.RS 4
+0 if not needed 
+.PP
+anything else if needed 
+.RE
+.PP
+
+.SS "int kdbbReadLock (FILE * f)"
+.PP
+Locks file for read mode.
+.PP
+Other processes and threads are allowed to read the file too simultaneous.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIf\fP is a valid filedescriptor 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure
+.RE
+.PP
+\fBError\fP
+.RS 4
+sets KDB_ERR_NOLOCK when locking failed 
+.RE
+.PP
+
+.SS "int kdbbUnlock (FILE * f)"
+.PP
+Unlocks file.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIf\fP is a valid filedescriptor 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure
+.RE
+.PP
+\fBError\fP
+.RS 4
+sets KDB_ERR_NOLOCK when locking failed 
+.RE
+.PP
+
+.SS "int kdbbUTF8Engine (int direction, char ** string, size_t * inputOutputByteSize)"
+.PP
+Converts string to (\fCdirection\fP = \fCUTF8_TO\fP) and from (\fCdirection\fP = \fCUTF8_FROM\fP) UTF-8.
+.PP
+Since Elektra provides portability for key names and string values between different codesets, you should use this helper in your backend to convert to and from universal UTF-8 strings, when storing key names, values and comments.
+.PP
+Broken locales in applications can cause problems too. Make sure to load the environment locales in your application using 
+.PP
+.nf
+setlocale (LC_ALL, '');
+
+.fi
+.PP
+.PP
+Otherwise kdbbUTF8Engine will quit with -1 leading that backends return with error when non-ascii characters appear. Binary values are not effected.
+.PP
+If iconv() or nl_langinfo() is not available on your system, or if iconv() usage is disabled (--disable-iconv on build time) simply return 0 immediately.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIdirection\fP must be \fCUTF8_TO\fP (convert from current non-UTF-8 to UTF-8) or \fCUTF8_FROM\fP (convert from UTF-8 to current non-UTF-8) 
+.br
+\fIstring\fP before the call: the string to be converted; after the call: reallocated to carry the converted string 
+.br
+\fIinputOutputByteSize\fP before the call: the size of the string including leading NULL; after the call: the size of the converted string including leading NULL 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure 
+.RE
+.PP
+
+.SS "int kdbbWriteLock (FILE * f)"
+.PP
+Locks file for exclusive write mode.
+.PP
+This function will block until all reader and writer have left the file.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIf\fP is a valid filedescriptor 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure
+.RE
+.PP
+\fBError\fP
+.RS 4
+sets KDB_ERR_NOLOCK when locking failed 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/err.3 b/doc/elektra-api/man/man3/err.3
new file mode 100644 (file)
index 0000000..92d2c72
--- /dev/null
@@ -0,0 +1,26 @@
+.TH "err" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+err \- Error 
+.IP "\fBGlobal \fBkdbbReadLock\fP \fP" 1c
+sets KDB_ERR_NOLOCK when locking failed 
+.PP
+.PP
+.IP "\fBGlobal \fBkdbbUnlock\fP \fP" 1c
+sets KDB_ERR_NOLOCK when locking failed 
+.PP
+.PP
+.IP "\fBGlobal \fBkdbbWriteLock\fP \fP" 1c
+sets KDB_ERR_NOLOCK when locking failed 
+.PP
+.PP
+.IP "\fBGlobal \fBkdbSet_backend\fP \fP" 1c
+In normal execution cases a positive value will be returned. But in some cases you are not able to set keys and have to return -1. If you declare kdbcGetnoError() you are done, but otherwise you have to set the cause of the error. (Will be added with 0.7.1)
+.PP
+.PP
+
diff --git a/doc/elektra-api/man/man3/internal.3 b/doc/elektra-api/man/man3/internal.3
new file mode 100644 (file)
index 0000000..ca4456e
--- /dev/null
@@ -0,0 +1,148 @@
+.TH "KDB Backends :: Internal Helper for Elektra" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KDB Backends :: Internal Helper for Elektra \- Internal Methods for Elektra and Backends.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBkdbiStrCaseCmp\fP (const char *s1, const char *s2)"
+.br
+.ti -1c
+.RI "int \fBkdbiRealloc\fP (void **buffer, size_t size)"
+.br
+.ti -1c
+.RI "void \fBkdbiFree\fP (void *ptr)"
+.br
+.ti -1c
+.RI "char * \fBkdbiStrDup\fP (const char *s)"
+.br
+.ti -1c
+.RI "size_t \fBkdbiStrLen\fP (const char *s)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Internal Methods for Elektra and Backends. 
+.PP
+To use them: 
+.PP
+.nf
+ #include <kdbbackend.h>
+
+.fi
+.PP
+.PP
+There are some areas where libraries have to reimplement some basic functions to archive support for non-standard systems, for testing purposes or to provide a little more convenience. 
+.SH "Function Documentation"
+.PP 
+.SS "void kdbiFree (void * ptr)"
+.PP
+Free memory of elektra or its backends.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIptr\fP the pointer to free
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+kdbiMalloc 
+.RE
+.PP
+
+.SS "int kdbiRealloc (void ** buffer, size_t size)"
+.PP
+Reallocate Storage in a save way.
+.PP
+.PP
+.nf
+if (kdbiRealloc ((void **) & buffer, new_length) < 0) {
+        // here comes the failure handler
+        // you can still use the old buffer
+#if DEBUG
+        fprintf (stderr, 'Reallocation error\n');
+#endif
+        free (buffer);
+        buffer = 0;
+        // return with error
+}
+ *
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIbuffer\fP is a pointer to a malloc 
+.br
+\fIsize\fP is the new size for the memory 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+-1 on failure 
+.PP
+0 on success 
+.RE
+.PP
+
+.SS "int kdbiStrCaseCmp (const char * s1, const char * s2)"
+.PP
+Compare Strings ignoring case.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIs1\fP The first string to be compared 
+.br
+\fIs2\fP The second string to be compared
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a negative number if s1 is less than s2 
+.PP
+0 if s1 matches s2 
+.PP
+a positive number if s1 is greater than s2 
+.RE
+.PP
+
+.SS "char* kdbiStrDup (const char * s)"
+.PP
+Copy string into new allocated memory.
+.PP
+You need to free the memory yourself.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIs\fP the null-terminated string to duplicate
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbiFree\fP 
+.PP
+\fBkdbiStrLen\fP 
+.RE
+.PP
+
+.SS "size_t kdbiStrLen (const char * s)"
+.PP
+Calculates the length in bytes of a string.
+.PP
+This function differs from strlen() because it is Unicode and multibyte chars safe. While strlen() counts characters and ignores the final NULL, \fBkdbiStrLen()\fP count bytes including the ending NULL.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIs\fP the string to get the length from 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes used by the string, including the final NULL. 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/kdb.3 b/doc/elektra-api/man/man3/kdb.3
new file mode 100644 (file)
index 0000000..48c9300
--- /dev/null
@@ -0,0 +1,502 @@
+.TH "KDB :: Low Level Methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KDB :: Low Level Methods \- General methods to access the Key database.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBkdbMount\fP (KDB *handle, const Key *mountpoint, const KeySet *config)"
+.br
+.ti -1c
+.RI "int \fBkdbUnmount\fP (KDB *handle, const Key *mountpoint)"
+.br
+.ti -1c
+.RI "Key * \fBkdbGetMountpoint\fP (KDB *handle, const Key *where)"
+.br
+.ti -1c
+.RI "KDB * \fBkdbOpen\fP ()"
+.br
+.ti -1c
+.RI "int \fBkdbClose\fP (KDB *handle)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbGet\fP (KDB *handle, KeySet *returned, Key *parentKey, option_t options)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbSet\fP (KDB *handle, KeySet *ks, Key *parentKey, option_t options)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+General methods to access the Key database. 
+.PP
+To use them: 
+.PP
+.nf
+ #include <kdb.h>
+
+.fi
+.PP
+.PP
+The kdb*() class of methods are used to access the storage, to get and set \fBKeys \fP or \fBKeySets \fP.
+.PP
+The most important functions are:
+.IP "\(bu" 2
+\fBkdbOpen()\fP
+.IP "\(bu" 2
+\fBkdbClose()\fP
+.IP "\(bu" 2
+\fBkdbGet()\fP
+.IP "\(bu" 2
+\fBkdbSet()\fP
+.PP
+.PP
+The two essential functions for dynamic information about backends are:
+.IP "\(bu" 2
+\fBkdbGetMountpoint()\fP
+.IP "\(bu" 2
+kdbGetCapability()
+.PP
+.PP
+They use some backend implementation to know the details about how to access the storage. Currently we have this backends:
+.IP "\(bu" 2
+\fCberkeleydb:\fP the keys are stored in a Berkeley DB database, providing very small footprint, speed, and other advantages.
+.IP "\(bu" 2
+\fCfilesys:\fP the key hierarchy and data are saved as plain text files in the filesystem.
+.IP "\(bu" 2
+\fCini:\fP the key hierarchy are saved into configuration files. 
+.PP
+\fBSee also:\fP
+.RS 4
+http://www.libelektra.org/Ini
+.RE
+.PP
+
+.IP "\(bu" 2
+\fCfstab:\fP a reference backend used to interpret the \fC/etc/fstab\fP file as a set of keys under \fCsystem/filesystems\fP .
+.IP "\(bu" 2
+\fCgconf:\fP makes Elektra use the GConf daemon to access keys. Only the \fCuser/\fP tree is available since GConf is not system wide.
+.PP
+.PP
+Backends are physically a library named \fC/lib/libelektra-{NAME}\fP.so.
+.PP
+See \fBwriting a new backend \fP for information about how to write a backend.
+.PP
+Language binding writers should follow the same rules:
+.IP "\(bu" 2
+You must relay completely on the backend-dependent methods.
+.IP "\(bu" 2
+You may use or reimplement the second set of methods.
+.IP "\(bu" 2
+You should completely reimplement in your language the higher lever methods.
+.IP "\(bu" 2
+Many methods are just for comfort in C. These methods are marked and need not to be implemented if the binding language has e.g. string operators which can do the operation easily. 
+.PP
+
+.SH "Function Documentation"
+.PP 
+.SS "int kdbClose (KDB * handle)"
+.PP
+Closes the session with the Key database.
+.PP
+You should call this method when you finished your affairs with the key database. You can manipulate Key and KeySet objects also after \fBkdbClose()\fP. You must not use any kdb* call afterwards. You can implement \fBkdbClose()\fP in the atexit() handler.
+.PP
+This is the counterpart of \fBkdbOpen()\fP.
+.PP
+The \fChandle\fP parameter will be finalized and all resources associated to it will be freed. After a \fBkdbClose()\fP, this \fChandle\fP can't be used anymore, unless it gets initialized again with another call to \fBkdbOpen()\fP.
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbOpen()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
+.SS "ssize_t kdbGet (KDB * handle, KeySet * returned, Key * parentKey, option_t options)"
+.PP
+Retrieve keys in an atomic and universal way, all other kdbGet Functions rely on that one.
+.PP
+The \fCreturned\fP KeySet must be initialized or may already contain some keys. The new retrieved keys will be appended using \fBksAppendKey()\fP.
+.PP
+In default behaviour (\fCoptions\fP = 0) it will fully retrieve all keys under the \fCparentKey\fP folder, with all subfolders and their children but not inactive keys or folders.
+.PP
+The keyset will not be sorted at first place, but will be marked dirty and sorted afterwards when needed. That could be a subsequent \fBksLookup()\fP, \fBksLookupByName()\fP or \fBkdbSet()\fP. See \fBksSort()\fP on that issue.
+.PP
+The behaviour can be fine-tuned with options in various ways to make \fBkdbGet()\fP more comfortable.
+.SH "Options"
+.PP
+The \fCoption\fP is an array of the following ORed flags:
+.PP
+.IP "\(bu" 2
+\fCoption_t::KDB_O_DEL\fP 
+.br
+ Its often useful to \fBkeyDel()\fP the parentKey in the line after \fBkdbGet()\fP. Using this flag, you can just pass a key allocated with \fBkeyNew()\fP, \fBkdbGet()\fP will free it for you in the end.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_POP\fP 
+.br
+ The \fCparentKey\fP itself will always be added to \fCreturned\fP. If you only want the children of the parentKey in \fCreturned\fP, but not the parentKey itself, use this flag. This is only valid for the first parentKey, the one you passed. The other recursive parentKeys will stay in the keyset. To get only the leaves of the tree, without any parentKey, see \fBoption_t::KDB_O_NODIR\fP below.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NODIR\fP 
+.br
+ Don't include folders in the \fCreturned\fP KeySet, so only keys without subkeys. You can picture it best that you only get the leaves of the tree of keys.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_DIRONLY\fP 
+.br
+ Put in \fCreturned\fP only the folder keys. The resulting KeySet will be only the skeleton of the tree. This option must not be ORed together with KDB_O_DIR.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NOSTAT\fP 
+.br
+ Don't stat they keys, whatever \fBkeyNeedStat()\fP says. That means that also the key value and comment will be retrieved. The flag will result in that all keys in \fCreturned\fP don't have \fBkeyNeedStat()\fP set.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_STATONLY\fP 
+.br
+ Only stat the keys. It means that key value and comment will not be retrieved. The resulting keys will contain only meta info such as user and group IDs, owner, mode permissions and modification times. You don't need that flag if the keys already have \fBkeyNeedStat()\fP set. The flag will result in that all keys in \fCreturned\fP have \fBkeyNeedStat()\fP set.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_INACTIVE\fP 
+.br
+ Will make it not ignore inactive keys, so \fCreturned\fP will contain also inactive keys. Inactive keys are those that have names begining with '.' (dot). Please be sure that you know what you are doing, inactive keys must not have any semantics to the application. This flag should only be set in key browsers after explicit user request. You might also get inactive keys when you plan to remove a whole hierarchy.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_SORT\fP 
+.br
+ Force \fCreturned\fP to be \fBksSort()\fPed. Normally you don't want that the \fCreturned\fP is sorted immediately because you might add other keys or go for another \fBkdbGet()\fP. Sorting will take place automatically when needed by \fBksLookup()\fP or \fBkdbSet()\fP, also without this option set. But you need to sort the keyset for yourself, when you just iterate over it. If you want to do that, pass this flag at the last \fBkdbGet()\fP.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NORECURSIVE\fP 
+.br
+ Dont get the keys recursive. Only receive keys from one folder. This might not work if the backend does not support it. Be prepared for more keys and use \fBksLookup()\fP and avoid static assumptions on how many keys you get.
+.PP
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+KDB *handle;
+KeySet *myConfig;
+Key *key;
+
+myConfig=ksNew(0);
+
+handle = kdbOpen();
+
+key=keyNew('system/sw/MyApp',KEY_END);
+rc=kdbGet(handle,key, myConfig, 0);
+keyDel(key);
+
+key=keyNew('user/sw/MyApp',KEY_END);
+rc=kdbGet(handle,key, myConfig, 0);
+keyDel(key);
+
+// will sort keyset here
+key=ksLookupByName(myConfig,'/sw/MyApp/key', 0);
+// check if key is not 0 and work with it...
+
+ksDel (myConfig); // delete the in-memory configuration
+
+
+// maybe you want kdbSet() myConfig here
+
+kdbClose(handle); // no more affairs with the key database.
+
+.fi
+.PP
+.RE
+.PP
+.SH "Details"
+.PP
+When no backend could be found (e.g. no backend mounted) the default backend will be used.
+.PP
+If you pass a NULL pointer as handle and/or returned \fBkdbGet()\fP will return -1 and do nothing but \fBkeyDel()\fP the parentKey when requested and not a NULL pointer.
+.PP
+If you pass NULL as parentKey the root keys of all namespaces will be appended to returned.
+.PP
+For every directory key (\fBkeyIsDir()\fP) the appropriate backend will be chosen and keys in it will be requested.
+.PP
+If any backend reports an failure the recursive getting of keys will be stopped. Backends only report failure when they are not able to get keys for any problems.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIparentKey\fP parent key or NULL to get the root keys 
+.br
+\fIreturned\fP the (pre-initialized) KeySet returned with all keys found 
+.br
+\fIoptions\fP ORed options to control approaches 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+option_t 
+.PP
+\fBkdb higher level Methods \fP that rely on \fBkdbGet()\fP 
+.PP
+\fBksLookupByName()\fP, ksLookupByString() for powerful lookups after the KeySet was retrieved 
+.PP
+commandList() code in \fBKDB :: Low Level Methods\fP command for usage example 
+.PP
+commandEdit() code in \fBKDB :: Low Level Methods\fP command for usage example 
+.PP
+commandExport() code in \fBKDB :: Low Level Methods\fP command for usage example 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of keys contained by \fCreturned\fP 
+.PP
+-1 on failure 
+.RE
+.PP
+
+.SS "Key* kdbGetMountpoint (KDB * handle, const Key * where)"
+.PP
+Lookup a mountpoint in a handle for a specific key.
+.PP
+Will return a key representing the mountpoint or null if there is no appropriate mountpoint e.g. its the root mountpoint.
+.PP
+Together with kdbGetCapability() the two essential informations about mounted backends.
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+Key * key = keyNew ('system/template');
+KDB * handle = kdbOpen();
+Key *mountpoint=0;
+mountpoint=kdbGetMountpoint(handle, key);
+
+printf('The library I am using is %s mounted in %s\n',
+        keyValue(mountpoint),
+        keyName(mountpoint));
+kdbClose (handle);
+keyDel (key);
+
+.fi
+.PP
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP is the data structure, where the mounted directories are saved. 
+.br
+\fIwhere\fP the key, that should be looked up. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the mountpoint associated with the key 
+.RE
+.PP
+
+.SS "int kdbMount (KDB * handle, const Key * mountpoint, const KeySet * config)"
+.PP
+Dynamically mount a single backend.
+.PP
+Maps the mountpoint, defined through its name and value, into the global elektra hierachy. If successfull, under the mountpoint another backend will reside.
+.PP
+This only works for a single KDB, that means a single thread in a single process. You may want statically mounting by editing system/elektra/mountpoints.
+.PP
+If you allocated mountpoint and config first, make sure that you free it! It is ok to free it immediately afterwards.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP handle to the kdb data structure 
+.br
+\fImountpoint\fP the \fBkeyName()\fP of this key is the mountpoint, \fBkeyValue()\fP the backend 
+.br
+\fIconfig\fP the configuration passed for that backend 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success, -1 if an error occurred 
+.RE
+.PP
+
+.SS "KDB* kdbOpen (void)"
+.PP
+Opens the session with the Key database.
+.PP
+The first step is to open the default backend. With it system/elektra/mountpoints will be loaded and all needed libraries and mountpoints will be determined. These libraries for backends will be loaded and with it the \fCKDB\fP datastructure will be initialized.
+.PP
+You must always call this method before retrieving or commiting any keys to the database. In the end of the program, after using the key database, you must not forget to \fBkdbClose()\fP. You can use the atexit () handler for it.
+.PP
+The pointer to the \fCKDB\fP structure returned will be initialized like described above, and it must be passed along on any kdb*() method your application calls.
+.PP
+Get a \fCKDB\fP handle for every thread using elektra. Don't share the handle across threads, and also not the pointer accessing it: 
+.PP
+.nf
+thread1 {
+        KDB * h;
+        h = kdbOpen();
+        // fetch keys and work with them
+        kdbClose(h);
+}
+thread2 {
+        KDB * h;
+        h = kdbOpen();
+        // fetch keys and work with them
+        kdbClose(h);
+}
+
+.fi
+.PP
+.PP
+You don't need to use the \fBkdbOpen()\fP if you only want to manipulate plain in-memory Key or KeySet objects without any affairs with the backend key database,
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbClose()\fP to end all affairs to the \fBKey :: Basic Methods\fP database. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a KDB pointer on success 
+.PP
+NULL on failure 
+.RE
+.PP
+
+.SS "ssize_t kdbSet (KDB * handle, KeySet * ks, Key * parentKey, option_t options)"
+.PP
+Set keys in an atomic and universal way, all other kdbSet Functions rely on that one.
+.PP
+The given handle and keyset are the objects to work with.
+.PP
+With parentKey you can only store a part of the given keyset. Otherwise pass a null pointer or a parentKey without a name.
+.PP
+.PP
+.nf
+KeySet *ks = ksNew(0);
+kdbGet (h, ks, keyNew('system/myapp',0), KDB_O_DEL);
+kdbGet (h, ks, keyNew('user/myapp',0), KDB_O_DEL);
+
+//now only set everything below user, because you can't write to system
+kdbSet (h, ks, keyNew('user/myapp',0), KDB_O_DEL);
+
+ksDel (ks);
+.fi
+.PP
+.PP
+Each key is checked with \fBkeyNeedSync()\fP before being actually committed. So only changed keys are updated. If no key of a backend needs to be synced the \fBkdbSet_backend()\fP will be omitted.
+.PP
+If some error occurs, \fBkdbSet()\fP will stop. In this situation the KeySet internal cursor will be set on the key that generated the error. This specific key and all behind it were not set. To be failsafe jump over it and try to set the rest, but report the error to the user.
+.PP
+\fBExample of how this method can be used:\fP.RS 4
+
+.PP
+.nf
+int i;
+KeySet *ks;  // the KeySet I want to set
+// fill ks with some keys
+for (i=0; i< 10; i++) // limit to 10 tries
+{
+        ret=kdbSet(handle,ks, 0, 0);
+        if (ret == -1)
+        {
+                // We got an error. Warn user.
+                Key *problem;
+                problem=ksCurrent(ks);
+                if (problem)
+                {
+                        char keyname[300]='';
+                        keyGetFullName(problem,keyname,sizeof(keyname));
+                        fprintf(stderr,'kdb import: while importing %s', keyname);
+                } else break;
+                // And try to set keys again starting from the next key,
+                // unless we reached the end of KeySet
+                if (ksNext(ks) == 0) break;
+        }
+}
+
+.fi
+.PP
+.RE
+.PP
+.SH "Options"
+.PP
+There are some options changing the behaviour of \fBkdbSet()\fP:
+.PP
+.IP "\(bu" 2
+\fCoption_t::KDB_O_DEL\fP 
+.br
+ Its often useful to \fBkeyDel()\fP the parentKey in the line after \fBkdbGet()\fP. Using this flag, you can just pass a key allocated with \fBkeyNew()\fP, \fBkdbGet()\fP will free it for you in the end.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_SYNC\fP 
+.br
+ Will force to save all keys, independent of their sync state.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NOREMOVE\fP 
+.br
+ Don't remove any key from disk, even if \fBkeyRemove()\fP was set. With that flag removing keys can't happen unintentional. The flag will result in that all keys in \fCreturned\fP don't have \fBkeyNeedRemove()\fP set.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_REMOVEONLY\fP 
+.br
+ Remove all keys instead of setting them. All keys in \fCreturned\fP will have \fBkeyNeedRemove()\fP set, but not \fBkeyNeedStat()\fP saying to you that the key was deleted permanently. This option implicit also activates \fCoption_t::KDB_O_SYNC\fP because the sync state will be changed when they are marked remove. You might need \fBoption_t::KDB_O_INACTIVE\fP set for the previous call of \fBkdbGet()\fP if there are any. Otherwise the recursive remove will fail, because removing directories is only possible when all subkeys are removed.
+.PP
+.SH "Details"
+.PP
+When you dont have a parentKey or its name empty, then all keys will be set.
+.PP
+You can remove some keys instead of setting them by marking them with \fBkeyRemove()\fP. The \fBkeyNeedSync()\fP flag will be unset after successful removing. But the \fBkeyNeedRemove()\fP flag will stay, but its safe to delete the key.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIks\fP a KeySet which should contain changed keys, otherwise nothing is done 
+.br
+\fIparentKey\fP holds the information below which key keys should be set 
+.br
+\fIoptions\fP see in \fBkdbSet()\fP documentation 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyNeedSync()\fP, \fBksNext()\fP, \fBksCurrent()\fP 
+.PP
+\fBkeyRemove()\fP, \fBkeyNeedRemove()\fP 
+.PP
+commandEdit(), commandImport() code in \fBKDB :: Low Level Methods\fP command for usage and error handling example 
+.RE
+.PP
+
+.SS "int kdbUnmount (KDB * handle, const Key * mountpoint)"
+.PP
+Dynamically unmount a single backend.
+.PP
+Unmount a backend that was mounted with \fBkdbMount()\fP before.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP handle to the kdb data structure 
+.br
+\fImountpoint\fP directory where backend is mounted to, that should be unmounted 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success, -1 if an error ocurred. 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/kdbhighlevel.3 b/doc/elektra-api/man/man3/kdbhighlevel.3
new file mode 100644 (file)
index 0000000..fba8f10
--- /dev/null
@@ -0,0 +1,293 @@
+.TH "KDB :: High Level methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KDB :: High Level methods \- High level methods to access the Key database.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBkdbGetKey\fP (KDB *handle, Key *dest)"
+.br
+.ti -1c
+.RI "int \fBkdbSetKey\fP (KDB *handle, const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkdbGetString\fP (KDB *handle, const char *keyname, char *returned, size_t maxSize)"
+.br
+.ti -1c
+.RI "int \fBkdbSetString\fP (KDB *handle, const char *keyname, const char *value)"
+.br
+.ti -1c
+.RI "int \fBkdbRemove\fP (KDB *handle, const char *keyname)"
+.br
+.ti -1c
+.RI "ssize_t \fBkdbGetByName\fP (KDB *handle, KeySet *returned, const char *name, option_t options)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+High level methods to access the Key database. 
+.PP
+To use them: 
+.PP
+.nf
+ #include <kdb.h>
+
+.fi
+.PP
+.PP
+These methods are higher level. They use \fBkdbOpen()\fP, \fBkdbClose()\fP, \fBkdbGet()\fP and \fBkdbSet()\fP methods to do their job, and don't have to be reimplemented for a different backend.
+.PP
+These functions avoid limitations through not implemented capabilities. This will of course cost some effort, so read through the description carefully and decide if it is appropriate for your problem.
+.PP
+Binding writers don't have to implement these functions, use features of the binding language instead. But you can use these functions as ideas what high level methods may be useful.
+.PP
+Don't use writing single keys in a loop, prefer always writing out a keyset! 
+.SH "Function Documentation"
+.PP 
+.SS "ssize_t kdbGetByName (KDB * handle, KeySet * returned, const char * name, option_t options)"
+.PP
+This method is similar \fBkdbGet()\fP but the path is given by a string.
+.PP
+When it is not possible to make a key out of that string -1 is returned .
+.PP
+When parentName starts with / cascading will be used and both keys from user and system will be fetched.
+.PP
+A typically app with about 3000 keys may have this line:
+.PP
+.PP
+.nf
+KDB *handle = kdbOpen();
+KeySet *myConfig = (4096, KS_END);
+ssize_t ret = kdbGetByName (handle, myConfig, '/sw/app/current', 0);
+
+// check ret and work with keyset myConfig
+
+ksDel (myConfig);
+kdbClose (handle);
+ *
+.fi
+.PP
+.PP
+myConfig will be loaded with keys from system/sw/app/current but also user/sw/app/current.
+.PP
+When one of these \fBkdbGet()\fP fails -1 will be returned, but the other \fBkdbGet()\fP will be tried too.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIname\fP the name where to get the keys below 
+.br
+\fIreturned\fP the (pre-initialized) KeySet returned with all keys found 
+.br
+\fIoptions\fP ORed options to control approaches Unlike to \fBkdbGet()\fP is KDB_O_POP set per default. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of keys contained by \fCreturned\fP 
+.PP
+-1 on failure 
+.PP
+-1 when \fCname\fP is no valid key 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbGet()\fP 
+.RE
+.PP
+
+.SS "int kdbGetKey (KDB * handle, Key * dest)"
+.PP
+Fully retrieves the passed \fCkey\fP from the backend storage.
+.PP
+The backend will try to get the key, identified through its name.
+.PP
+It uses \fBkdbGet()\fP for retrieving the key and copies the found data to dest.
+.PP
+While \fBkdbGetKey()\fP is perfect for a simple get of a specific key, \fBkdbGet()\fP and \fBkdbGetByName()\fP gives you more control over the keyset.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIdest\fP a pointer to a Key that has a name set 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbSetKey()\fP to set a single \fBKey :: Basic Methods\fP 
+.PP
+commandGet() code in \fBKDB :: Low Level Methods\fP command for usage example 
+.PP
+\fBkdbGet()\fP and \fBkdbGetByName()\fP to have more control over \fBKeySet :: Class Methods\fP and options 
+.RE
+.PP
+
+.SS "int kdbGetString (KDB * handle, const char * keyname, char * returned, size_t maxSize)"
+.PP
+A high-level method to get a key value, by key name.
+.PP
+This method gets a backend from any backend with \fBkdbGetKey()\fP and extracts the string and store it into returned. It only works with string keys.
+.PP
+This method gives you the direct relation between a keyname and the value, without any kdb specific structures. Use it when you just want some values out of the kdb namespace.
+.PP
+You need to know the maximum string length of the object. That could be the case when you e.g. save a path which is limited with MAX_PATH.
+.PP
+.PP
+.nf
+KDB *handle = kdbOpen();
+char buffer [MAX_PATH];
+
+if (kdbGetString(handle, 'user/key/to/get/pathname', buffer, sizeof(buffer)) == -1)
+{
+        // handle error cases
+} else {
+        printf ('The keys value is %s\n', buffer);
+}
+kdbClose(handle);
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIkeyname\fP the name of the key to receive the value 
+.br
+\fIreturned\fP a buffer to put the key value 
+.br
+\fImaxSize\fP the size of the buffer 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure 
+.PP
+-1 on NULL pointers 
+.PP
+-1 if maxSize is 0 or larger than SSIZE_MAX 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbSetString()\fP and \fBkdbRemove()\fP to set and remove a string 
+.PP
+\fBkdbGetKey()\fP, keySetKey() to work with Keys 
+.PP
+\fBkdbGet()\fP and \fBkdbGetByName()\fP for full access to \fBKDB Backends :: Internal Helper for Elektra\fP datastructures 
+.RE
+.PP
+
+.SS "int kdbRemove (KDB * handle, const char * keyname)"
+.PP
+Remove a key by its name from the backend storage.
+.PP
+With \fBkdbSetString()\fP its only possible to set a key with an empty string. To really remove a key in a highlevel way you can use this method.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIkeyname\fP the name of the key to be removed 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure 
+.PP
+-1 on NULL pointers 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+together with \fBkdbSetString()\fP and \fBkdbGetString()\fP a highlevel interface for \fBKDB :: Low Level Methods\fP 
+.PP
+commandRemove() code in \fBKDB :: Low Level Methods\fP command for usage example 
+.RE
+.PP
+
+.SS "int kdbSetKey (KDB * handle, const Key * key)"
+.PP
+Sets \fCkey\fP in the backend storage.
+.PP
+While \fBkdbSetKey()\fP is perfect for a simple get of a specific key, \fBkdbGet()\fP and \fBkdbGetByName()\fP gives you more control over the keyset.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIkey\fP Key to set 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on failure 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbGetKey()\fP to get a single \fBKey :: Basic Methods\fP 
+.PP
+\fBkdbSet()\fP for more control over \fBKeySet :: Class Methods\fP and options 
+.PP
+commandSet() code in \fBKDB :: Low Level Methods\fP command for usage example 
+.RE
+.PP
+
+.SS "int kdbSetString (KDB * handle, const char * keyname, const char * value)"
+.PP
+A high-level method to set a value to a key, by key name.
+.PP
+It will check if key exists first, and keep its metadata. So you'll not loose the previous key comment.
+.PP
+This will set a text key. So if the key was previously a binary it will be retyped as string.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIhandle\fP contains internal information of \fBopened \fP key database 
+.br
+\fIkeyname\fP the name of the key to receive the value 
+.br
+\fIvalue\fP the value to be set 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL pointers 
+.PP
+-1 on failure 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbGetString()\fP, \fBkeySetString()\fP, \fBkdbSetKey()\fP 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/key.3 b/doc/elektra-api/man/man3/key.3
new file mode 100644 (file)
index 0000000..e6efe7c
--- /dev/null
@@ -0,0 +1,521 @@
+.TH "Key :: Basic Methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+Key :: Basic Methods \- Key construction and initialization methods.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "Key * \fBkeyNew\fP (const char *keyName,...)"
+.br
+.ti -1c
+.RI "Key * \fBkeyDup\fP (const Key *source)"
+.br
+.ti -1c
+.RI "int \fBkeyCopy\fP (Key *dest, const Key *source)"
+.br
+.ti -1c
+.RI "int \fBkeyDel\fP (Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyIncRef\fP (Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyDecRef\fP (Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetRef\fP (const Key *key)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Key construction and initialization methods. 
+.PP
+To use them: 
+.PP
+.nf
+#include <kdb.h>
+
+.fi
+.PP
+.PP
+A Key is the essential class that encapsulates key \fBname \fP, \fBvalue \fP and \fBmetainfo \fP. Key properties are:
+.IP "\(bu" 2
+\fBKey name \fP
+.IP "\(bu" 2
+\fBKey value \fP
+.IP "\(bu" 2
+\fBData type \fP
+.IP "\(bu" 2
+\fBKey comment \fP
+.IP "\(bu" 2
+\fBKey owner \fP
+.IP "\(bu" 2
+\fBUID, GID and filesystem-like mode permissions \fP
+.IP "\(bu" 2
+\fBMode, change and modification times \fP
+.PP
+.PP
+Described here the methods to allocate and free the key. 
+.SH "Function Documentation"
+.PP 
+.SS "int keyCopy (Key * dest, const Key * source)"
+.PP
+Copy or Clear a key.
+.PP
+Most often you may prefer \fBkeyDup()\fP which allocates a new key and returns a duplication of another key.
+.PP
+But when you need to copy into an existing key, e.g. because it was passed by a pointer in a function you can do so:
+.PP
+.PP
+.nf
+int h (Key *k)
+{
+        // receive key c
+        keyCopy (k, c);
+        // the caller will see the changed key k
+}
+.fi
+.PP
+.PP
+The reference counter will not change for the destination key. Affiliation to keysets are also not affected.
+.PP
+When you pass a NULL-pointer as source the data of dest will be cleaned completely and you get a fresh dest key.
+.PP
+.PP
+.nf
+int g (Key *k)
+{
+        keyCopy (k, 0);
+        // k is now an empty and fresh key
+}
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIdest\fP the key which will be written to 
+.br
+\fIsource\fP the key which should be copied or NULL to clean the destination key
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+-1 on failure when a NULL pointer was passed for dest or a dynamic property could not be written. 
+.PP
+0 when dest was cleaned 
+.PP
+1 when source was successfully copied 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyDup()\fP to get a duplication of a \fBKey :: Basic Methods\fP 
+.RE
+.PP
+
+.SS "ssize_t keyDecRef (Key * key)"
+.PP
+Decrement the viability of a key object.
+.PP
+The reference counter can't be decremented once it reached 0. In that situation nothing will happen and 0 will be returned.
+.PP
+\fBReturns:\fP
+.RS 4
+the value of the new reference counter 
+.PP
+-1 on null pointer 
+.PP
+0 when the key is ready to be freed 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetRef()\fP, \fBkeyDel()\fP, \fBkeyIncRef()\fP 
+.RE
+.PP
+
+.SS "int keyDel (Key * key)"
+.PP
+A destructor for Key objects.
+.PP
+Every key created by \fBkeyNew()\fP must be deleted with \fBkeyDel()\fP.
+.PP
+It is save to delete keys which are in a keyset, the number of references will be returned then.
+.PP
+It is save to delete a nullpointer, -1 will be returned then.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to delete 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyNew()\fP, keyInc(), \fBkeyGetRef()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the value of the reference counter if the key is within keyset(s) 
+.PP
+0 when the key was freed 
+.PP
+-1 on null pointers 
+.RE
+.PP
+
+.SS "Key* keyDup (const Key * source)"
+.PP
+Return a duplicate of a key.
+.PP
+Memory will be allocated as needed for dynamic properties.
+.PP
+The new key will not be member of any KeySet and will start with a new reference counter at 0. A subsequent \fBkeyDel()\fP will delete the key.
+.PP
+.PP
+.nf
+int f (const Key * source)
+{
+        Key * dup = keyDup (source);
+        // work with duplicate
+        keyDel (dup);
+        // everything related to dup is freed
+        // and source is unchanged
+}
+.fi
+.PP
+.PP
+Like for a new key after \fBkeyNew()\fP a subsequent \fBksAppend()\fP makes a KeySet to take care of the lifecycle of the key.
+.PP
+.PP
+.nf
+int g (const Key * source, KeySet * ks)
+{
+        Key * dup = keyDup (source);
+        // work with duplicate
+        ksAppendKey (ks, dup);
+        // ksDel(ks) will also free the duplicate
+        // source remains unchanged.
+}
+.fi
+.PP
+.PP
+Duplication of keys should be preferred to \fBkeyNew()\fP, because data like owner can be filled with a copy of the key instead of asking the environment. It can also be optimized in the checks, because the keyname is known to be valid.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIsource\fP has to be an initializised source Key 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 failure or on NULL pointer 
+.PP
+a fully copy of source on success 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksAppend()\fP, \fBkeyDel()\fP 
+.PP
+keyClear(), \fBkeyNew()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetRef (const Key * key)"
+.PP
+Return how many references the key has.
+.PP
+The references will be incremented when \fBksAppendKey()\fP or \fBksAppend()\fP uses the key and will be decremented when \fBksPop()\fP is used.
+.PP
+\fBkeyDup()\fP will reset the references for dupped key.
+.PP
+For your own applications you can use \fBkeyIncRef()\fP and keyDelRef() for reference counting. Keys with zero references will be deleted when using \fBkeyDel()\fP.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of references 
+.PP
+-1 on null pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyIncRef()\fP and \fBkeyDecRef()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyIncRef (Key * key)"
+.PP
+Increment the viability of a key object.
+.PP
+This function is intended for applications using their own reference counter for key objects. With it you can increment the reference and thus avoid destruction of the object in a subsequent \fBkeyDel()\fP.
+.PP
+.PP
+.nf
+Key *k;
+keyInc (k);
+function_that_keyDec(k);
+// work with k
+keyDel (k); // now really free it
+.fi
+.PP
+.PP
+The reference counter can't be incremented once it reached SSIZE_MAX. In that situation nothing will happen and SSIZE_MAX will be returned.
+.PP
+\fBReturns:\fP
+.RS 4
+the value of the new reference counter 
+.PP
+-1 on null pointer 
+.PP
+SSIZE_MAX when maximum exceeded 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetRef()\fP, \fBkeyDecRef()\fP, \fBkeyDel()\fP 
+.RE
+.PP
+
+.SS "Key* keyNew (const char * keyName,  ...)"
+.PP
+A practical way to fully create a Key object in one step.
+.PP
+This function tries to mimic the C++ way for constructors.
+.PP
+To just get a key object, simple do: 
+.PP
+.nf
+Key *k = keyNew(0);
+// work with it
+keyDel (k);
+
+.fi
+.PP
+.PP
+If you want the key object to contain a name, value, comment and other meta info read on.
+.PP
+\fBNote:\fP
+.RS 4
+When you already have a key with similar properties its easier and cheaper to \fBkeyDup()\fP the key.
+.RE
+.PP
+Due to ABI compatibility, the \fCKey\fP structure is not defined in kdb.h, only declared. So you can only declare \fCpointers\fP to \fCKeys\fP in your program, and allocate and free memory for them with \fBkeyNew()\fP and \fBkeyDel()\fP respectively. See http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135
+.PP
+You can call it in many different ways depending on the attribute tags you pass as parameters. Tags are represented as the keyswitch_t values, and tell \fBkeyNew()\fP which Key attribute comes next.
+.PP
+The simplest and minimum way to use it is with no tags, only a key name: 
+.PP
+.nf
+Key *nullKey,*emptyNamedKey;
+
+// Create a key that has no name, is completely empty, but is initialized
+nullKey=keyNew(0);
+keyDel (nullKey);
+
+// Is the same as above
+nullKey=keyNew('', KEY_END);
+keyDel (nullKey);
+
+// Create and initialize a key with a name and nothing else
+emptyNamedKey=keyNew('user/some/example',KEY_END);
+keyDel (emptyNamedKey);
+
+.fi
+.PP
+.PP
+\fBkeyNew()\fP allocates memory for a key object and cleans everything up. After that, it processes the given argument list.
+.PP
+The Key attribute tags are the following:
+.IP "\(bu" 2
+keyswitch_t::KEY_TYPE 
+.br
+ Next parameter is a type of the value. Default assumed is KEY_TYPE_UNDEFINED. Set this attribute so that a subsequent KEY_VALUE can toggle to \fBkeySetString()\fP or \fBkeySetBinary()\fP regarding to \fBkeyIsString()\fP or \fBkeyIsBinary()\fP. If you don't use KEY_TYPE but a KEY_VALUE follows afterwards, KEY_TYPE_STRING will be used.
+.IP "\(bu" 2
+keyswitch_t::KEY_SIZE 
+.br
+ Define a maximum length of the value. This is especially useful for setting a binary key. So make sure you use that before you KEY_VALUE for binary keys.
+.IP "\(bu" 2
+keyswitch_t::KEY_VALUE 
+.br
+ Next parameter is a pointer to the value that will be set to the key If no keyswitch_t::KEY_TYPE was used before, keyswitch_t::KEY_TYPE_STRING is assumed. If KEY_TYPE was previously passed with a KEY_TYPE_BINARY, you should have passed KEY_SIZE before! Otherwise it will be cut of with first \\0 in string!
+.IP "\(bu" 2
+keyswitch_t::KEY_UID, \fCkeyswitch_t::KEY_GID\fP 
+.br
+ Next parameter is taken as the UID (uid_t) or GID (gid_t) that will be defined on the key. See \fBkeySetUID()\fP and \fBkeySetGID()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_MODE 
+.br
+ Next parameter is taken as mode permissions (mode_t) to the key. See \fBkeySetMode()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_DIR 
+.br
+ Define that the key is a directory rather than a ordinary key. This means its executable bits in its mode are set. This option allows the key to have subkeys. See \fBkeySetDir()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_OWNER 
+.br
+ Next parameter is the owner. See \fBkeySetOwner()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_COMMENT 
+.br
+ Next parameter is a comment. See \fBkeySetComment()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_REMOVE 
+.br
+ Mark the key to be removed instead of set it. See \fBkeyRemove()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_STAT 
+.br
+ Mark the key to be stated instead of get it. See \fBkeyStat()\fP.
+.IP "\(bu" 2
+keyswitch_t::KEY_END 
+.br
+ Must be the last parameter passed to \fBkeyNew()\fP. It is always required, unless the \fCkeyName\fP is 0.
+.PP
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+KeySet *ks=ksNew(0);
+
+ksAppendKey(ks,keyNew(0));       // an empty key
+
+ksAppendKey(ks,keyNew('user/sw',              // the name of the key
+        KEY_END));                      // no more args
+
+ksAppendKey(ks,keyNew('user/tmp/ex1',
+        KEY_VALUE,'some data',          // set a string value
+        KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew('user/tmp/ex2',
+        KEY_VALUE,'some data',          // with a simple value
+        KEY_MODE,0777,                  // permissions
+        KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew('user/tmp/ex4',
+        KEY_TYPE,KEY_TYPE_BINARY,       // key type
+        KEY_SIZE,7,                     // assume binary length 7
+        KEY_VALUE,'some data',          // value that will be truncated in 7 bytes
+        KEY_COMMENT,'value is truncated',
+        KEY_OWNER,'root',               // owner (not uid) is root
+        KEY_UID,0,                      // root uid
+        KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew('user/tmp/ex5',
+        KEY_TYPE,
+                KEY_TYPE_DIR | KEY_TYPE_BINARY,// dir key with a binary value
+        KEY_SIZE,7,
+        KEY_VALUE,'some data',          // value that will be truncated in 7 bytes
+        KEY_COMMENT,'value is truncated',
+        KEY_OWNER,'root',               // owner (not uid) is root
+        KEY_UID,0,                      // root uid
+        KEY_END));                      // end of args
+
+ksDel(ks);
+
+.fi
+.PP
+.RE
+.PP
+The reference counter (see \fBkeyGetRef()\fP) will be initialized with 0, that means a subsequent call of \fBkeyDel()\fP will delete the key. If you append the key to a keyset the reference counter will be incremented by one (see keyInc()) and the key can't be be deleted by a \fBkeyDel()\fP.
+.PP
+.PP
+.nf
+Key *k = keyNew(0); // ref counter 0
+ksAppendKey(ks, k); // ref counter of key 1
+ksDel(ks); // key will be deleted with keyset
+ *
+.fi
+.PP
+.PP
+If you increment only by one with keyInc() the same as said above is valid:
+.PP
+.PP
+.nf
+Key *k = keyNew(0); // ref counter 0
+keyIncRef(k); // ref counter of key 1
+keyDel(k);    // has no effect
+keyDecRef(k); // ref counter back to 0
+keyDel(k);    // key is now deleted
+ *
+.fi
+.PP
+.PP
+If you add the key to more keySets:
+.PP
+.PP
+.nf
+Key *k = keyNew(0); // ref counter 0
+ksAppendKey(ks1, k); // ref counter of key 1
+ksAppendKey(ks2, k); // ref counter of key 2
+ksDel(ks1); // ref counter of key 1
+ksDel(ks2); // k is now deleted
+ *
+.fi
+.PP
+.PP
+or use keyInc() more than once:
+.PP
+.PP
+.nf
+Key *k = keyNew(0); // ref counter 0
+keyIncRef(k); // ref counter of key 1
+keyDel (k);   // has no effect
+keyIncRef(k); // ref counter of key 2
+keyDel (k);   // has no effect
+keyDecRef(k); // ref counter of key 1
+keyDel (k);   // has no effect
+keyDecRef(k); // ref counter is now 0
+keyDel (k); // k is now deleted
+ *
+.fi
+.PP
+.PP
+they key won't be deleted by a \fBkeyDel()\fP as long refcounter is not 0.
+.PP
+The key's sync bit will always be set for any call, except: 
+.PP
+.nf
+Key *k = keyNew(0);
+// keyNeedSync() will be false
+
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkeyName\fP a valid name to the key, or NULL to get a simple initialized, but really empty, object 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyDel()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to a new allocated and initialized Key object, or NULL if an invalid \fCkeyName\fP was passed (see \fBkeySetName()\fP). 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/keymeta.3 b/doc/elektra-api/man/man3/keymeta.3
new file mode 100644 (file)
index 0000000..0873712
--- /dev/null
@@ -0,0 +1,653 @@
+.TH "Key :: Meta Info Manipulation Methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+Key :: Meta Info Manipulation Methods \- Methods to do various operations on Key metainfo.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBkeyStat\fP (Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyRemove\fP (Key *key)"
+.br
+.ti -1c
+.RI "uid_t \fBkeyGetUID\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetUID\fP (Key *key, uid_t uid)"
+.br
+.ti -1c
+.RI "gid_t \fBkeyGetGID\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetGID\fP (Key *key, gid_t gid)"
+.br
+.ti -1c
+.RI "int \fBkeySetDir\fP (Key *key)"
+.br
+.ti -1c
+.RI "mode_t \fBkeyGetMode\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetMode\fP (Key *key, mode_t mode)"
+.br
+.ti -1c
+.RI "type_t \fBkeyGetType\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetType\fP (Key *key, type_t newType)"
+.br
+.ti -1c
+.RI "time_t \fBkeyGetATime\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetATime\fP (Key *key, time_t atime)"
+.br
+.ti -1c
+.RI "time_t \fBkeyGetMTime\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetMTime\fP (Key *key, time_t mtime)"
+.br
+.ti -1c
+.RI "time_t \fBkeyGetCTime\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeySetCTime\fP (Key *key, time_t ctime)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to do various operations on Key metainfo. 
+.PP
+To use them: 
+.PP
+.nf
+#include <kdb.h>
+
+.fi
+.PP
+.PP
+Next to \fBName (key and owner) \fP and \fBValue (data and comment) \fP there is the so called metainfo inside every key.
+.PP
+Key metainfo insists of:
+.IP "\(bu" 2
+UID, the user id
+.IP "\(bu" 2
+GID, the group id
+.IP "\(bu" 2
+filesystem-like mode permissions (rwx)
+.IP "\(bu" 2
+Mode, change and modification times
+.PP
+.PP
+The comment can contain userdata which directly belong to that key.
+.PP
+Owner is the user that owns the key. It only works for the user/ hierachy.
+.PP
+Every user and group of your System has a uniqe ID. These values are used in the keys too. They are very important for the mode. See man 2 chown.
+.PP
+With the mode mode you can choose if a user, group or the world can mode your key. See man 2 chmod. 
+.SH "Function Documentation"
+.PP 
+.SS "time_t keyGetATime (const Key * key)"
+.PP
+Get last time the key data was read from disk.
+.PP
+Every \fBkdbGet()\fP might update the access time of a key. You get information when the key was read the last time from the database.
+.PP
+You will get 0 when the key was not read already.
+.PP
+Beware that multiple copies of keys with \fBkeyDup()\fP might have different atimes because you \fBkdbGet()\fP one, but not the other. You can use this information to decide which key is the latest.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP Key to get information from. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the time you got the key with \fBkdbGet()\fP 
+.PP
+0 on key that was never \fBkdbGet()\fP 
+.PP
+(time_t)-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetATime()\fP 
+.PP
+\fBkdbGet()\fP 
+.RE
+.PP
+
+.SS "time_t keyGetCTime (const Key * key)"
+.PP
+Get last time the key metadata was changed from disk.
+.PP
+You will get 0 when the key was not read already.
+.PP
+Any changed field in metadata will influence the ctime of a key.
+.PP
+This time is not updated if only value or comment are changed.
+.PP
+Not changed keys will not update this time, even after \fBkdbSet()\fP.
+.PP
+It is possible that other keys written to disc influence this time if the backend is not grained enough.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP Key to get information from. 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetCTime()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+(time_t)-1 on NULL pointer 
+.PP
+the metadata change time 
+.RE
+.PP
+
+.SS "gid_t keyGetGID (const Key * key)"
+.PP
+Get the group ID of a key.
+.SH "GID"
+.PP
+The group ID is a unique identification for every group present on a system. Keys will belong to root (0) as long as you did not get their real GID with \fBkdbGet()\fP.
+.PP
+Unlike UID users might change their group. This makes it possible to share configuration between some users.
+.PP
+A fresh key will have (gid_t)-1 also known as the group nogroup. It means that the key is not related to a group ID at the moment.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the system's GID of the key 
+.PP
+(gid_t)-1 on NULL key or currently unknown ID 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetGID()\fP, \fBkeyGetUID()\fP 
+.RE
+.PP
+
+.SS "mode_t keyGetMode (const Key * key)"
+.PP
+Return the key mode permissions.
+.PP
+Default is 0664 (octal) for keys and 0775 for directory keys which used \fBkeySetDir()\fP.
+.PP
+The defaults are defined with the macros KEY_DEF_MODE and KEY_DEF_DIR.
+.PP
+For more information about the mode permissions see \fBMode\fP.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+mode permissions of the key 
+.PP
+(mode_t)-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetMode()\fP 
+.RE
+.PP
+
+.SS "time_t keyGetMTime (const Key * key)"
+.PP
+Get last modification time of the key on disk.
+.PP
+You will get 0 when the key was not read already.
+.PP
+Everytime you change value or comment and \fBkdbSet()\fP the key the mtime will be updated. When you \fBkdbGet()\fP the key, the atime is set appropriate.
+.PP
+Not changed keys may not even passed to \fBkdbSet_backend()\fP so it will not update this time, even after \fBkdbSet()\fP.
+.PP
+It is possible that other keys written to disc influence this time if the backend is not grained enough.
+.PP
+If you add or remove a key the key thereunder in the hierarchy will update the mtime if written with \fBkdbSet()\fP to disc.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP Key to get information from. 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetMTime()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the last modification time 
+.PP
+(time_t)-1 on NULL pointer 
+.RE
+.PP
+
+.SS "type_t keyGetType (const Key * key)"
+.PP
+Returns the key data type.
+.PP
+See type_t for the type definition.
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetType()\fP 
+.PP
+\fBkeyIsBinary()\fP and \fBkeyIsString()\fP 
+.PP
+\fBkeyIsDir()\fP is not related to the type system 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP key where to get the type. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the key type 
+.PP
+KEY_TYPE_UNDEFINED on keys without type 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
+.SS "uid_t keyGetUID (const Key * key)"
+.PP
+Get the user ID of a key.
+.SH "UID"
+.PP
+The user ID is a unique identification for every user present on a system. Keys will belong to root (0) as long as you did not get their real UID with \fBkdbGet()\fP.
+.PP
+Although usually the same, the UID of a key is not related to its owner.
+.PP
+A fresh key will have (uid_t)-1 also known as the user nobody. It means that the key is not related to a user ID at the moment.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the system's UID of the key 
+.PP
+(uid_t)-1 on NULL key or currently unknown ID 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetGID()\fP, \fBkeySetUID()\fP, \fBkeyGetOwner()\fP 
+.RE
+.PP
+
+.SS "int keyRemove (Key * key)"
+.PP
+Permanently remove a key after committing to database.
+.PP
+This functions sets a flag that the key needs to be removed. It also sets a flag that it is not synced.
+.PP
+Remove the key instead of writing it in the key database when doing \fBkdbSet()\fP and related functions.
+.PP
+This key will be ignored and it is save to delete it afterwards. To be sure that it was removed, check if it needs sync with \fBkeyNeedSync()\fP.
+.PP
+\fBNote:\fP
+.RS 4
+Delete in elektra terminology means to free memory, remove means to free permanent storage.
+.RE
+.PP
+\fBWarning:\fP
+.RS 4
+You should not change a key's remove status once it belongs to a keyset. See \fBksSort()\fP for more information.
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyNeedRemove()\fP, \fBkdbSet()\fP, \fBkdbRemove()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on success 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
+.SS "int keySetATime (Key * key, time_t atime)"
+.PP
+Update the atime information for a key.
+.PP
+When you do manual sync of keys you might also update the atime to make them indistinguishable.
+.PP
+It can also be useful if you work with keys not using a keydatabase.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP The Key object to work with 
+.br
+\fIatime\fP The new access time for the key 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetATime()\fP 
+.RE
+.PP
+
+.SS "int keySetCTime (Key * key, time_t ctime)"
+.PP
+Update the ctime information for a key.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP The Key object to work with 
+.br
+\fIctime\fP The new change metadata time for the key 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetCTime()\fP 
+.RE
+.PP
+
+.SS "int keySetDir (Key * key)"
+.PP
+Set mode so that key will be recognized as directory.
+.PP
+The function will add all executable bits.
+.PP
+.IP "\(bu" 2
+Mode 0200 will be translated to 0311
+.IP "\(bu" 2
+Mode 0400 will be translated to 0711
+.IP "\(bu" 2
+Mode 0664 will be translated to 0775
+.PP
+.PP
+The macro KEY_DEF_DIR (defined to 0111) will be used for that.
+.PP
+The executable bits show that child keys are allowed and listable. There is no way to have child keys which are not listable for anyone, but it is possible to restrict listing the keys to the owner only.
+.PP
+.IP "\(bu" 2
+Mode 0000 means that it is a key not read or writable to anyone.
+.IP "\(bu" 2
+Mode 0111 means that it is a directory not read or writable to anyone. But it is recognized as directory to anyone.
+.PP
+.PP
+For more about mode see \fBkeySetMode()\fP.
+.PP
+It is not possible to access keys below a not executable key. If a key is not writeable and executable \fBkdbSet()\fP will fail to access the keys below. If a key is not readable and executable \fBkdbGet()\fP will fail to access the keys below.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key to set permissions to be recognized as directory. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetMode()\fP 
+.RE
+.PP
+
+.SS "int keySetGID (Key * key, gid_t gid)"
+.PP
+Set the group ID of a key.
+.PP
+See \fBGID\fP for more information about group IDs.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIgid\fP is the group ID 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL key 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetGID()\fP, \fBkeySetUID()\fP 
+.RE
+.PP
+
+.SS "int keySetMode (Key * key, mode_t mode)"
+.PP
+Set the key mode permissions.
+.PP
+The mode consists of 9 individual bits for mode permissions. In the following explanation the octal notation with leading zero will be used.
+.PP
+Default is 0664 (octal) for keys and 0775 for directory keys which used \fBkeySetDir()\fP.
+.PP
+The defaults are defined with the macros KEY_DEF_MODE and KEY_DEF_DIR.
+.PP
+\fBNote:\fP
+.RS 4
+libelektra 0.7.0 only allows 0775 (directory keys) and 0664 (other keys). More will be added later in a sense of the description below.
+.RE
+.PP
+.SH "Mode"
+.PP
+0000 is the most restrictive mode. No user might read, write or execute the key.
+.PP
+Reading the key means to get the value and comment by \fBkdbGet()\fP or all highlevel methods.
+.PP
+Writing the key means to set the value and comment by \fBkdbSet()\fP or all highlevel methods.
+.PP
+Execute the key means to make a step deeper in the hierarchy. But you must be able to read the key to be able to list the keys below. See also \fBkeySetDir()\fP in that context. But you must be able to write the key to be able to add or remove keys below.
+.PP
+0777 is the most relaxing mode. Every user is allowed to read, write and execute the key, if he is allowed to execute and read all keys below.
+.PP
+0700 allows every action for the current user, identified by the uid. See \fBkeyGetUID()\fP and \fBkeySetUID()\fP.
+.PP
+To be more specific for the user the single bits can elect the mode for read, write and execute. 0100 only allows executing which gives the information that it is a directory for that user, but not accessable. 0200 only allows reading. This information may be combined to 0300, which allows execute and reading of the directory. Last 0400 decides about the writing permissions.
+.PP
+The same as above is also valid for the 2 other octal digits. 0070 decides about the group permissions, in that case full access. Groups are identified by the gid. See \fBkeyGetGID()\fP and \fBkeySetGID()\fP. In that example everyone with a different uid, but the gid of the the key, has full access.
+.PP
+0007 decides about the world permissions. This is taken into account when neighter the uid nor the gid matches. So that example would allow everyone with a different uid and gid of that key gains full access.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key to set mode permissions 
+.br
+\fImode\fP the mode permissions 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL key 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetMode()\fP 
+.RE
+.PP
+
+.SS "int keySetMTime (Key * key, time_t mtime)"
+.PP
+Update the mtime information for a key.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP The Key object to work with 
+.br
+\fImtime\fP The new modification time for the key 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetMTime()\fP 
+.RE
+.PP
+
+.SS "int keySetType (Key * key, type_t newType)"
+.PP
+Set a new key type.
+.PP
+This method is usually not needed, unless you are working with more semantic value types, or want to force a specific value type for a key. It is not usually needed because the data type is automatically set when setting the key value.
+.PP
+See type_t for the type defintion.
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+// here we define the new type
+enum
+{
+        KEY_TYPE_COLOR=KEY_TYPE_STRING+4
+};
+// here we make a new key with the type
+Key *k1 = keyNew ('user/sw/oyranos/current/color1',
+        KEY_VALUE, '#4B52CA',
+        KEY_COMMENT, 'a custom color',
+        KEY_TYPE, KEY_TYPE_COLOR,
+        KEY_END);
+// lets check if it is really correct type
+if (keyGetType(k1) == KEY_TYPE_COLOR) printf ('correct type');
+
+.fi
+.PP
+.RE
+.PP
+When using type_t::KEY_TYPE_DIR, this method will not set mode permissions to the key. You'll have to set it manually after \fBkeySetType()\fP, calling \fBkeySetMode()\fP with appropriate permissions. Or use the \fBkeySetDir()\fP.
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetType()\fP 
+.PP
+\fBkeySetDir()\fP to see that the directory concept is independent of types 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fInewType\fP contains the new type 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on sucess 
+.PP
+-1 on NULL pointer and when newType >= KEY_TYPE_MAX 
+.RE
+.PP
+
+.SS "int keySetUID (Key * key, uid_t uid)"
+.PP
+Set the user ID of a key.
+.PP
+See \fBUID\fP for more information about user IDs.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIuid\fP the user ID to set 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL key 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetGID()\fP, \fBkeyGetUID()\fP, \fBkeyGetOwner()\fP 
+.RE
+.PP
+
+.SS "int keyStat (Key * key)"
+.PP
+Only stat a key instead of receiving value, comment and key type.
+.PP
+Only stat the key in the database when doing \fBkdbGet()\fP. The key may not have any value, comment or key type set.
+.PP
+It is not possible to revert the action on per-key basis. When you want to remove the flag you have to pass option_t::KDB_O_NOSTAT to the next \fBkdbGet()\fP.
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyNeedStat()\fP, \fBkdbGet()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on succuess 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/keyname.3 b/doc/elektra-api/man/man3/keyname.3
new file mode 100644 (file)
index 0000000..eee7169
--- /dev/null
@@ -0,0 +1,651 @@
+.TH "Key :: Name Manipulation Methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+Key :: Name Manipulation Methods \- Methods to do various operations on Key names.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "const char * \fBkeyName\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetNameSize\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetName\fP (const Key *key, char *returnedName, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeySetName\fP (Key *key, const char *newName)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetFullNameSize\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetFullName\fP (const Key *key, char *returnedName, size_t maxSize)"
+.br
+.ti -1c
+.RI "const char * \fBkeyBaseName\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetBaseNameSize\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetBaseName\fP (const Key *key, char *returned, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyAddBaseName\fP (Key *key, const char *baseName)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeySetBaseName\fP (Key *key, const char *baseName)"
+.br
+.ti -1c
+.RI "const char * \fBkeyOwner\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetOwnerSize\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetOwner\fP (const Key *key, char *returned, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeySetOwner\fP (Key *key, const char *owner)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to do various operations on Key names. 
+.PP
+To use them: 
+.PP
+.nf
+#include <kdb.h>
+
+.fi
+.PP
+.PP
+These functions make it easier for c programmers to work with key names. Everything here can also be done with keySetName, described in key.
+.PP
+\fBRules for Key Names\fP.RS 4
+
+.RE
+.PP
+When using Elektra to store your application's configuration and state, please keep in mind the following rules:
+.IP "\(bu" 2
+You are not allowed to create keys right under \fCsystem\fP or \fCuser\fP.
+.IP "\(bu" 2
+You are not allowed to create folder keys right under \fCsystem\fP or \fCuser\fP. They are reserved for very essential OS subsystems.
+.IP "\(bu" 2
+The keys for your application, called say \fIMyApp\fP, should be created under \fCsystem/sw/MyApp\fP and/or \fCuser/sw/MyApp\fP.
+.IP "\(bu" 2
+It is suggested to make your application look for default keys under \fCsystem/sw/MyApp/current\fP and/or \fCuser/sw/MyApp/current\fP. This way, from a sysadmin perspective, it will be possible to copy the \fCsystem/sw/MyApp/current\fP tree to something like \fCsystem/sw/MyApp/old\fP, and keep system clean and organized.
+.IP "\(bu" 2
+\\0 must not occur in names.
+.IP "\(bu" 2
+/ is the seperator. 
+.PP
+
+.SH "Function Documentation"
+.PP 
+.SS "ssize_t keyAddBaseName (Key * key, const char * baseName)"
+.PP
+Adds \fCbaseName\fP to the current key name.
+.PP
+Assumes that \fCkey\fP is a directory. \fCbaseName\fP is appended to it. The function adds \fC'/'\fP if needed while concatenating.
+.PP
+So if \fCkey\fP has name \fC'system/dir1/dir2'\fP and this method is called with \fCbaseName\fP \fC'mykey'\fP, the resulting key will have name \fC'system/dir1/dir2/mykey'\fP.
+.PP
+When baseName is 0 or '' nothing will happen and the size of the name is returned.
+.PP
+\fBWarning:\fP
+.RS 4
+You should not change a keys name once it belongs to a keyset. See \fBksSort()\fP for more information.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIbaseName\fP the string to append to the name 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the size in bytes of the new key name including the ending NULL 
+.PP
+-1 if the key had no name 
+.PP
+-1 on NULL pointers 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetBaseName()\fP 
+.PP
+\fBkeySetName()\fP to set a new name. 
+.RE
+.PP
+
+.SS "const char* keyBaseName (const Key * key)"
+.PP
+Returns a pointer to the real internal key name where the \fCbasename\fP starts.
+.PP
+This is a much more efficient version of \fBkeyGetBaseName()\fP and you should use it if you are responsible enough to not mess up things. The name might change or even point to a wrong place after a \fBkeySetName()\fP. If you need a copy of the basename consider to use \fBkeyGetBaseName()\fP.
+.PP
+\fBkeyBaseName()\fP returns '' when there is no keyBaseName. The reason is 
+.PP
+.nf
+key=keyNew(0);
+keySetName(key,'');
+keyName(key); // you would expect '' here
+keySetName(key,'user');
+keyName(key); // you would expect '' here
+keyDel(key);
+
+.fi
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \fBkeyBaseName()\fP method to set a new value. Use \fBkeySetBaseName()\fP instead.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the object to obtain the basename from 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to the basename 
+.PP
+'' on null pointer or when the key has no name 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetBaseName()\fP, \fBkeyGetBaseNameSize()\fP 
+.PP
+\fBkeyName()\fP to get a pointer to the name 
+.PP
+\fBkeyOwner()\fP to get a pointer to the owner 
+.RE
+.PP
+
+.SS "ssize_t keyGetBaseName (const Key * key, char * returned, size_t maxSize)"
+.PP
+Calculate the basename of a key name and put it in \fCreturned\fP finalizing the string with NULL.
+.PP
+Some examples:
+.IP "\(bu" 2
+basename of \fCsystem/some/keyname\fP is \fCkeyname\fP 
+.IP "\(bu" 2
+basename of \fC'user/tmp/some key'\fP is \fC'some key'\fP 
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key to extract basename from 
+.br
+\fIreturned\fP a pre-allocated buffer to store the basename 
+.br
+\fImaxSize\fP size of the \fCreturned\fP buffer 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes copied to \fCreturned\fP 
+.PP
+1 on empty name 
+.PP
+-1 on NULL pointers 
+.PP
+-1 when maxSize is 0 or larger than SSIZE_MAX 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyBaseName()\fP, \fBkeyGetBaseNameSize()\fP 
+.PP
+\fBkeyName()\fP, \fBkeyGetName()\fP, \fBkeySetName()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetBaseNameSize (const Key * key)"
+.PP
+Calculates number of bytes needed to store basename of \fCkey\fP.
+.PP
+Key names that have only root names (e.g. \fC'system'\fP or \fC'user'\fP or \fC'user:domain'\fP ) does not have basenames, thus the function will return 1 bytes to store ''.
+.PP
+Basenames are denoted as:
+.IP "\(bu" 2
+\fCsystem/some/thing/basename\fP -> \fCbasename\fP 
+.IP "\(bu" 2
+\fCuser:domain/some/thing/base\\/name\fP > \fCbase\\/name\fP 
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+size in bytes of \fCkey's\fP basename including ending NULL 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyBaseName()\fP, \fBkeyGetBaseName()\fP 
+.PP
+\fBkeyName()\fP, \fBkeyGetName()\fP, \fBkeySetName()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetFullName (const Key * key, char * returnedName, size_t maxSize)"
+.PP
+Get key full name, including the user domain name.
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written 
+.PP
+1 on empty name 
+.PP
+-1 on NULL pointers 
+.PP
+-1 if maxSize is 0 or larger than SSIZE_MAX 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object 
+.br
+\fIreturnedName\fP pre-allocated memory to write the key name 
+.br
+\fImaxSize\fP maximum number of bytes that will fit in returnedName, including the final NULL 
+.RE
+.PP
+
+.SS "ssize_t keyGetFullNameSize (const Key * key)"
+.PP
+Bytes needed to store the key name including user domain and ending NULL.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes needed to store key name including user domain 
+.PP
+1 on empty name 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetFullName()\fP, \fBkeyGetNameSize()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetName (const Key * key, char * returnedName, size_t maxSize)"
+.PP
+Get abbreviated key name (without owner name).
+.PP
+When there is not enough space to write the name, nothing will be written and -1 will be returned.
+.PP
+maxSize is limited to SSIZE_MAX. When this value is exceeded -1 will be returned. The reason for that is that any value higher is just a negative return value passed by accident. Of course malloc is not as failure tolerant and will try to allocate.
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to \fCreturnedName\fP 
+.PP
+1 when only a null was written 
+.PP
+-1 when keyname is longer then maxSize or 0 or any NULL pointer 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIreturnedName\fP pre-allocated memory to write the key name 
+.br
+\fImaxSize\fP maximum number of bytes that will fit in returnedName, including the final NULL 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetNameSize()\fP, \fBkeyGetFullName()\fP, \fBkeyGetFullNameSize()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetNameSize (const Key * key)"
+.PP
+Bytes needed to store the key name without owner.
+.PP
+For an empty key name you need one byte to store the ending NULL. For that reason 1 is returned.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes needed, including ending NULL, to store key name without owner 
+.PP
+1 if there is is no key Name 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetName()\fP, \fBkeyGetFullNameSize()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetOwner (const Key * key, char * returned, size_t maxSize)"
+.PP
+Return the owner of the key.
+.IP "\(bu" 2
+Given \fCuser:someuser/\fP..... return \fCsomeuser\fP 
+.IP "\(bu" 2
+Given \fCuser:some.user/\fP.... return \fCsome.user\fP 
+.IP "\(bu" 2
+Given \fCuser/\fP.... return the current user
+.PP
+.PP
+Only \fCuser/\fP... keys have a owner. For \fCsystem/\fP... keys (that doesn't have a key owner) an empty string ('') is returned.
+.PP
+Although usually the same, the owner of a key is not related to its UID. Owner are related to WHERE the key is stored on disk, while UIDs are related to mode controls of a key.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the object to work with 
+.br
+\fIreturned\fP a pre-allocated space to store the owner 
+.br
+\fImaxSize\fP maximum number of bytes that fit returned 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to buffer 
+.PP
+1 if there is no owner 
+.PP
+-1 on NULL pointers 
+.PP
+-1 when maxSize is 0, larger than SSIZE_MAX or too small for ownername 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetName()\fP, \fBkeySetOwner()\fP, \fBkeyOwner()\fP, \fBkeyGetFullName()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetOwnerSize (const Key * key)"
+.PP
+Return the size of the owner of the Key with concluding 0.
+.PP
+The returned number can be used to allocate a string. 1 will returned on an empty owner to store the concluding 0 on using \fBkeyGetOwner()\fP.
+.PP
+.PP
+.nf
+char * buffer;
+buffer = malloc (keyGetOwnerSize (key));
+// use buffer and keyGetOwnerSize (key) for maxSize
+.fi
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+that -1 might be returned on null pointer, so when you directly allocate afterwards its best to check if you will pass a null pointer before.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes 
+.PP
+1 if there is no owner 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetOwner()\fP 
+.RE
+.PP
+
+.SS "const char* keyName (const Key * key)"
+.PP
+Returns a pointer to the abbreviated real internal \fCkey\fP name.
+.PP
+This is a much more efficient version of \fBkeyGetName()\fP and can use it if you are responsible enough to not mess up things. You are not allowed to change anything in the returned array. The content of that string may change after \fBkeySetName()\fP and similar functions. If you need a copy of the name, consider using \fBkeyGetName()\fP.
+.PP
+The name will be without owner, see \fBkeyGetFullName()\fP if you need the name with its owner.
+.PP
+\fBkeyName()\fP returns '' when there is no keyName. The reason is 
+.PP
+.nf
+key=keyNew(0);
+keySetName(key,'');
+keyName(key); // you would expect '' here
+keyDel(key);
+
+.fi
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \fBkeyName()\fP method to set a new value. Use \fBkeySetName()\fP instead.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to the keyname which must not be changed. 
+.PP
+'' when there is no (a empty) keyname 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetNameSize()\fP for the string length 
+.PP
+\fBkeyGetFullName()\fP, \fBkeyGetFullNameSize()\fP to get the full name 
+.PP
+\fBkeyGetName()\fP as alternative to get a copy 
+.PP
+\fBkeyOwner()\fP to get a pointer to owner 
+.RE
+.PP
+
+.SS "const char* keyOwner (const Key * key)"
+.PP
+Return a pointer to the real internal \fCkey\fP owner.
+.PP
+This is a much more efficient version of \fBkeyGetOwner()\fP and you should use it if you are responsible enough to not mess up things. You are not allowed to modify the returned string in any way. If you need a copy of the string, consider to use \fBkeyGetOwner()\fP instead.
+.PP
+\fBkeyOwner()\fP returns '' when there is no keyOwner. The reason is 
+.PP
+.nf
+key=keyNew(0);
+keySetOwner(key,'');
+keyOwner(key); // you would expect '' here
+keySetOwner(key,'system');
+keyOwner(key); // you would expect '' here
+
+.fi
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \fBkeyOwner()\fP method to set a new value. Use \fBkeySetOwner()\fP instead.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to internal owner 
+.PP
+'' when there is no (a empty) owner 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetOwnerSize()\fP for the size of the string with concluding 0 
+.PP
+\fBkeyGetOwner()\fP, \fBkeySetOwner()\fP 
+.PP
+\fBkeyName()\fP for name without owner 
+.PP
+\fBkeyGetFullName()\fP for name with owner 
+.RE
+.PP
+
+.SS "ssize_t keySetBaseName (Key * key, const char * baseName)"
+.PP
+Sets \fCbaseName\fP as the new basename for \fCkey\fP.
+.PP
+All text after the last \fC'/'\fP in the \fCkey\fP keyname is erased and \fCbaseName\fP is appended.
+.PP
+So lets suppose \fCkey\fP has name \fC'system/dir1/dir2/key1'\fP. If \fCbaseName\fP is \fC'key2'\fP, the resulting key name will be \fC'system/dir1/dir2/key2'\fP. If \fCbaseName\fP is empty or NULL, the resulting key name will be \fC'system/dir1/dir2'\fP.
+.PP
+\fBWarning:\fP
+.RS 4
+You should not change a keys name once it belongs to a keyset. See \fBksSort()\fP for more information.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIbaseName\fP the string used to overwrite the basename of the key 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the size in bytes of the new key name 
+.PP
+-1 on NULL pointers 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyAddBaseName()\fP 
+.PP
+\fBkeySetName()\fP to set a new name 
+.RE
+.PP
+
+.SS "ssize_t keySetName (Key * key, const char * newName)"
+.PP
+Set a new name to a key.
+.PP
+A valid name is of the forms:
+.IP "\(bu" 2
+\fCsystem/something\fP 
+.IP "\(bu" 2
+\fCuser/something\fP 
+.IP "\(bu" 2
+\fCuser:username/something\fP 
+.PP
+.PP
+The last form has explicitly set the owner, to let the library know in which user folder to save the key. A owner is a user name. If not defined (the second form) current user is calculated and used as default.
+.PP
+You should always follow the guidelines for key tree structure creation.
+.PP
+A private copy of the key name will be stored, and the \fCnewName\fP parameter can be freed after this call.
+.PP
+.., . and / will be handled correctly. A valid name will be build out of the (valid) name what you pass, e.g. user///sw/../sw//././MyApp -> user/sw/MyApp
+.PP
+On invalid names, NULL or '' the name will be '' afterwards.
+.PP
+\fBWarning:\fP
+.RS 4
+You should not change a keys name once it belongs to a keyset. See \fBksSort()\fP for more information.
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+size in bytes of this new key name including ending NULL 
+.PP
+-1 if \fCnewName\fP is empty or invalid or any NULL pointer 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fInewName\fP the new key name 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyNew()\fP, \fBkeySetOwner()\fP 
+.PP
+\fBkeyGetName()\fP, \fBkeyGetFullName()\fP, \fBkeyName()\fP 
+.PP
+\fBkeySetBaseName()\fP, \fBkeyAddBaseName()\fP to manipulate a name 
+.RE
+.PP
+
+.SS "ssize_t keySetOwner (Key * key, const char * owner)"
+.PP
+Set the owner of a key.
+.PP
+A owner is a name of a system user related to a UID. The owner decides on which location on the disc the key goes.
+.PP
+A private copy is stored, so the passed parameter can be freed after the call.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIowner\fP the owner (or user name) 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually saved including final NULL 
+.PP
+1 when owner is freed (by setting 0 or '') 
+.PP
+-1 on null pointer or memory problems 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetName()\fP, \fBkeyGetOwner()\fP, \fBkeyGetFullName()\fP 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/keyset.3 b/doc/elektra-api/man/man3/keyset.3
new file mode 100644 (file)
index 0000000..c4550b9
--- /dev/null
@@ -0,0 +1,984 @@
+.TH "KeySet :: Class Methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+KeySet :: Class Methods \- Methods to manipulate KeySets.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "KeySet * \fBksNew\fP (size_t alloc,...)"
+.br
+.ti -1c
+.RI "KeySet * \fBksDup\fP (const KeySet *source)"
+.br
+.ti -1c
+.RI "int \fBksCopy\fP (KeySet *dest, const KeySet *source)"
+.br
+.ti -1c
+.RI "int \fBksDel\fP (KeySet *ks)"
+.br
+.ti -1c
+.RI "void \fBksSort\fP (KeySet *ks)"
+.br
+.ti -1c
+.RI "ssize_t \fBksGetSize\fP (const KeySet *ks)"
+.br
+.ti -1c
+.RI "ssize_t \fBksAppendKey\fP (KeySet *ks, Key *toAppend)"
+.br
+.ti -1c
+.RI "ssize_t \fBksAppend\fP (KeySet *ks, const KeySet *toAppend)"
+.br
+.ti -1c
+.RI "Key * \fBksPop\fP (KeySet *ks)"
+.br
+.ti -1c
+.RI "int \fBksRewind\fP (KeySet *ks)"
+.br
+.ti -1c
+.RI "Key * \fBksNext\fP (KeySet *ks)"
+.br
+.ti -1c
+.RI "Key * \fBksCurrent\fP (const KeySet *ks)"
+.br
+.ti -1c
+.RI "Key * \fBksHead\fP (const KeySet *ks)"
+.br
+.ti -1c
+.RI "Key * \fBksTail\fP (const KeySet *ks)"
+.br
+.ti -1c
+.RI "cursor_t \fBksGetCursor\fP (const KeySet *ks)"
+.br
+.ti -1c
+.RI "int \fBksSetCursor\fP (KeySet *ks, cursor_t cursor)"
+.br
+.ti -1c
+.RI "Key * \fBksLookup\fP (KeySet *ks, Key *key, option_t options)"
+.br
+.ti -1c
+.RI "Key * \fBksLookupByName\fP (KeySet *ks, const char *name, option_t options)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to manipulate KeySets. 
+.PP
+A KeySet is a unsorted set of keys.
+.PP
+Terminate with ksNew(0) or ksNew(20, ..., KS_END) This is because there is a list of Key* required and KS_END has the length of (Key*).
+.PP
+It can be implemented in various ways like a linked list or with a dynamically allocated array.
+.PP
+With \fBksNew()\fP you can create a new KeySet.
+.PP
+You can add keys with \fBksAppendKey()\fP in the keyset. \fBksGetSize()\fP tells you the current size of the keyset.
+.PP
+With \fBksRewind()\fP and \fBksNext()\fP you can navigate through the keyset. Don't expect any particular order, but it is assured that you will get every key of the set.
+.PP
+KeySets have an \fBinternal cursor \fP. This is used for \fBksLookup()\fP and \fBkdbSet()\fP.
+.PP
+KeySet has a fundamental meaning inside elektra. It makes it possible to get and store many keys at once inside the database. In addition to that the class can be used as high level datastructure in applications. With \fBksLookupByName()\fP it is possible to fetch easily specific keys out of the list of keys.
+.PP
+You can easily create and iterate keys: 
+.PP
+.nf
+#include <kdb.h>
+
+// create a new keyset with 3 keys
+// with a hint that about 20 keys will be inside
+KeySet *myConfig = ksNew(20,
+        keyNew ('user/name1', 0),
+        keyNew ('user/name2', 0),
+        keyNew ('user/name3', 0),
+        KS_END);
+// append a key in the keyset
+ksAppendKey(myConfig, keyNew('user/name4', 0));
+
+Key *current;
+ksRewind(myConfig);
+while ((current=ksNext(myConfig))!=0) {
+        printf('Key name is %s.\n', keyName (current));
+}
+ksDel (myConfig); // delete keyset and all keys appended
+
+.fi
+.PP
+.SH "Function Documentation"
+.PP 
+.SS "ssize_t ksAppend (KeySet * ks, const KeySet * toAppend)"
+.PP
+Append all \fCtoAppend\fP contained keys to the end of the \fCks\fP.
+.PP
+\fCtoAppend\fP KeySet will be left unchanged.
+.PP
+Makes the keyset dirty, see \fBksSort()\fP.
+.PP
+\fBReturns:\fP
+.RS 4
+the size of the KeySet after transfer 
+.PP
+-1 on NULL pointers 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the KeySet that will receive the keys 
+.br
+\fItoAppend\fP the KeySet that provides the keys that will be transfered 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksAppendKey()\fP, ksInsert(), ksInsertKeys() 
+.RE
+.PP
+
+.SS "ssize_t ksAppendKey (KeySet * ks, Key * toAppend)"
+.PP
+Appends a Key to the end of \fCks\fP.
+.PP
+A pointer to the key will be stored, and not a private copy. So a future \fBksDel()\fP on \fCks\fP may \fBkeyDel()\fP the \fCtoAppend\fP object, see \fBkeyGetRef()\fP.
+.PP
+The reference counter of the key will be incremented, and thus toAppend is not const.
+.PP
+The KeySet internal cursor is not moved.
+.PP
+Makes the keyset dirty, see \fBksSort()\fP.
+.PP
+\fBReturns:\fP
+.RS 4
+the size of the KeySet after insertion 
+.PP
+-1 on NULL pointers 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP KeySet that will receive the key 
+.br
+\fItoAppend\fP Key that will be appended to ks 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+ksInsert(), ksInsertKeys(), \fBksAppend()\fP, \fBkeyNew()\fP, \fBksDel()\fP 
+.PP
+\fBkeyIncRef()\fP 
+.RE
+.PP
+
+.SS "int ksCopy (KeySet * dest, const KeySet * source)"
+.PP
+Copy a keyset.
+.PP
+Most often you may want a duplicate of a keyset, see \fBksDup()\fP or append keys, see \fBksAppend()\fP. But in some situations you need to copy a keyset to a existing keyset, for that this function exists.
+.PP
+You can also use it to clear a keyset when you pass a NULL pointer as \fCsource\fP.
+.PP
+Note that all keys in \fCdest\fP will be deleted. Afterwards the content of the source will be added to the destination and the \fBksCurrent()\fP is set properly in \fCdest\fP.
+.PP
+A flat copy is made, so the keys will not be duplicated, but there reference counter is updated, so both keysets need to be \fBksDel()\fP.
+.PP
+.PP
+.nf
+int f (KeySet *ks)
+{
+        KeySet *c = ksNew (20, ..., KS_END);
+        // c receives keys
+        ksCopy (ks, c); // pass the keyset to the caller
+
+        ksDel (c);
+}       // caller needs to ksDel (ks)
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIsource\fP has to be an initialized source KeySet or NULL 
+.br
+\fIdest\fP has to be an initialized KeySet where to write the keys 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on success 
+.PP
+0 if dest was cleared successfully (source is NULL) 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNew()\fP, \fBksDel()\fP, \fBksDup()\fP 
+.PP
+\fBkeyCopy()\fP for copying keys 
+.RE
+.PP
+
+.SS "Key* ksCurrent (const KeySet * ks)"
+.PP
+Return the current Key.
+.PP
+The pointer is NULL if you reached the end or after \fBksRewind()\fP.
+.PP
+\fBNote:\fP
+.RS 4
+You must not delete the key or change the key, use \fBksPop()\fP if you want to delete it.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+pointer to the Key pointed by \fCks's\fP cursor 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNext()\fP, \fBksRewind()\fP 
+.PP
+kdbMonitorKeys() for a usage example 
+.RE
+.PP
+
+.SS "int ksDel (KeySet * ks)"
+.PP
+A destructor for KeySet objects.
+.PP
+Cleans all internal dynamic attributes, decrement all reference pointers to all keys and then \fBkeyDel()\fP all contained Keys, and free()s the release the KeySet object memory (that was previously allocated by \fBksNew()\fP).
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 when the keyset was freed 
+.PP
+-1 on null pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNew()\fP 
+.RE
+.PP
+
+.SS "KeySet* ksDup (const KeySet * source)"
+.PP
+Return a duplicate of a keyset.
+.PP
+Objects created with \fBksDup()\fP must be destroyed with \fBksDel()\fP.
+.PP
+Memory will be allocated as needed for dynamic properties, so you need to \fBksDel()\fP the returned pointer.
+.PP
+A flat copy is made, so the keys will not be duplicated, but there reference counter is updated, so both keysets need \fBksDel()\fP.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIsource\fP has to be an initializised source KeySet 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a flat copy of source on success 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNew()\fP, \fBksDel()\fP 
+.PP
+\fBkeyDup()\fP for \fBKey :: Basic Methods\fP duplication 
+.RE
+.PP
+
+.SS "cursor_t ksGetCursor (const KeySet * ks)"
+.PP
+Get the KeySet internal cursor.
+.PP
+Use it to get the cursor of the actual position.
+.PP
+\fBWarning:\fP
+.RS 4
+Cursors are getting invalid when the key was \fBksPop()\fPed or \fBksLookup()\fP with KDB_O_POP was used.
+.RE
+.PP
+.SH "Read ahead"
+.PP
+With the cursors it is possible to read ahead in a keyset:
+.PP
+.PP
+.nf
+cursor_t jump;
+ksRewind (ks);
+while ((key = keyNext (ks))!=0)
+{
+        // now mark this key
+        jump = ksGetCursor(ks);
+
+        //code..
+        keyNext (ks); // now browse on
+        // use ksCurrent(ks) to check the keys
+        //code..
+
+        // jump back to the position marked before
+        ksSetCursor(ks, jump);
+}
+.fi
+.PP
+.SH "Restoring state"
+.PP
+It can also be used to restore the state of a keyset in a function
+.PP
+.PP
+.nf
+int f (KeySet *ks)
+{
+        cursor_t state = ksGetCursor(ks);
+
+        // work with keyset
+
+        // now bring the keyset to the state before
+        ksSetCursor (ks, state);
+}
+.fi
+.PP
+.PP
+It is of course possible to make the KeySet const and cast its const away to set the cursor. Another way to achieve the same is to \fBksDup()\fP the keyset, but it is not as efficient.
+.PP
+An invalid cursor will be returned directly after \fBksRewind()\fP. When you set an invalid cursor \fBksCurrent()\fP is 0 and \fBksNext()\fP == \fBksHead()\fP.
+.PP
+\fBNote:\fP
+.RS 4
+Only use a cursor for the same keyset which it was made for.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a valid cursor on success 
+.PP
+an invalid cursor on NULL pointer or after \fBksRewind()\fP 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNext()\fP, \fBksSetCursor()\fP 
+.RE
+.PP
+
+.SS "ssize_t ksGetSize (const KeySet * ks)"
+.PP
+Return the number of keys that \fCks\fP contains.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of keys that \fCks\fP contains. 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+ksNew(0), \fBksDel()\fP 
+.RE
+.PP
+
+.SS "Key* ksHead (const KeySet * ks)"
+.PP
+Return the first key in the KeySet.
+.PP
+The KeySets cursor will not be effected.
+.PP
+If \fBksCurrent()\fP==ksHead() you know you are on the first key.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the first Key of a keyset 
+.PP
+0 on NULL pointer or empty keyset 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksTail()\fP for the last \fBKey :: Basic Methods\fP 
+.PP
+\fBksRewind()\fP, \fBksCurrent()\fP and \fBksNext()\fP for iterating over the \fBKeySet :: Class Methods\fP 
+.RE
+.PP
+
+.SS "Key* ksLookup (KeySet * ks, Key * key, option_t options)"
+.PP
+Look for a Key contained in \fCks\fP that matches the name of the \fCkey\fP.
+.SH "Introduction"
+.PP
+\fC\fBksLookup()\fP\fP is designed to let you work with entirely pre-loaded KeySets, so instead of \fBkdbGetKey()\fP, key by key, the idea is to fully \fBkdbGet()\fP for your application root key and process it all at once with \fC\fBksLookup()\fP\fP.
+.PP
+This function is very efficient by using binary search. Together with \fBkdbGet()\fP which can you load the whole configuration with only some communication to backends you can write very effective but short code for configuration.
+.SH "Usage"
+.PP
+If found, \fCks\fP internal cursor will be positioned in the matched key (also accessible by \fBksCurrent()\fP), and a pointer to the Key is returned. If not found, \fCks\fP internal cursor will not move, and a NULL pointer is returned.
+.PP
+Cascading is done if the first character is a /. This leads to ignoring the prefix like system/ and user/. 
+.PP
+.nf
+        if (kdbGet(handle, 'user/myapp', myConfig, 0 ) == -1)
+                ErrorHandler ('Could not get Keys');
+
+        if (kdbGet(handle, 'system/myapp', myConfig, 0 ) == -1)
+                ErrorHandler ('Could not get Keys');
+
+        if ((myKey = ksLookup(myConfig, key, 0)) == NULL)
+                ErrorHandler ('Could not Lookup Key');
+
+.fi
+.PP
+.PP
+This is the way multi user Programs should get there configuration and search after the values. It is guaranteed that more namespaces can be added easily and that all values can be set by admin and user.
+.SS "KDB_O_NOALL"
+When KDB_O_NOALL is set the keyset will be only searched from \fBksCurrent()\fP to \fBksTail()\fP. You need to \fBksRewind()\fP the keyset yourself. \fBksCurrent()\fP is always set properly after searching a key, so you can go on searching another key after the found key.
+.PP
+When KDB_O_NOALL is not set the cursor will stay untouched and all keys are considered. A much more efficient binary search will be used then.
+.PP
+So if you change keys, e.g. rename (\fBkeySetName()\fP) or remove (\fBkeyRemove()\fP) them make sure to sort the keyset with \fBksSort()\fP. When the keyset is dirty, see ksNeedSort() it will be sorted automatically when needed.
+.SS "KDB_O_POP"
+When KDB_O_POP is set the key which was found will be \fBksPop()\fPed. \fBksCurrent()\fP will not be changed, only iff \fBksCurrent()\fP is the searched key, then the keyset will be \fBksRewind()\fPed.
+.PP
+\fBNote:\fP
+.RS 4
+Note that \fBkeyRemove()\fP keys won't be found after the first time the keyset is resorted. Lookup automatically sorts the keyset, if needed, but it can't find it out when only keys are changed, not the keyset.
+.PP
+Like in \fBksPop()\fP the popped key always needs to be \fBkeyDel()\fP afterwards, even if it is appended to another keyset.
+.RE
+.PP
+\fBWarning:\fP
+.RS 4
+All cursors on the keyset will be invalid iff you use KDB_O_POP, so don't use this if you rely on a cursor, see \fBksGetCursor()\fP.
+.RE
+.PP
+\fBNote:\fP
+.RS 4
+Never use \fBksLookup()\fP with KDB_O_POP and \fBksAppendKey()\fP or \fBksAppend()\fP together in a loop. Otherwise \fBksLookup()\fP will need to resort the keyset every iteration and spend 99.96% of the time in \fBksSort()\fP (benchmarked with above 500k iterations).
+.RE
+.PP
+You can solve this problem by using KDB_O_NOALL, risking you have to iterate n^2 instead of n.
+.PP
+The more elegant way is to separate the keyset you use for \fBksLookup()\fP and \fBksAppendKey()\fP: 
+.PP
+.nf
+int f(KeySet *iterator, KeySet *lookup)
+{
+        KeySet *append = ksNew (ksGetSize(lookup), KS_END);
+        Key *key;
+        Key *current;
+
+        ksRewind(iterator);
+        while (current=ksNext(iterator))
+        {
+                key = ksLookup (lookup, current, KDB_O_POP);
+                // do something...
+                ksAppendKey(append, key); // now append it to append, not lookup!
+                keyDel (key); // make sure to ALWAYS delete poped keys.
+        }
+        ksAppend(lookup, append);
+        // now lookup needs to be sorted only once, append never
+        ksDel (append);
+}
+
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP where to look for 
+.br
+\fIkey\fP the key object you are looking for 
+.br
+\fIoptions\fP some \fCKDB_O_*\fP option bits:
+.IP "\(bu" 2
+\fCKDB_O_NOCASE\fP 
+.br
+ Lookup ignoring case.
+.IP "\(bu" 2
+\fCKDB_O_WITHOWNER\fP 
+.br
+ Also consider correct owner.
+.IP "\(bu" 2
+\fCKDB_O_NOALL\fP 
+.br
+ Only search from \fBksCurrent()\fP to end of keyset, see above text.
+.IP "\(bu" 2
+\fCKDB_O_POP\fP 
+.br
+ Pop the key which was found.
+.IP "\(bu" 2
+\fCKDB_O_SORT\fP 
+.br
+ Force sorting before searching, see \fBksSort()\fP. Together with KDB_O_NOALL the search will start from beginning. 
+.PP
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+pointer to the Key found, 0 otherwise 
+.PP
+0 on NULL pointers 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksLookupByName()\fP to search by a name given by a string 
+.PP
+\fBksCurrent()\fP, \fBksRewind()\fP, \fBksNext()\fP for iterating over a \fBKeySet :: Class Methods\fP 
+.PP
+\fBksSort()\fP to understand how \fBKeySet :: Class Methods\fP sort themself 
+.RE
+.PP
+
+.SS "Key* ksLookupByName (KeySet * ks, const char * name, option_t options)"
+.PP
+Look for a Key contained in \fCks\fP that matches \fCname\fP.
+.PP
+\fC\fBksLookupByName()\fP\fP is designed to let you work with entirely pre-loaded KeySets, so instead of \fBkdbGetKey()\fP, key by key, the idea is to fully \fBkdbGetByName()\fP for your application root key and process it all at once with \fC\fBksLookupByName()\fP\fP.
+.PP
+This function is very efficient by using binary search. Together with \fBkdbGetByName()\fP which can you load the whole configuration with only some communication to backends you can write very effective but short code for configuration.
+.PP
+If found, \fCks\fP internal cursor will be positioned in the matched key (also accessible by \fBksCurrent()\fP), and a pointer to the Key is returned. If not found, \fCks\fP internal cursor will not move, and a NULL pointer is returned.
+.SH "Cascading"
+.PP
+Cascading is done if the first character is a /. This leads to ignoring the prefix like system/ and user/. 
+.PP
+.nf
+        if (kdbGetByName(handle, '/sw/myapp/current', myConfig, 0 ) == -1)
+                ErrorHandler ('Could not get Keys');
+
+        if ((myKey = ksLookupByName (myConfig, '/myapp/current/key', 0)) == NULL)
+                ErrorHandler ('Could not Lookup Key');
+
+.fi
+.PP
+.PP
+This is the way multi user Programs should get there configuration and search after the values. It is guaranteed that more namespaces can be added easily and that all values can be set by admin and user.
+.SH "Full Search"
+.PP
+When KDB_O_NOALL is set the keyset will be only searched from \fBksCurrent()\fP to \fBksTail()\fP. You need to \fBksRewind()\fP the keyset yourself. \fBksCurrent()\fP is always set properly after searching a key, so you can go on searching another key after the found key.
+.PP
+When KDB_O_NOALL is not set the cursor will stay untouched and all keys are considered. A much more efficient binary search will be used then.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP where to look for 
+.br
+\fIname\fP key name you are looking for 
+.br
+\fIoptions\fP some \fCKDB_O_*\fP option bits:
+.IP "\(bu" 2
+\fCKDB_O_NOCASE\fP 
+.br
+ Lookup ignoring case.
+.IP "\(bu" 2
+\fCKDB_O_WITHOWNER\fP 
+.br
+ Also consider correct owner.
+.IP "\(bu" 2
+\fCKDB_O_NOALL\fP 
+.br
+ Only search from \fBksCurrent()\fP to end of keyset, see above text.
+.IP "\(bu" 2
+\fCKDB_O_POP\fP 
+.br
+ Pop the key which was found.
+.IP "\(bu" 2
+\fCKDB_O_SORT\fP 
+.br
+ Force sorting before searching, see \fBksSort()\fP. Together with KDB_O_NOALL the search will start from beginning.
+.PP
+.RE
+.PP
+Currently no options supported. 
+.PP
+\fBReturns:\fP
+.RS 4
+pointer to the Key found, 0 otherwise 
+.PP
+0 on NULL pointers 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+keyCompare() for very powerfull Key lookups in KeySets 
+.PP
+\fBksCurrent()\fP, \fBksRewind()\fP, \fBksNext()\fP 
+.RE
+.PP
+
+.SS "KeySet* ksNew (size_t alloc,  ...)"
+.PP
+Allocate, initialize and return a new KeySet object.
+.PP
+Objects created with \fBksNew()\fP must be destroyed with \fBksDel()\fP.
+.PP
+You can use a various long list of parameters to preload the keyset with a list of keys. Either your first and only parameter is 0 or your last parameter must be KEY_END.
+.PP
+For most uses 
+.PP
+.nf
+KeySet *keys = ksNew(0);
+// work with it
+ksDel (keys);
+
+.fi
+.PP
+ goes ok, the alloc size will be 16, defined in kdbprivate.h. The alloc size will be doubled whenever size reaches alloc size, so it also performs out large keysets.
+.PP
+But if you have any clue how large your keyset may be you should read the next statements.
+.PP
+If you want a keyset with length 15 (because you know of your application that you normally need about 12 up to 14 keys), use: 
+.PP
+.nf
+KeySet * keys = ksNew (15, KS_END);
+// work with it
+ksDel (keys);
+
+.fi
+.PP
+.PP
+If you start having 3 keys, and your application needs approximately 200-500 keys, you can use: 
+.PP
+.nf
+KeySet * config = ksNew (500,
+        keyNew ('user/sw/app/fixedConfiguration/key1', KEY_SWITCH_VALUE, 'value1', 0),
+        keyNew ('user/sw/app/fixedConfiguration/key2', KEY_SWITCH_VALUE, 'value2', 0),
+        keyNew ('user/sw/app/fixedConfiguration/key3', KEY_SWITCH_VALUE, 'value3', 0),
+        KS_END); // don't forget the KS_END at the end!
+// work with it
+ksDel (config);
+
+.fi
+.PP
+ Alloc size is 500, the size of the keyset will be 3 after ksNew. This means the keyset will reallocate when appending more than 497 keys.
+.PP
+The main benefit of taking a list of variant length parameters is to be able to have one C-Statement for any possible KeySet.
+.PP
+Due to ABI compatibility, the \fCKeySet\fP structure is only declared in kdb.h, and not defined. So you can only declare \fCpointers\fP to \fCKeySets\fP in your program. See http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksDel()\fP to free the \fBKeySet :: Class Methods\fP afterwards 
+.PP
+\fBksDup()\fP to duplicate an existing \fBKeySet :: Class Methods\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIalloc\fP gives a hint for the size how many Keys may be stored initially 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a ready to use KeySet object 
+.PP
+0 on memory error 
+.RE
+.PP
+
+.SS "Key* ksNext (KeySet * ks)"
+.PP
+Returns the next Key in a KeySet.
+.PP
+KeySets have an internal cursor that can be reset with \fBksRewind()\fP. Every time \fBksNext()\fP is called the cursor is incremented and the new current Key is returned.
+.PP
+You'll get a NULL pointer if the key after the end of the KeySet was reached. It will set the cursor to the beginning of the KeySet and the next time the first key is returned.
+.PP
+The \fCks\fP internal cursor will be changed, so it is not const.
+.PP
+\fBNote:\fP
+.RS 4
+You must not delete or change the key, use \fBksPop()\fP if you want to delete it.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the new current Key 
+.PP
+0 when the end is reached 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksRewind()\fP, \fBksCurrent()\fP 
+.RE
+.PP
+
+.SS "Key* ksPop (KeySet * ks)"
+.PP
+Remove and return the last key of \fCks\fP.
+.PP
+The reference counter will be decremented by one.
+.PP
+The KeySets cursor will not be effected if it did not point to the popped key.
+.PP
+\fBNote:\fP
+.RS 4
+You need to \fBkeyDel()\fP the key afterwards, if you don't append it to another keyset. It has the same semantics like a key allocated with \fBkeyNew()\fP or \fBkeyDup()\fP.
+.RE
+.PP
+.PP
+.nf
+ks1=ksNew(0);
+ks2=ksNew(0);
+
+k1=keyNew(0); // ref counter 0
+ksAppendKey(ks1, k1); // ref counter 1
+ksAppendKey(ks2, k1); // ref counter 2
+
+k1=ksPop (ks1); // ref counter 1
+k1=ksPop (ks2); // ref counter 0, like after keyNew()
+
+ksAppendKey(ks1, k1); // ref counter 1
+
+ksDel (ks1); // key is deleted too
+ksDel (ks2);
+ *
+.fi
+.PP
+.PP
+\fBReturns:\fP
+.RS 4
+the last key of \fCks\fP 
+.PP
+NULL if \fCks\fP is empty or on NULL pointer 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP KeySet to work with 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksAppendKey()\fP, \fBksAppend()\fP 
+.PP
+commandList() for an example 
+.RE
+.PP
+
+.SS "int ksRewind (KeySet * ks)"
+.PP
+Rewinds the KeySet internal cursor.
+.PP
+Use it to set the cursor to the beginning of the KeySet. \fBksCurrent()\fP will then always return NULL afterwards. So you want to \fBksNext()\fP first.
+.PP
+.PP
+.nf
+ksRewind (ks);
+while ((key = keyNext (ks))!=0) {}
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 on success 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNext()\fP, \fBksCurrent()\fP 
+.RE
+.PP
+
+.SS "int ksSetCursor (KeySet * ks, cursor_t cursor)"
+.PP
+Set the KeySet internal cursor.
+.PP
+Use it to set the cursor to a stored position. \fBksCurrent()\fP will then be the position which you got with.
+.PP
+\fBWarning:\fP
+.RS 4
+Cursors may get invalid when the key was \fBksPop()\fPed or \fBksLookup()\fP was used together with KDB_O_POP.
+.RE
+.PP
+.PP
+.nf
+cursor_t cursor;
+..
+// key now in any position here
+cursor = ksGetCursor (ks);
+while ((key = keyNext (ks))!=0) {}
+ksSetCursor (ks, cursor); // reset state
+ksCurrent(ks); // in same position as before
+.fi
+.PP
+.PP
+An invalid cursor will set the keyset to its beginning like \fBksRewind()\fP. When you set an invalid cursor \fBksCurrent()\fP is 0 and \fBksNext()\fP == \fBksHead()\fP.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIcursor\fP the cursor to use 
+.br
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+0 when the keyset is \fBksRewind()\fPed 
+.PP
+1 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksNext()\fP, \fBksGetCursor()\fP 
+.RE
+.PP
+
+.SS "void ksSort (KeySet * ks)"
+.PP
+Sorts a KeySet alphabetically by Key name.
+.PP
+You need \fBksSort()\fP only in few cases directly.
+.SH "Don't need to sort before using ksLookup"
+.PP
+You don't need it if you just just \fBkdbGet()\fP and subsequent \fBksLookup()\fP. 
+.PP
+.nf
+KeySet *ks = ksNew(0);
+kdbGet(h, ks, k, 0);
+// ksPop(), ksAppend() and ksAppendKey() allowed here
+ksLookup(ks, s, 0); // you dont need to sort ks
+
+.fi
+.PP
+.PP
+This is because the KeySet tracks if it needs to be sorted and \fBksLookup()\fP will sort when needed.
+.SH "Sort when iterating"
+.PP
+Before you iterate over a keyset you have to sort it, if you need it in a sorted way.
+.PP
+To achieve that you can pass option_t::KDB_O_SORT to \fBkdbGet()\fP and \fBkdbSet()\fP. Then you will receive a already sorted keyset over which you can iterate. 
+.PP
+.nf
+KeySet *ks = ksNew(0);
+kdbGet(h, ks, k, KDB_O_SORT);
+// no changes to keyset allowed
+ksRewind(ks);
+// now you can iterate over a sorted keyset
+
+.fi
+.PP
+.PP
+Its of course also possible to use \fBksLookup()\fP once, because it will sort the keyset too.
+.SH "Sort when changing key"
+.PP
+\fBWarning:\fP
+.RS 4
+You should not use \fBkeySetName()\fP or \fBkeyRemove()\fP when a key belongs to a keyset. When you are doing this, you always need to \fCmanually\fP sort \fCall\fP keysets where the key was before using \fBksLookup()\fP (otherwise \fBksLookup()\fP won't find that keys), \fBkdbGet()\fP or \fBkdbSet()\fP methods.
+.RE
+.PP
+When you change a key's name or its remove status the order which was previously correctly is then wrong. The keyset does not recognize this, so the programmer has to take care that \fBksSort()\fP is called before any operation which needs a sorted keyset (which are all \fBksLookup()\fP, all \fBkdbGet()\fP and all \fBkdbSet()\fP functions).
+.PP
+\fBNote:\fP
+.RS 4
+You can remember that easily that all functions which get options require one of the following:
+.IP "\(bu" 2
+that you did not manipulate a keys name or a remove status
+.IP "\(bu" 2
+that you pass KDB_O_SORT when you know that you manipulated at least one key
+.IP "\(bu" 2
+that you \fBksSort()\fP yourself after manipulating keys
+.PP
+.RE
+.PP
+.SH "Dirty KeySet"
+.PP
+When you use \fBksAppend()\fP, \fBksAppendKey()\fP, \fBksPop()\fP the keyset is dirty afterwards, which means that it needs to be sorted. This is done automatically using a \fBksLookup()\fP method and in ksGet() or ksSet() (All methods which accept options).
+.PP
+It won't be done if you just iterate over the keyset, so you might use a \fBksLookup()\fP or \fBksSort()\fP first. \fBksLookup()\fP will be more efficient in that case, because it will only sort when needed. Don't pass KDB_O_NOALL (it will deactivate the sorting feature), see \fBksLookup()\fP for more information.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP KeySet to be sorted 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkdbGet()\fP, \fBkdbSet()\fP, \fBksLookup()\fP for some functions which may need sorting before they are called. (All functions which take options as arguments) 
+.PP
+\fBkeySetName()\fP, \fBkeySetBaseName()\fP, \fBkeyAddBaseName()\fP and \fBkeyRemove()\fP for all methods which change the sorting state where the \fBKeySet :: Class Methods\fP can't track the change. 
+.PP
+\fBksAppend()\fP, \fBksAppendKey()\fP, \fBksPop()\fP for all methods which make a \fBKeySet :: Class Methods\fP dirty. 
+.RE
+.PP
+
+.SS "Key* ksTail (const KeySet * ks)"
+.PP
+Return the last key in the KeySet.
+.PP
+The KeySets cursor will not be effected.
+.PP
+If \fBksCurrent()\fP==ksTail() you know you are on the last key. \fBksNext()\fP will return a NULL pointer afterwards.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the last Key of a keyset 
+.PP
+0 on NULL pointer or empty keyset 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksHead()\fP for the first \fBKey :: Basic Methods\fP 
+.PP
+\fBksRewind()\fP, \fBksCurrent()\fP and \fBksNext()\fP for iterating over the \fBKeySet :: Class Methods\fP 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/keytest.3 b/doc/elektra-api/man/man3/keytest.3
new file mode 100644 (file)
index 0000000..c642173
--- /dev/null
@@ -0,0 +1,392 @@
+.TH "Key :: Methods for Making Tests" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+Key :: Methods for Making Tests \- Methods to do various tests on Keys.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBkeyNeedStat\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyNeedSync\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyNeedRemove\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyIsSystem\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyIsUser\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyIsBelow\fP (const Key *key, const Key *check)"
+.br
+.ti -1c
+.RI "int \fBkeyIsDirectBelow\fP (const Key *key, const Key *check)"
+.br
+.ti -1c
+.RI "int \fBkeyIsInactive\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyIsDir\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyIsBinary\fP (const Key *key)"
+.br
+.ti -1c
+.RI "int \fBkeyIsString\fP (const Key *key)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to do various tests on Keys. 
+.PP
+To use them: 
+.PP
+.nf
+#include <kdb.h>
+
+.fi
+.PP
+.SH "Function Documentation"
+.PP 
+.SS "int keyIsBelow (const Key * key, const Key * check)"
+.PP
+Check if the key check is below the key key or not.
+.PP
+.PP
+.nf
+Example:
+key user/sw/app
+check user/sw/app/key
+
+returns true because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+returns also true because check is indirect below key
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIcheck\fP the key to find the relative position of 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if check is below key 
+.PP
+0 if it is not below or if it is the same key 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetName()\fP, \fBkeyGetName()\fP, \fBkeyIsDirectBelow()\fP 
+.RE
+.PP
+
+.SS "int keyIsBinary (const Key * key)"
+.PP
+Check if a key is binary type.
+.PP
+The function checks if the keytype is in the range between KEY_TYPE_BINARY and less than excluding KEY_TYPE_STRING. Then it will be interpreted as binary.
+.PP
+Make sure to use this function and don't test the binary type another way to ensure compatibility and to write less error prone programs.
+.PP
+\fBReturns:\fP
+.RS 4
+1 if it is binary 
+.PP
+0 if it is not 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetType()\fP for more information on types 
+.PP
+\fBkeyGetBinary()\fP, \fBkeySetBinary()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key to check 
+.RE
+.PP
+
+.SS "int keyIsDir (const Key * key)"
+.PP
+Check if a key is directory key.
+.PP
+Folder keys may also have value and comment. They are discern by having a executable bit set.
+.PP
+If any executable bit is set it will be recognized as a directory.
+.PP
+\fBNote:\fP
+.RS 4
+keyIsDir may return true even though you can't access the directory.
+.RE
+.PP
+To know if you can access the directory, you need to check, if your
+.IP "\(bu" 2
+user ID is equal the key's user ID and the mode & 100 is true
+.IP "\(bu" 2
+group ID is equal the key's group ID and the mode & 010 is true
+.IP "\(bu" 2
+mode & 001 is true
+.PP
+.PP
+Accessing does not mean that you can get any value or comments below, see \fBMode\fP for more information.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if key is a directory, 0 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetDir()\fP, \fBkeySetMode()\fP 
+.RE
+.PP
+
+.SS "int keyIsDirectBelow (const Key * key, const Key * check)"
+.PP
+Check if the key check is direct below the key key or not.
+.PP
+.PP
+.nf
+Example:
+key user/sw/app
+check user/sw/app/key
+
+returns true because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+does not return true, because there is only a indirect relation
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIcheck\fP the key to find the relative position of 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if check is below key 
+.PP
+0 if it is not below or if it is the same key 
+.PP
+-1 on null pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyIsBelow()\fP, \fBkeySetName()\fP, \fBkeyGetName()\fP 
+.RE
+.PP
+
+.SS "int keyIsInactive (const Key * key)"
+.PP
+Check whether a key is inactive or not.
+.PP
+In elektra terminology any key is inactive if the it's basename starts with '.'. Inactive keys must not have any meaning to applications, they are reserved for users and administrators.
+.PP
+To remove a whole hierarchy in elektra, don't forget to pass option_t::KDB_O_INACTIVE to \fBkdbGet()\fP to receive the inactive keys in order to remove them.
+.PP
+Otherwise you should not fetch these keys.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if the key is inactive, 0 otherwise 
+.PP
+-1 on NULL pointer or when key has no name 
+.RE
+.PP
+
+.SS "int keyIsString (const Key * key)"
+.PP
+Check if a key is string type.
+.PP
+The function checks if the keytype is larger or equal KEY_TYPE_STRING. Then it will be considered as string type.
+.PP
+Make sure to use this function and don't test the string type another way to ensure compatibility and to write less error prone programs.
+.PP
+\fBReturns:\fP
+.RS 4
+1 if it is string 
+.PP
+0 if it is not 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeySetType\fP for more information on types 
+.PP
+\fBkeyGetString()\fP, \fBkeySetString()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key to check 
+.RE
+.PP
+
+.SS "int keyIsSystem (const Key * key)"
+.PP
+Check whether a key is under the \fCsystem\fP namespace or not
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if key name begins with \fCsystem\fP, 0 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyIsUser()\fP, \fBkeySetName()\fP, \fBkeyName()\fP 
+.RE
+.PP
+
+.SS "int keyIsUser (const Key * key)"
+.PP
+Check whether a key is under the \fCuser\fP namespace or not.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if key name begins with \fCuser\fP, 0 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyIsSystem()\fP, \fBkeySetName()\fP, \fBkeyName()\fP 
+.RE
+.PP
+
+.SS "int keyNeedRemove (const Key * key)"
+.PP
+Ask if key is marked for permanent remove.
+.PP
+Ask if the key will be removed instead of writing in the key database when doing \fBkdbSetKey()\fP or \fBkdbSet()\fP.
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyRemove()\fP 
+.PP
+\fBkdbSet()\fP, \fBkdbSetKey()\fP, \fBkdbRemove()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if it is marked, 0 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
+.SS "int keyNeedStat (const Key * key)"
+.PP
+Ask if key is marked for stat only.
+.PP
+Ask if the key will be stat instead of get it from the key database completely doing \fBkdbGetKey()\fP or \fBkdbGet()\fP. This is useful if you are not interested in the value, comment or key type.
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyStat()\fP, \fBkdbGet()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if it is marked, 0 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
+.SS "int keyNeedSync (const Key * key)"
+.PP
+Test if a key needs to be synced to backend storage.
+.PP
+If any key modification took place the key will be flagged with KEY_FLAG_SYNC so that \fBkdbSet()\fP knows which keys were modified and which not.
+.PP
+After \fBkeyNew()\fP the flag will normally be set, but after \fBkdbGet()\fP and \fBkdbSet()\fP the flag will be removed. When you modify the key the flag will be set again.
+.PP
+In your application you can make use of that flag to know if you changed something in a key after a \fBkdbGet()\fP or \fBkdbSet()\fP.
+.PP
+\fBNote:\fP
+.RS 4
+Note that also changes in the meta data will set that flag.
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyNew()\fP 
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 if \fCkey\fP was changed in memory, 0 otherwise 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/keyvalue.3 b/doc/elektra-api/man/man3/keyvalue.3
new file mode 100644 (file)
index 0000000..6f2f417
--- /dev/null
@@ -0,0 +1,551 @@
+.TH "Key :: Value Manipulation Methods" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+Key :: Value Manipulation Methods \- Methods to do various operations on Key values.  
+
+.PP
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "const void * \fBkeyValue\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetValueSize\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetString\fP (const Key *key, char *returnedString, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeySetString\fP (Key *key, const char *newStringValue)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetBinary\fP (const Key *key, void *returnedBinary, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeySetBinary\fP (Key *key, const void *newBinary, size_t dataSize)"
+.br
+.ti -1c
+.RI "const char * \fBkeyComment\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetCommentSize\fP (const Key *key)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyGetComment\fP (const Key *key, char *returnedComment, size_t maxSize)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeySetComment\fP (Key *key, const char *newComment)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to do various operations on Key values. 
+.PP
+A key can contain a value in different format. The most likely situation is, that the value is interpreted as text. Use \fBkeyGetString()\fP for that. You can save any Unicode Symbols and Elektra will take care that you get the same back, independent of your current environment.
+.PP
+In some situations this idea fails. When you need exactly the same value back without any interpretation of the characters, there is \fBkeySetBinary()\fP. If you use that, its very likely that your Configuration is not according to the standard. Also for Numbers, Booleans and Date you should use \fBkeyGetString()\fP. To do so, you might use strtod() strtol() and then atol() or atof() to convert back.
+.PP
+To use them: 
+.PP
+.nf
+#include <kdb.h>
+
+.fi
+.PP
+.SH "Function Documentation"
+.PP 
+.SS "const char* keyComment (const Key * key)"
+.PP
+Return a pointer to the real internal \fCkey\fP comment.
+.PP
+This is a much more efficient version of \fBkeyGetComment()\fP and you should use it if you are responsible enough to not mess up things. You are not allowed to change anything in the memory region the returned pointer points to.
+.PP
+\fBkeyComment()\fP returns '' when there is no keyComment. The reason is 
+.PP
+.nf
+key=keyNew(0);
+keySetComment(key,'');
+keyComment(key); // you would expect '' here
+keyDel(key);
+
+.fi
+.PP
+.PP
+See \fBkeySetComment()\fP for more information on comments.
+.PP
+\fBNote:\fP
+.RS 4
+Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \fBkeyComment()\fP method to set a new value. Use \fBkeySetComment()\fP instead.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to the internal managed comment 
+.PP
+'' when there is no comment 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetCommentSize()\fP for size and \fBkeyGetComment()\fP as alternative 
+.RE
+.PP
+
+.SS "ssize_t keyGetBinary (const Key * key, void * returnedBinary, size_t maxSize)"
+.PP
+Get the value of a key as a binary.
+.PP
+If the type is not binary -1 will be returned.
+.PP
+When the binary data is empty (this is not the same as ''!) 0 will be returned and the returnedBinary will not be changed.
+.PP
+For string values see \fBkeyGetString()\fP and \fBkeyIsString()\fP.
+.PP
+When the returnedBinary is to small to hold the data (its maximum size is given by maxSize), the returnedBinary will not be changed and -1 is returned.
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+Key *key = keyNew ('user/keyname', KEY_TYPE, KEY_TYPE_BINARY, KEY_END);
+char buffer[300];
+
+if (keyGetBinary(key,buffer,sizeof(buffer)) == -1)
+{
+        // handle error
+}
+
+.fi
+.PP
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the object to gather the value from 
+.br
+\fIreturnedBinary\fP pre-allocated memory to store a copy of the key value 
+.br
+\fImaxSize\fP number of bytes of pre-allocated memory in \fCreturnedBinary\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually copied to \fCreturnedBinary\fP 
+.PP
+0 if the binary is empty 
+.PP
+-1 on NULL pointers 
+.PP
+-1 when maxSize is 0, too small to hold the value or larger than SSIZE_MAX 
+.PP
+-1 on typing error when the key is not binary 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyValue()\fP, \fBkeyGetValueSize()\fP, \fBkeySetBinary()\fP 
+.PP
+\fBkeyGetString()\fP and \fBkeySetString()\fP as preferred alternative to binary 
+.PP
+\fBkeyIsBinary()\fP to see how to check for binary type 
+.RE
+.PP
+
+.SS "ssize_t keyGetComment (const Key * key, char * returnedComment, size_t maxSize)"
+.PP
+Get the key comment.
+.SH "Comments"
+.PP
+A Key comment is description for humans what this key is for. It may be a textual explanation of valid values, when and why a user or administrator changed the key or any other text that helps the user or administrator related to that key.
+.PP
+Don't depend on a comment in your program. A user is always allowed to remove or change it in any way he wants to. But you are allowed or even encouraged to always show the content of the comment to the user and allow him to change it.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIreturnedComment\fP pre-allocated memory to copy the comments to 
+.br
+\fImaxSize\fP number of bytes that will fit returnedComment 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually copied to \fCreturnedString\fP, including final NULL 
+.PP
+1 if the string is empty 
+.PP
+-1 on NULL pointer 
+.PP
+-1 if maxSize is 0, not enough to store the comment or when larger then SSIZE_MAX 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetCommentSize()\fP, \fBkeySetComment()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetCommentSize (const Key * key)"
+.PP
+Calculates number of bytes needed to store a key comment, including final NULL.
+.PP
+Use this method to know to size for allocated memory to retrieve a key comment.
+.PP
+See \fBkeySetComment()\fP for more information on comments.
+.PP
+For an empty key name you need one byte to store the ending NULL. For that reason 1 is returned.
+.PP
+.PP
+.nf
+char *buffer;
+buffer = malloc (keyGetCommentSize (key));
+// use this buffer to store the comment
+// pass keyGetCommentSize (key) for maxSize
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes needed 
+.PP
+1 if there is no comment 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetComment()\fP, \fBkeySetComment()\fP 
+.RE
+.PP
+
+.SS "ssize_t keyGetString (const Key * key, char * returnedString, size_t maxSize)"
+.PP
+Get the value of a key as a string.
+.PP
+When there is no value inside the string, 1 will be returned and the returnedString will be empty '' to avoid programming errors that old strings are shown to the user.
+.PP
+For binary values see \fBkeyGetBinary()\fP and \fBkeyIsBinary()\fP.
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+Key *key = keyNew ('user/keyname', KEY_END);
+char buffer[300];
+
+if (keyGetString(key,buffer,sizeof(buffer)) == -1)
+{
+        // handle error
+} else {
+        printf ('buffer: %s\n', buffer);
+}
+
+.fi
+.PP
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the object to gather the value from 
+.br
+\fIreturnedString\fP pre-allocated memory to store a copy of the key value 
+.br
+\fImaxSize\fP number of bytes of allocated memory in \fCreturnedString\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually copied to \fCreturnedString\fP, including final NULL 
+.PP
+1 if the string is empty 
+.PP
+-1 on NULL pointer 
+.PP
+-1 on type mismatch 
+.PP
+maxSize is 0, too small for string or is larger than SSIZE_MAX 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyValue()\fP, \fBkeyGetValueSize()\fP, \fBkeySetString()\fP 
+.PP
+\fBkeyGetBinary()\fP for working with binary data 
+.RE
+.PP
+
+.SS "ssize_t keyGetValueSize (const Key * key)"
+.PP
+Returns the number of bytes needed to store the key value, including the NULL terminator.
+.PP
+It returns the correct size, independent of the Key Type. If it is a binary there might be '\\0' values in it.
+.PP
+For an empty string you need one byte to store the ending NULL. For that reason 1 is returned. This is not true for binary data, so there might be returned 0 too.
+.PP
+A binary key has no '\\0' termination. String types have it, so to there length will be added 1 to have enough space to store it.
+.PP
+This method can be used with malloc() before \fBkeyGetString()\fP or \fBkeyGetBinary()\fP is called.
+.PP
+.PP
+.nf
+char *buffer;
+buffer = malloc (keyGetValueSize (key));
+// use this buffer to store the value (binary or string)
+// pass keyGetValueSize (key) for maxSize
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes needed to store the key value 
+.PP
+1 when there is no data and type is not binary 
+.PP
+0 when there is no data and type is binary 
+.PP
+-1 on null pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetString()\fP, \fBkeyGetBinary()\fP, \fBkeyValue()\fP 
+.RE
+.PP
+
+.SS "ssize_t keySetBinary (Key * key, const void * newBinary, size_t dataSize)"
+.PP
+Set the value of a key as a binary.
+.PP
+A private copy of \fCnewBinary\fP will allocated and saved inside \fCkey\fP, so the parameter can be deallocated after the call.
+.PP
+The \fCfilesys\fP backend, when used through a \fBkdbSetKey()\fP, will make the value be kdbbEncoded into a human readable hex-digit text format.
+.PP
+Consider using a string key instead.
+.PP
+When newBinary is a NULL pointer the binary will be freed and 0 will be returned.
+.PP
+\fBNote:\fP
+.RS 4
+When the type of the key is already a binary type it won't be changed.
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the object on which to set the value 
+.br
+\fInewBinary\fP is a pointer to any binary data or NULL to free the previous set data 
+.br
+\fIdataSize\fP number of bytes to copy from \fCnewBinary\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually copied to internal struct storage 
+.PP
+0 when the internal binary was freed 
+.PP
+-1 on NULL pointer 
+.PP
+-1 when dataSize is 0 (but newBinary not NULL) or larger than SSIZE_MAX 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetBinary()\fP 
+.PP
+\fBkeyIsBinary()\fP to check if the type is binary 
+.PP
+\fBkeyGetString()\fP and \fBkeySetString()\fP as preferred alternative to binary 
+.RE
+.PP
+
+.SS "ssize_t keySetComment (Key * key, const char * newComment)"
+.PP
+Set a comment for a key.
+.PP
+A key comment is like a configuration file comment. See \fBkeySetComment()\fP for more information.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fInewComment\fP the comment, that can be freed after this call. 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually saved including final NULL 
+.PP
+1 when the comment was freed 
+.PP
+-1 on NULL pointer or memory problems 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetComment()\fP 
+.RE
+.PP
+
+.SS "ssize_t keySetString (Key * key, const char * newStringValue)"
+.PP
+Set the value for \fCkey\fP as \fCnewStringValue\fP.
+.PP
+The function will allocate and save a private copy of \fCnewStringValue\fP, so the parameter can be freed after the call.
+.PP
+String values will be saved in backend storage, when \fBkdbSetKey()\fP will be called, in UTF-8 universal encoding, regardless of the program's current encoding, when compiled with --enable-iconv.
+.PP
+The type will be set to KEY_TYPE_STRING. When the type of the key is already a string type it won't be changed.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key to set the string value 
+.br
+\fInewStringValue\fP NULL-terminated text string to be set as \fCkey's\fP value 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+the number of bytes actually saved in private struct including final NULL 
+.PP
+-1 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetString()\fP, \fBkeyValue()\fP 
+.RE
+.PP
+
+.SS "const void* keyValue (const Key * key)"
+.PP
+Return a pointer to the real internal \fCkey\fP value.
+.PP
+This is a much more efficient version of \fBkeyGetString()\fP \fBkeyGetBinary()\fP, and you should use it if you are responsible enough to not mess up things. You are not allowed to modify anything in the returned string. If you need a copy of the Value, consider to use \fBkeyGetString()\fP or \fBkeyGetBinary()\fP instead.
+.SH "String Handling"
+.PP
+If \fCkey\fP is string (\fBkeyIsString()\fP), you may cast the returned as a \fC'char *'\fP because you'll get a NULL terminated regular string.
+.PP
+\fBkeyValue()\fP returns '' in string mode when there is no value. The reason is 
+.PP
+.nf
+key=keyNew(0);
+keySetString(key,'');
+keyValue(key); // you would expect '' here
+keyDel(key);
+
+.fi
+.PP
+.SH "Binary Data Handling"
+.PP
+If the data is binary, the size of the value must be determined by \fBkeyGetValueSize()\fP, any strlen() operations are not suitable to determine the size.
+.PP
+\fBkeyValue()\fP returns 0 in binary mode when there is no value. The reason is 
+.PP
+.nf
+int i=23;
+key=keyNew(0);
+keySetBinary(key, 0, 0);
+keyValue(key); // you would expect 0 here
+
+keySetBinary(key,'', 1);
+keyValue(key); // you would expect '' (a pointer to '\0') here
+
+keySetBinary(key, (void*)&i, 4);
+(int*)keyValue(key); // you would expect a pointer to (int)23 here
+keyDel(key);
+
+.fi
+.PP
+.PP
+\fBNote:\fP
+.RS 4
+Note that the Key structure keeps its own size field that is calculated by library internal calls, so to avoid inconsistencies, you must never use the pointer returned by \fBkeyValue()\fP method to set a new value. Use \fBkeySetString()\fP or \fBkeySetBinary()\fP instead.
+.RE
+.PP
+\fBWarning:\fP
+.RS 4
+Binary keys will return a NULL pointer when there is no data in contrast to \fBkeyName()\fP, \fBkeyBaseName()\fP, \fBkeyOwner()\fP and \fBkeyComment()\fP. For string value the behaviour is the same.
+.RE
+.PP
+\fBExample:\fP.RS 4
+
+.PP
+.nf
+KDB *handle = kdbOpen();
+KeySet *ks=ksNew(0);
+Key *current=0;
+
+kdbGetByName(handle,ks,'system/sw/my',KDB_O_SORT|KDB_O_RECURSIVE);
+
+ksRewind(ks);
+while(current=ksNext(ks)) {
+        size_t size=0;
+        
+        if (keyIsBin(current)) {
+                size=keyGetValueSize(current);
+                printf('Key %s has a value of size %d bytes. Value: <BINARY>\nComment: %s',
+                        keyName(current),
+                        size,
+                        keyComment(current));
+        } else {
+                size=kdbiStrLen((char *)keyValue(current));
+                printf('Key %s has a value of size %d bytes. Value: %s\nComment: %s',
+                        keyName(current),
+                        size,
+                        (char *)keyValue(current),
+                        keyComment(current));
+        }
+}
+
+ksDel (ks);
+kdbClose (handle);
+
+.fi
+.PP
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+a pointer to internal value 
+.PP
+'' when there is no data and key is not binary 
+.PP
+0 where there is no data and key is binary 
+.PP
+0 on NULL pointer 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyGetValueSize()\fP, \fBkeyGetString()\fP, \fBkeyGetBinary()\fP 
+.RE
+.PP
+
diff --git a/doc/elektra-api/man/man3/stream.3 b/doc/elektra-api/man/man3/stream.3
new file mode 100644 (file)
index 0000000..0e92608
--- /dev/null
@@ -0,0 +1,507 @@
+.TH "Streaming" 3 "30 Jun 2009" "Elektra Projekt" \" -*- nroff -*-
+.ad l
+.nh
+.SH NAME
+Streaming \- Methods to output, generate and toXML Keys and Keysets.  
+
+.PP
+.SS "Enumerations"
+
+.in +1c
+.ti -1c
+.RI "enum \fBKDBStream\fP { \fBKDB_O_SHOWMETA\fP = 0xF0, \fBKDB_O_SHOWFLAGS\fP = 1<<14, \fBKDB_O_SHOWINDICES\fP = 1<<15, \fBKDB_O_CONDENSED\fP = 1<<16, \fBKDB_O_NUMBER\fP = 1<<17, \fBKDB_O_HEADER\fP = 1<<18, \fBKDB_O_FULLNAME\fP = 1<<19, \fBKDB_O_HIER\fP = 1<<20 }"
+.br
+.in -1c
+.SS "Functions"
+
+.in +1c
+.ti -1c
+.RI "int \fBksFromXMLfile\fP (KeySet *ks, const char *filename)"
+.br
+.ti -1c
+.RI "int \fBksFromXML\fP (KeySet *ks, int fd)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyToStream\fP (const Key *key, FILE *stream, option_t options)"
+.br
+.ti -1c
+.RI "ssize_t \fBkeyToStreamBasename\fP (const Key *key, FILE *stream, const char *parent, const size_t parentSize, option_t options)"
+.br
+.ti -1c
+.RI "ssize_t \fBksToStream\fP (const KeySet *ks, FILE *stream, option_t options)"
+.br
+.ti -1c
+.RI "int \fBkeyOutput\fP (const Key *k, FILE *stream, option_t options)"
+.br
+.ti -1c
+.RI "int \fBksOutput\fP (const KeySet *ks, FILE *stream, option_t options)"
+.br
+.ti -1c
+.RI "int \fBkeyGenerate\fP (const Key *key, FILE *stream, option_t options)"
+.br
+.ti -1c
+.RI "int \fBksGenerate\fP (const KeySet *ks, FILE *stream, option_t options)"
+.br
+.in -1c
+.SH "Detailed Description"
+.PP 
+Methods to output, generate and toXML Keys and Keysets. 
+.PP
+Here are some functions that are in a separate library because they depend on non-basic libraries as libxml. Link against kdbtools library if your program won't be installed in /bin, or is not essential in early boot stages.
+.PP
+It is also possible to have a dynamic linkage, see the sourcecode from kdb-tool or testing framework how to achieve that.
+.PP
+Output prints keys line per line, meaned to be read by humans.
+.IP "\(bu" 2
+\fBkeyOutput()\fP
+.IP "\(bu" 2
+\fBksOutput()\fP
+.PP
+.PP
+Generate prints keys and keysets as C-Structs, meaned to be read by a C-Compiler.
+.IP "\(bu" 2
+\fBkeyGenerate()\fP
+.IP "\(bu" 2
+\fBksGenerate()\fP
+.PP
+.PP
+toXML prints keys and keysets as XML, meaned to be used as exchange format.
+.IP "\(bu" 2
+\fBkeyToStream()\fP
+.IP "\(bu" 2
+\fBksToStream()\fP
+.PP
+.PP
+To use them: 
+.PP
+.nf
+#include <kdbtools.h>
+
+.fi
+.PP
+.SH "Enumeration Type Documentation"
+.PP 
+.SS "enum \fBKDBStream\fP"
+.PP
+Options to change the default behavior of streaming.
+.PP
+On default the streaming options only output the names of the given keysets. If you want more information, header, metainfo, compressed output, full names, values or comments you will find the appropriate options here.
+.PP
+For full influence of value, comment and metadata shown, use these options together with keyswitch_t. All bits of meta-information ORed together are KDB_O_SHOWMETA.
+.PP
+For more information about the flags, consult the documentation of the streaming methods.
+.PP
+These options can be ORed. That is the |-Operator in C.
+.PP
+It uses the values defined in keyswitch_t too, so it starts with 14.
+.PP
+\fBSee also:\fP
+.RS 4
+kdbGetChildKeys() 
+.PP
+\fBksToStream()\fP 
+.PP
+\fBkeyToStream()\fP 
+.RE
+.PP
+
+.PP
+\fBEnumerator: \fP
+.in +1c
+.TP
+\fB\fIKDB_O_SHOWMETA \fP\fP
+Show all metadata (type, uid, gid, mode) 
+.TP
+\fB\fIKDB_O_SHOWFLAGS \fP\fP
+Show all flags 
+.TP
+\fB\fIKDB_O_SHOWINDICES \fP\fP
+Show the indices for the entries 
+.TP
+\fB\fIKDB_O_CONDENSED \fP\fP
+Spare any whitespaces and do not group visually together. 
+.TP
+\fB\fIKDB_O_NUMBER \fP\fP
+Use a number intead of user and group name. 
+.TP
+\fB\fIKDB_O_HEADER \fP\fP
+Show also the header of the document. 
+.TP
+\fB\fIKDB_O_FULLNAME \fP\fP
+Export \fCuser\fP keys using full name. 
+.TP
+\fB\fIKDB_O_HIER \fP\fP
+Export to the new hierarchical XML representation using key basename. See \fBksToStream()\fP. 
+.SH "Function Documentation"
+.PP 
+.SS "int keyGenerate (const Key * key, FILE * stream, option_t options)"
+.PP
+Generate a C-Style key and stream it.
+.PP
+This keyset can be used to include as c-code for applikations using elektra.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIstream\fP the file pointer where to send the stream 
+.br
+\fIoptions\fP KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on success 
+.RE
+.PP
+
+.SS "int keyOutput (const Key * k, FILE * stream, option_t options)"
+.PP
+Output every information of a single key depending on options.
+.PP
+The format is not very strict and only intend to be read by human eyes for debugging purposes. Don't rely on the format in your applications.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIk\fP the key object to work with 
+.br
+\fIstream\fP the file pointer where to send the stream 
+.br
+\fIoptions\fP see text above 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksOutput()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on success 
+.PP
+-1 on allocation errors 
+.RE
+.PP
+
+.SS "ssize_t keyToStream (const Key * key, FILE * stream, option_t options)"
+.PP
+Prints an XML representation of the key.
+.PP
+String generated is of the form: 
+.PP
+.nf
+
+       <key name="system/sw/xorg/Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>
+.fi
+.PP
+.PP
+.PP
+.nf
+
+       <key parent="system/sw/xorg/Monitor/Monitor0" basename="Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIstream\fP where to write output: a file or stdout 
+.br
+\fIoptions\fP Some option_t ORed:
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NUMBERS\fP 
+.br
+ Do not convert UID and GID into user and group names
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_CONDENSED\fP\fP 
+.br
+ Less human readable, more condensed output
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_FULLNAME\fP\fP 
+.br
+ The \fCuser\fP keys are exported with their full names (including user domains)
+.PP
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBksToStream()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to output 
+.RE
+.PP
+
+.SS "ssize_t keyToStreamBasename (const Key * key, FILE * stream, const char * parent, const size_t parentSize, option_t options)"
+.PP
+Same as \fBkeyToStream()\fP but tries to strip \fCparentSize\fP bytes from \fCkey\fP name if it matches \fCparent\fP .
+.PP
+Taking the example from \fBkeyToStream()\fP, if \fCparent\fP is \fC'system/sw/xorg'\fP, the generated string is of the form: 
+.PP
+.nf
+
+       <key basename="Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>
+.fi
+.PP
+.PP
+It usefull to produce more human readable XML output of a key when it is being represented in a context that defines the parent key name. For example:
+.PP
+.PP
+.nf
+
+       <keyset parent="user/sw">
+               <key basename="kdbedit"..../>
+               <key basename="phototools"..../>
+               <key basename="myapp"..../>
+       </keyset>.fi
+.PP
+.PP
+In the bove example, each \fC<key>\fP entry was generated by a call to \fBkeyToStreamBasename()\fP having \fC'user/sw'\fP as \fCparent\fP .
+.PP
+This method is used when \fBksToStream()\fP is called with \fBKDBOption::KDB_O_HIER\fP option.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIkey\fP the key object to work with 
+.br
+\fIstream\fP the FILE where to send the stream 
+.br
+\fIparentSize\fP the maximum size of \fCparent\fP that will be used. If 0, the entire \fCparent\fP will be used. 
+.br
+\fIparent\fP the string (or part of it, defined by \fCparentSize\fP ) that will be used to strip from the key name. 
+.br
+\fIoptions\fP Some option_t ORed:
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NUMBERS\fP 
+.br
+ Do not convert UID and GID into user and group names
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_CONDENSED\fP\fP 
+.br
+ Less human readable, more condensed output
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_FULLNAME\fP\fP 
+.br
+ The \fCuser\fP keys are exported with their full names (including user domains)
+.PP
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to output 
+.RE
+.PP
+
+.SS "int ksFromXML (KeySet * ks, int fd)"
+.PP
+FIXME: not working when fd is stdin Given a file descriptor (that can be \fCstdin\fP) for an XML file, validate schema, process nodes, convert and save it in the \fCks\fP KeySet.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP keyset 
+.br
+\fIfd\fP Filedeskriptor?? should be FILE* 
+.RE
+.PP
+
+.SS "int ksFromXMLfile (KeySet * ks, const char * filename)"
+.PP
+Given an XML \fCfilename\fP, open it, validate schema, process nodes, convert and save it in the \fCks\fP KeySet.
+.PP
+Currently, the XML file can have many root \fC<keyset>\fP and \fC<key>\fP nodes. They will all be reduced to simple keys returned in \fCks\fP.
+.PP
+To check if the xml file is valid (best before you read from it): 
+.PP
+.nf
+#include 
+char schemaPath[513];
+schemaPath[0]=0;
+ret=kdbGetString(handle, KDB_SCHEMA_PATH_KEY,schemaPath,sizeof(schemaPath));
+
+if (ret==0) ret = isValidXML(filename,schemaPath);
+else ret = isValidXML(filename,KDB_SCHEMA_PATH); 
+
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset 
+.br
+\fIfilename\fP the file to parse 
+.RE
+.PP
+
+.SS "int ksGenerate (const KeySet * ks, FILE * stream, option_t options)"
+.PP
+Generate a C-Style keyset and stream it.
+.PP
+This keyset can be used to include as c-code for applikations using elektra.
+.PP
+The options takes the same options as \fBkdbGet()\fP and \fBkdbSet()\fP.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset to work with 
+.br
+\fIstream\fP the file pointer where to send the stream 
+.br
+\fIoptions\fP which keys not to output 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on success 
+.RE
+.PP
+
+.SS "int ksOutput (const KeySet * ks, FILE * stream, option_t options)"
+.PP
+Output all information of a keyset.
+.PP
+The format is not very strict and only intend to be read by human eyes for debugging purposes. Don't rely on the format in your applications.
+.PP
+Keys are printed line per line with \fBkeyOutput()\fP.
+.PP
+The same options as \fBkeyOutput()\fP are taken and passed to them.
+.PP
+Additional KDB_O_HEADER will print the number of keys as first line.
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the keyset to work with 
+.br
+\fIstream\fP the file pointer where to send the stream 
+.br
+\fIoptions\fP 
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyOutput()\fP 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+1 on success 
+.PP
+-1 on allocation errors 
+.RE
+.PP
+
+.SS "ssize_t ksToStream (const KeySet * ks, FILE * stream, option_t options)"
+.PP
+Writes to \fCstream\fP an XML version of the \fCks\fP object.
+.PP
+String generated is of the form: 
+.PP
+.nf
+
+<keyset>
+<key name=...>...</key>
+<key name=...>...</key>
+<key name=...>...</key>
+
+</keyset>
+ * 
+.fi
+.PP
+.PP
+or if KDB_O_HIER is used, the form will be: 
+.PP
+.nf
+
+<keyset parent="user/smallest/parent/name">
+
+<key basename=...>...</key>
+<key name=...>...</key> <!-- a key thats not under this keyset's parent -->
+<key basename=...>...</key>
+
+</keyset>
+ * 
+.fi
+.PP
+.PP
+KDB_O_HEADER will additionally generate a header like: 
+.PP
+.nf
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of n keys. -->
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd">
+ * 
+.fi
+.PP
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP the KeySet to serialize 
+.br
+\fIstream\fP where to write output: a file or stdout 
+.br
+\fIoptions\fP accepted option_t ORed:
+.IP "\(bu" 2
+\fCoption_t::KDB_O_NUMBERS\fP 
+.br
+ Do not convert UID and GID into user and group names.
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_FULLNAME\fP\fP 
+.br
+ The \fCuser\fP keys are exported with their full names (including user domains)
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_CONDENSED\fP\fP 
+.br
+ Less human readable, more condensed output.
+.IP "\(bu" 2
+\fCoption_t::KDB_O_XMLHEADERS\fP 
+.br
+ Exclude the correct XML headers in the output. If not used, the <?xml?> and schema info inside the <keyset> object will not be generated.
+.IP "\(bu" 2
+\fC\fBoption_t::KDB_O_HIER\fP\fP 
+.br
+ Will generate a <keyset> node containing a \fCparent\fP attribute, and <key> nodes with a \fCbasename\fP relative to that \fCparent\fP. The \fCparent\fP is calculated by taking the smallest key name in the keyset, so it is a good idea to have only related keys on the keyset. Otherwise, a valid consistent XML document still will be generated with regular absolute \fCname\fP attribute for the <key> nodes, due to a clever \fBkeyToStreamBasename()\fP implementation.
+.PP
+.RE
+.PP
+\fBSee also:\fP
+.RS 4
+\fBkeyToStream()\fP 
+.PP
+commandList() for usage example 
+.RE
+.PP
+\fBReturns:\fP
+.RS 4
+number of bytes written to output, or -1 if some error occurs
+.RE
+.PP
+\fBParameters:\fP
+.RS 4
+\fIks\fP The keyset to output 
+.br
+\fIstream\fP the file pointer where to send the stream 
+.br
+\fIoptions\fP see above text 
+.RE
+.PP
+
diff --git a/doc/elektra.5 b/doc/elektra.5
new file mode 100644 (file)
index 0000000..f6edf9f
--- /dev/null
@@ -0,0 +1,163 @@
+.\"     Title: elektra
+.\"    Author: Avi Alkalay <avi at unix.sh>
+.\" Generator: DocBook XSL Stylesheets v1.73.2 <http://docbook.sf.net/>
+.\"      Date: March 2004
+.\"    Manual: 
+.\"    Source: Elektra Initiative
+.\"
+.TH "ELEKTRA" "5" "March 2004" "Elektra Initiative" ""
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+elektra \- A framework to store configuration atoms hierarchically
+.sp
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+Note
+.PP
+This section is provided for the sake of the openness of Elektra\&. You should not access the Elektra\'s key files directly\&. You should use the API or the
+\fBkdb\fR(1)
+command for that\&.
+
+Elektra Key Storage Strategy.PP
+Elektra implements a very simple way to store the key\-value pairs\&. The value (and some metainfo) for each key is stored in a single file\&. The key name (and some of its context) is sufficient to find the file name that stores the value\&.
+.PP
+The
+\fIsystem/*\fR
+keys are stored under
+\fI/etc/kdb/\fR, and the
+\fIuser/*\fR
+keys can be found under each user\'s
+\fI$HOME/\&.kdb/\fR\&.
+.PP
+Here are some examples of key names, and where Elektra goes to look for them in the disk\&.
+.PP
+\fIsystem/sw/XFree86/screen0/driver\fR
+.RS 4
+Maps to:
+\fI/etc/kdb/system/sw/XFree86/screen0/driver\fR
+.RE
+.PP
+\fIuser/env/PATH\fR
+.RS 4
+Maps to:
+\fI~$USER/\&.kdb/user/env/PATH\fR
+.RE
+.PP
+\fIuser:luciana/env/PATH\fR
+.RS 4
+Maps to:
+\fI~luciana/\&.kdb/user/env/PATH\fR
+.RE
+.PP
+\fIsystem/mime\&.types/some\&.mime\fR
+.RS 4
+Maps to:
+\fI/etc/kdb/system/mime\&.types/some\&.mime\fR
+.RE
+.PP
+Some may think that one file per key will consume many filesystem i\-nodes\&. Actually, when not using Reiser4 filesystem, it may consume some more disk space, and it may also be not so efficient than reading one single text file, as KConfig does\&. But Elektra\'s nature lets applications load their keys on demand; so it is possible to avoid the read\-all\-use\-some approach\&. Writing updated keys back to disk is also more robust, because unchanged keys won\'t be touched, different from a single file approach, that must be entirelly rewritten\&.
+.PP
+Besides that, big applications (like Mozilla, Konqueror, KDE, Gnome) key gathering time is a very small part of what they have to do to start up\&. And the benefits of an homogeneous key database to the entire system are much bigger then these issues\&. Think about a common integration between everything, flexibility, security granularity and openness\&.
+XML, Storage Backends and Elektra.PP
+This document you are just reading was written in DocBook XML\&. XML is a wonderfull technology, but brings no value to this software\&. Two main goals of the Elektra Project are to be lightweight, and to be accessible by early boot stage programs like
+\fB/sbin/init\fR
+and the
+\fI/etc/rc\&.d\fR
+scripts\&.
+.PP
+XML parsing libraries are memory eaters, not so fast as we can expect, and they are usualy located under
+\fI/usr/lib\fR, which may be unavailable to these early boot stage needs\&.
+.PP
+Some discussions asked for a sort of plugin architecture to let user decide the format to store keys content\&. Well, the info that goes into the key file is not big deal as you\'ll see, and sometimes, too many options is bad business, and not the path for the Elektra Project\&.
+.PP
+So no XML, no plugin architecture, no sophistication\&. Lets keep it simple and generic\&. A very straight forward text based file format was defined to store a single key value\&.
+Key Files Format.PP
+Inside Elektra key database, each key is a file, and every file is a key\&. So most of the key\'s metainformation are actually its file attributes, as you can see in a regular
+\fBls\fR(1)
+command output\&.
+.PP
+So what needs to be stored inside the key file is the data type (binary or text), key comment and the actual data\&. The format of each key file is:
+.sp
+.RS 4
+.nf
+File Format Version
+Data Type
+As many lines of
+comments as we want (UTF\-8 encoded)
+<DATA>
+The data encoded as text
+               
+.fi
+.RE
+.PP
+So if we set the key
+\fIsystem/hw/eth0/driver\fR
+as type
+\fBString\fR
+and value "\fI3com\fR", and comment "\fIThe driver for my network interface\fR", we\'ll find the file
+\fI/etc/kdb/system/hw/eth0/driver\fR
+containing:
+.sp
+.RS 4
+.nf
+RG002
+40
+The driver for my network interface
+<DATA>
+3com
+               
+.fi
+.RE
+.PP
+Other example: setting
+\fIuser/tmp/proprietary\fR
+as
+\fBBinary\fR, and value "\fIA groovy data I want to hide\fR", and comment "\fIStay away from here\fR", you\'ll get in
+\fI~$USER/\&.kdb/user/tmp/proprietary\fR
+the following:
+.sp
+.RS 4
+.nf
+RG002
+20
+Stay away from here
+<DATA>
+41206772 6f6f7679 20646174 61204920 77616e74 20746f20 68696465
+               
+.fi
+.RE
+.PP
+The current data types are:
+.PP
+Between 20 and 39
+.RS 4
+Binary value\&. The data will be encoded into a text format\&. Today only type 20 is used, and means a raw stream of bytes with no special semantics to Elektra\&. The other values are reserved for future use; being treated still as binary values but possibly with some semantics to Elektra or a higher level application\&.
+.RE
+.PP
+40 up to 254
+.RS 4
+Text, UTF\-8 encoded\&. Values higher then 40 are reserved for future or application specific implementations of more abstract data types, like time/date, color, font, etc\&. But always represented as pure text that can be edited in any text editor like
+\fBvi\fR(1)\&.
+.RE
+.PP
+Types between 0 and 19 are used only internaly in the API, and will never appear into a key file\&. They are used to define meta keys as directory, link, etc\&.
+.SH "SEE ALSO"
+.PP
+\fBkdb\fR(1),
+\fBelektra\fR(7)
+.SH "AUTHOR"
+.PP
+\fBAvi Alkalay\fR <\&avi at unix\&.sh\&>
+.br
+Linux Market Developer, Senior IT and Software Architect, IBM Linux Impact Team :: \fIibm\&.com/linux\fR
+.sp -1n
+.IP "" 4
+Author.
+.SH "COPYRIGHT"
+Copyright \(co 2004 Avi Alkalay
+.br
diff --git a/doc/elektra.5.xml b/doc/elektra.5.xml
new file mode 100644 (file)
index 0000000..8ea6765
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+       <!ENTITY author SYSTEM "author.xml">
+       <!ENTITY storage SYSTEM "storage.xml">
+]>
+
+<!--
+$Id$
+$LastChangedBy$
+-->
+<refentry id="elektra">
+
+       <refentryinfo>
+               &author;
+       </refentryinfo>
+       
+       <refmeta>
+               <refentrytitle>elektra</refentrytitle>
+               <manvolnum>5</manvolnum>
+       </refmeta>
+
+       <refnamediv>
+               <refname>elektra</refname>
+               <refpurpose>A framework to store configuration atoms hierarchically</refpurpose>
+       </refnamediv>
+
+       &storage;
+               
+       <refsection><title>See Also</title>
+               <para><citerefentry><refentrytitle>kdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>elektra</refentrytitle><manvolnum>7</manvolnum></citerefentry></para>
+       </refsection>
+
+
+</refentry>
diff --git a/doc/elektra.7 b/doc/elektra.7
new file mode 100644 (file)
index 0000000..353a75c
--- /dev/null
@@ -0,0 +1,625 @@
+.\"     Title: elektra
+.\"    Author: Avi Alkalay <avi at unix.sh>
+.\" Generator: DocBook XSL Stylesheets v1.73.2 <http://docbook.sf.net/>
+.\"      Date: March 2004
+.\"    Manual: 
+.\"    Source: Elektra Initiative
+.\"
+.TH "ELEKTRA" "7" "March 2004" "Elektra Initiative" ""
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+elektra \- A framework to store configuration atoms hierarchically
+Library Linkage Architecture.PP
+The Elektra library (\fIlibelektra\&.so\fR) has 2 layers: public methods and backend access, according to the following architecture (these pictures were taken from the
+\fIElektra presentation\fR\&[1]):
+.PP
+.PP
+When using local backends such as the filesys backend, all key access happens in the actual process space as bellow:
+.PP
+.PP
+A remote daemon backend is also possible as noted bellow:
+.PP
+
+True Facts About Elektra
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It is much more an agreement then a piece of software\&. Relation is 99% to 1%\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It is a simple and consistent API to help software developers programatically store and retrieve global and user\-specific configuration parameters\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'All key\-value pairs are stored in clear\-text files, UTF\-8 encoded\&. All old charsets are also supported, with automatic transparent conversion to and from UTF\-8\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'API supports change notifications and multiple backends\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It provides a unique namespace for all values\&. Anywhere, anytime, any program can preciselly access keys by their names\&. Security restrictions may obviously apply\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It is designed to be secure and lightweight, to let even early boot\-stage programs like
+\fB/sbin/init\fR
+to use it, instead of
+\fI/etc/inittab\fR
+file\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It is designed to be easy to administrate with regular command line tools like
+\fBcat\fR,
+\fBvi\fR,
+\fBcp\fR,
+\fBls\fR,
+\fBln\fR\&. Its storage is 100% open\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It tries to set distribution\-independent naming standards to store things like hardware configuration, networking, user\'s session configuration, system\'s mime\-types, parameters for kernel modules, etc, that are generally stored under
+\fI/etc\fR\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It requires existing software to be changed to use its API\&. This will substitute hundreds of configuration\-text\-file parsing code, into clear Elektra\'s API key\-value access methods\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It is POSIX compliant\&. If it doesn\'t compile and run easily on some POSIX system, it should be easily modified to do so\&.
+.RE
+Elektra Is Not
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'Is NOT something that accesses SQL/relational databases\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'Is NOT an OS service that can become unavailable and make system unusable\&. It is just a library to access files according to a namespace\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'Is NOT an alternative to network information systems like LDAP or NIS\&. These are still required for networked environments\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'Is NOT a Webmin\-like or other GUI tool to be used by end users\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'Is NOT an additional software layer to edit/generate existing configuration files\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'Is NOT a "configuration system", because one can\'t be created by simply writing some code\&. A configuration system is an ecosystem, and the Elektra Project tries to help build one\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'It doesn\'t know a thing about the semantics of each data it stores\&.
+.RE
+Namespaces and Key Names.PP
+All keys are organized in a hierarchical tree with 2 Namespaces (subtrees) as showed by the picture:
+.PP
+.PP
+\fBsystem\fR
+.RS 4
+Contains all subsystems and global application keys/configuration\&. Equivalent to files under
+\fI/etc\fR
+directory\&.
+.RE
+.PP
+\fBuser\fR
+.RS 4
+The current user\'s keys\&. Equivalent to the dotfiles in a user\'s
+\fB$HOME\fR
+directory\&. These keys are phisically stored under the owner user home directory\&. The many
+\fIuser:\fR\fIusername\fR
+in the picture shows the full name of those trees\&. Read about user domains bellow for more\&.
+.RE
+User Domains.PP
+Different from the
+\fIsystem\fR
+namespace, the
+\fIuser\fR
+namespace is dynamic\&. For example, the key
+\fIuser/env/PATH\fR
+may have completely different values for users
+\fIluciana\fR
+and
+\fIvaleria\fR\&. In this example, if
+\fIvaleria\fR
+wants to access this key at
+\fIluciana\fR\'s space, it should refer to
+\fIuser:luciana/env/PATH\fR\&. Access permissions apply\&.
+.PP
+User domains were implemented also to address situations when different user names (\fB$USER\fR) have same UID\&. So a user key is stored in his home directory based on the user name, not the UID\&.
+Inactive Keys.PP
+A great thing about text configuration files is that some configuration items can be there as an example, but inactive or commented\&. Elektra provides a very simple way to simulate this behavior: if the key name begins with a dot (\&.), it is considered inactive or commented\&. In real world applications, the Elektra API will ignore these keys by default, but the keys are still accessible if the developer wants to\&.
+.PP
+These are some keys that have inactive subtrees:
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/XFree/InputDevice/\&.Mouse3/Driver\fR: All keys under
+\fI\&.Mouse3/*\fR
+subtree won\'t be read by default\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIuser:valeria/env/env2/\&.PATH\fR: The
+\fB$PATH\fR
+environment variable
+\fIwon\'t\fR
+be set when
+\fIvaleria\fR
+login\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/\&.louis/uid\fR: The entire
+\fI\&.louis/*\fR
+subtree is inactive\&. This is the same as commenting the user entry from a configuration file\&.
+.RE
+.PP
+See bellow more examples of inactive keys\&.
+Key Examples.PP
+Here are some valid key names, and their values:
+.PP
+The Elektra keys of the combined
+\fI/etc/passwd\fR
+and
+\fI/etc/shadow\fR
+entry for user \'nobody\' would look like:
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/uid\fR: 99
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/gid\fR: 99
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/gecos\fR: Nobody
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/home\fR: /
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/shell\fR: /sbin/nologin
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/password\fR: *
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/passwdChangeBefore\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/passwdChangeAfter\fR: 99999
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/passwdWarnBefore\fR: 7
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/passwdDisableAfter\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/passwdDisabledSince\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/users/nobody/passwdReserved\fR:
+.RE
+.PP
+The environment variables I want set, when I log in, with their full key name:
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIuser:aviram/env/env1/JAVA_HOME\fR: /usr/lib/jvm/java\-1\&.4\&.1\-ibm\-1\&.4\&.1\&.0/jre
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIuser:aviram/env/env2/PATH\fR: $PATH:~/bin:$JAVA_HOME/bin
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIuser:aviram/env/env2/PS1\fR: \eh:\ew\e$
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIuser:aviram/env/env3/PILOTRATE\fR: 57600
+.RE
+.PP
+The entry in
+\fI/etc/inittab\fR
+that is responsible for starting X11 would look:
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/init/x/runlevels\fR: 5
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/init/x/action\fR: respawn
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/init/x/process\fR: /etc/X11/prefdm \-nodaemon
+.RE
+.PP
+The users database files and
+\fI/etc/inittab\fR
+were Elektrified to key\-value pairs using the
+\fBusers\-convert\fR
+and
+\fBinittab\-convert\fR
+scripts included with the distribution\&.
+.PP
+An example of an elektrified
+\fI/etc/X11/xorg\&.conf\fR
+or
+\fI/etc/X11/XF86Config\fR:
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Layouts/Default Layout/Inputs/Keyboard0/CoreKeyboard\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Layouts/Default Layout/Inputs/Mouse0/CorePointer\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Layouts/Default Layout/Screens/Screen0/Absolute\&.x\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Layouts/Default Layout/Screens/Screen0/Absolute\&.y\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Layouts/Default Layout/Screens/Screen0/ScreenNumber\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Files/FontPath\fR: unix/:7100
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Files/RgbPath\fR: /usr/X11R6/lib/X11/rgb
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Devices/Videocard0/BoardName\fR: Intel 740 (generic)
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Devices/Videocard0/Driver\fR: i740
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Devices/Videocard0/VendorName\fR: Videocard vendor
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Keyboard0/Driver\fR: keyboard
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Keyboard0/Options/XkbLayout\fR: us_intl
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Keyboard0/Options/XkbModel\fR: pc105
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Mouse0/Driver\fR: mouse
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Mouse0/Options/Device\fR: /dev/input/mice
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Mouse0/Options/Emulate3Buttons\fR: yes
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Mouse0/Options/Protocol\fR: IMPS/2
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/InputDevices/Mouse0/Options/ZAxisMapping\fR: 4 5
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/DisplaySize\&.height\fR: 230
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/DisplaySize\&.width\fR: 300
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/HorizSync\fR: 30\&.0 \- 61\&.0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/ModelName\fR: SyncMaster
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/Options/dpms\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/VendorName\fR: Monitor Vendor
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/Monitor0/VertRefresh\fR: 56\&.0 \- 75\&.0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/\&.Monitor1/HorizSync\fR: 30\&.0 \- 61\&.0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/\&.Monitor1/ModelName\fR: Impression
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/\&.Monitor1/Options/dpms\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/\&.Monitor1/VendorName\fR: Monitor Vendor
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Monitors/\&.Monitor1/VertRefresh\fR: 56\&.0 \- 75\&.0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/DefaultDepth\fR: 16
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/Device\fR: Videocard0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/Displays/00/Depth\fR: 16
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/Displays/00/Modes\fR: 1024x768,800x600,640x480
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/Displays/00/Viewport\&.x\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/Displays/00/Viewport\&.y\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Screens/Screen0/Monitor\fR: Monitor0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/dbe\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/dri\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/extmod\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/fbdevhw\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/freetype\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/glx\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/record\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/Modules/type1\fR:
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/DRI/Group\fR: 0
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'\fIsystem/sw/xorg/current/DRI/Mode\fR: 0666
+.RE
+.PP
+Pay attention that the keys bellow
+\fIsystem/sw/XFree/current/Monitor/\&.Monitor1\fR
+are inactive because we have
+\fI\&.Monitor1\fR
+as their parent\&. So unless special options are used when calling the API, these keys will not be retrieved from the database\&.
+.PP
+Throughout this text you will see other examples of key names\&.
+Key Data Types.PP
+There are only two types of data that can be stored:
+.PP
+\fBText\fR
+.RS 4
+Handled as pure text\&. Regardeless of the charset being used, these values are always stored as UTF\-8\&. This ensures very strong internationalization and migration capabilities, while keeping simplicity\&. If you don\'t want the Elektra framework to convert your non\-ASCII text to UTF\-8 (not recomended), you should use the Binary data format\&.
+.RE
+.PP
+\fBBinary\fR
+.RS 4
+A stream of bytes, not necessarily text\&. It is recommended that you avoid using binary values because UNIX system administrators tend to consider them as unmanageable blackboxes\&. Anyway, the value will be encoded into pure text format based on hexadecimal digits, for openness and ease of administration\&. This data type should also be avoided because it is less efficient\&.
+.RE
+.PP
+There are very good reasons why types like
+\fBInteger\fR,
+\fBTime\fR,
+\fBFont\fR,
+\fBList\fR, etc were not implemented: Elektra was designed to be usefull for any type of program, so having more specific data types implicates in the definition of value limits, separators in the storage format, etc, that may be good for some application and bad for other\&. So the semantics of the data is handled by the application\&. A program or framework may define its own special data handling methods using these essential basic types\&. See the
+\fBkeyGetType()\fR
+and
+\fBkeySetType()\fR
+methods documentation in the
+\fBkdb\fR(3)
+man page to understand how to set keys with your own data types\&.
+.PP
+There are more two types of keys:
+.PP
+\fBDirectory\fR
+.RS 4
+It can\'t store a value, but, as a directory in a filesystem, it serves as a way to group correlated keys\&.
+.RE
+.PP
+\fBLink\fR
+.RS 4
+It is a link to another key\&. They work as symbolic links in the filesystem: when trying to access them, you will actually access the key they point to\&. The API also provides ways to access these special keys without dereferencing them\&.
+.RE
+Key Meta Data.PP
+Besides the key name and the value, each key has other attributes:
+.PP
+\fBOwner\'s User and Group\fR
+.RS 4
+This is a system\'s UID and GID equal to the ones found in regular files\' attributes\&.
+.RE
+.PP
+\fBAccess Permissions\fR
+.RS 4
+Filesystem\-like access permissions for user, group and others\&.
+.RE
+.PP
+\fBModification, Access and Stat Times\fR
+.RS 4
+Last time a key was modified, readed and stated (listed), respectively\&.
+.RE
+.PP
+\fBKey Comment\fR
+.RS 4
+Pretty much as a configuration file comment\&. Not intended to be used in GUI applications, because it isn\'t internationalizable\&.
+.RE
+Fine Grained Security Example.PP
+To show this metadata in action, this screen shows the
+\fBkdb\fR
+command listing keys and their attributes related to user
+\fInobody\fR\&.
+.sp
+.RS 4
+.nf
+bash$ \fBkdb ls \-Rlv system/users/nobody\fR
+\-rw\-r\-\-r\-\-   root  root    17 Mar 31 09:07 system/users/nobody/uid=99
+\-rw\-r\-\-r\-\-   root  root    17 Mar 31 09:07 system/users/nobody/gid=99
+\-rw\-r\-\-r\-\-   root  root    21 Mar 31 09:07 system/users/nobody/gecos=Nobody
+\-rw\-r\-\-r\-\-   root  root    16 Mar 31 09:07 system/users/nobody/home=/
+\-rw\-r\-\-r\-\-   root  root    28 Mar 31 09:07 system/users/nobody/shell=/sbin/nologin
+\-rw\-\-\-\-\-\-\-   root  root    16 Mar 31 09:07 system/users/nobody/password
+\-rw\-\-\-\-\-\-\-   root  root    16 Mar 31 09:07 system/users/nobody/passwdChangeBefore
+\-rw\-\-\-\-\-\-\-   root  root    20 Mar 31 09:07 system/users/nobody/passwdChangeAfter
+\-rw\-\-\-\-\-\-\-   root  root    16 Mar 31 09:07 system/users/nobody/passwdWarnBefore
+\-rw\-\-\-\-\-\-\-   root  root    15 Mar 31 09:07 system/users/nobody/passwdDisableAfter
+\-rw\-\-\-\-\-\-\-   root  root    15 Mar 31 09:07 system/users/nobody/passwdDisabledSince
+\-rw\-\-\-\-\-\-\-   root  root    15 Mar 31 09:07 system/users/nobody/passwdReserved
+                       
+.fi
+.RE
+.PP
+We ran the
+\fBkdb\fR
+command without super\-user credentials, asking for long (\fB\-l\fR), recursive (\fB\-R\fR) listing, and to show each key value (\fB\-v\fR)\&. But (since we are) regular user, we don\'t have permission to see the values of the
+\fIsystem/users/nobody/passwd*\fR
+fields\&.
+.PP
+The users database files were elektrified to key\-value pairs using the
+\fBusers\-convert\fR
+script included with the distribution\&.
+ExamplesSetting Keys.PP
+bash$\fBkdb set \-c "My first key" user/example/key "Some nice value"\fR
+.PP
+bash$\fBkdb set user:luciana/example/key \-\- "Some \- nice \- value with dashes"\fR
+.PP
+bash#\fBKDB_ROOT=user:http/sw/httpd kdb set \-u nobody \-g http key "Some value"\fR
+.PP
+bash$\fBkdb set \-b image\&.png \-t bin user/example/binaryKey\fR
+.PP
+bash$\fBkdb set \-b file\&.txt user/example/regularKey\fR
+.PP
+bash#\fBkdb set \-t link system/sw/XFree/current system/sw/XFree/handmade\fR
+Getting Keys.PP
+bash$\fBKDB_ROOT=user/example kdb get some/key/name\fR
+.PP
+bash$\fBeval `kdb get \-s user/env/env1/PS1`\fR
+.PP
+bash$\fBKDB_BACKEND=gconf kdb get user/sw/gnome\-terminal/global/active_encodings\fR
+Listing.PP
+bash$\fBkdb ls \-laR user:valeria\fR
+.PP
+bash$\fBkdb ls \-lR system/sw/xorg/current\fR
+.PP
+bash$\fBKDB_ROOT=system/sw kdb ls \-lR xorg\fR
+.PP
+bash$\fBKDB_BACKEND=fstab kdb ls \-Rv system/filesystems\fR
+.PP
+bash$\fBeval `kdb ls \-Rvs user/env/env2`\fR
+Miscelaneous.PP
+bash#\fBkdb ln system/sw/xorg/handmade system/sw/xorg/current\fR
+.PP
+bash#\fBkdb mv system/sw/xorg/current system/sw/xorg/old\fR
+.PP
+bash#\fBkdb rm system/inittab/rc4\fR
+.PP
+bash$\fBKDB_BACKEND=gconf kdb rm user/gconfKey\fR
+XML Import and Export.PP
+bash#\fBkdb export user/sw/app | sed \-e \'s|/app/|/app2/|g\' | kdb import\fR
+.PP
+bash#\fBKDB_ROOT=system/sw kdb export myapp > myappconf\&.xml\fR
+.PP
+bash#\fBkdb import myappconf\&.xml\fR
+.PP
+bash$\fBKDB_BACKEND=gconf kdb export user/sw\fR
+.SH "SEE ALSO"
+.PP
+\fBkdb\fR(1),
+\fBelektra\fR(5)
+.SH "AUTHOR"
+.PP
+\fBAvi Alkalay\fR <\&avi at unix\&.sh\&>
+.br
+Linux Market Developer, Senior IT and Software Architect, IBM Linux Impact Team :: \fIibm\&.com/linux\fR
+.sp -1n
+.IP "" 4
+Author.
+.SH "COPYRIGHT"
+Copyright \(co 2004 Avi Alkalay
+.br
+.SH "NOTES"
+.IP " 1." 4
+Elektra presentation
+.RS 4
+\%elektra.sxi
+.RE
diff --git a/doc/elektra.7.xml b/doc/elektra.7.xml
new file mode 100644 (file)
index 0000000..ba9e31b
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+       <!ENTITY author SYSTEM "author.xml">
+       <!ENTITY overview SYSTEM "overview.xml">
+       <!ENTITY bestpract SYSTEM "bestpract.xml">
+       <!ENTITY example SYSTEM "rgexample.xml">
+]>
+
+<!--
+$Id$
+$LastChangedBy$
+-->
+<refentry id="elektra">
+
+       <refentryinfo>
+               &author;
+       </refentryinfo>
+
+       <refmeta>
+               <refentrytitle>elektra</refentrytitle>
+               <manvolnum>7</manvolnum>
+       </refmeta>
+
+       <refnamediv>
+               <refname>elektra</refname>
+               <refpurpose>A framework to store configuration atoms hierarchically</refpurpose>
+       </refnamediv>
+
+       &overview;
+       &example;
+       
+       <refsection><title>See Also</title>
+               <para><citerefentry><refentrytitle>kdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>elektra</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+       </refsection>
+       
+       
+</refentry>
diff --git a/doc/garbage.xml b/doc/garbage.xml
new file mode 100644 (file)
index 0000000..85d2f38
--- /dev/null
@@ -0,0 +1,10 @@
+
+<!--
+$Id$
+-->
+
+       <section id="garbage"><title>Unused/Old Keys Left Behind Into the Key Database</title>
+               <para>Old/uninstalled software may leave unused keys into the Elektra database. Actually, this can also happen with regular text configuration files. Garbage collection of keys and files is done by the package manager or the sysadmin. The Elektra API and the <command>kdb</command> command line tool provide ways to find keys that were not accessed for a long time.</para>
+               <para>When a user is removed from the system, his keys disapear when his <envar>$HOME</envar> dir is removed.</para>
+       </section>
+               
diff --git a/doc/html-params.xsl b/doc/html-params.xsl
new file mode 100644 (file)
index 0000000..7881856
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version='1.0'?>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+               version='1.0'
+               xmlns="http://www.w3.org/TR/xhtml1/transitional"
+               exclude-result-prefixes="#default">
+
+       <xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/xhtml/chunk.xsl"/>
+       <!--xsl:import href="_DBSTYLESHEET_"/-->
+
+       <xsl:output method="html" indent="yes" encoding="UTF-8"/>
+
+       <xsl:param name="chunk.section.depth" select="'1'"/>
+       <xsl:param name="chunker.output.method" select="'html'"/>
+
+       <xsl:param name="html.stylesheet" select="'docbook.css'"/>
+       <xsl:param name="html.stylesheet.type">text/css</xsl:param>
+
+       <xsl:param name="admon.graphics.extension" select="'.gif'"/>
+       <xsl:param name="admon.graphics" select="1"/>
+       <xsl:param name="callout.graphics.extension" select="'.gif'"/>
+       <xsl:param name="callouts.extension" select="'0'"/>
+
+       <xsl:param name="section.autolabel" select="'0'"/>
+       <xsl:param name="appendix.autolabel" select="'0'"/>
+
+       <xsl:param name="suppress.navigation">1</xsl:param>
+
+       <xsl:param name="shade.verbatim" select="'0'"/>
+
+       <xsl:param name="toc.max.depth">8</xsl:param>
+       <xsl:param name="toc.section.depth">2</xsl:param>
+
+       <xsl:param name="admon.style">
+               <xsl:text>margin-left: 0.5in; margin-right: 1in;</xsl:text>
+       </xsl:param>
+
+       <xsl:param name="use.id.as.filename" select="'1'"/>
+
+       <xsl:param name="formal.title.placement">
+figure after
+example before
+equation after
+table before
+procedure before
+       </xsl:param>
+
+       <xsl:param name="generate.toc">
+appendix  toc
+/article   toc
+/book      toc,figure,table,example,equation
+chapter   toc
+part      toc
+preface   toc
+qandadiv  toc
+qandaset  toc
+reference toc
+section   toc
+set       toc
+       </xsl:param>
+
+</xsl:stylesheet>
diff --git a/doc/html-titlepage-layout.tpl b/doc/html-titlepage-layout.tpl
new file mode 100644 (file)
index 0000000..d4fd85c
--- /dev/null
@@ -0,0 +1,32 @@
+<t:templates xmlns:t="http://nwalsh.com/docbook/xsl/template/1.0"
+       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+       base-stylesheet="html-params.xsl">
+
+       <t:titlepage t:element="article" t:wrapper="div" class="titlepage">
+               <t:titlepage-content t:side="recto">
+                       <title t:predicate="[1]"/>
+                       <pubdate/>
+
+                       <authorgroup>
+                               <t:or>
+                                       <author>
+                                               <t:or>
+                                                       <firstname/>
+                                                       <affiliation>
+                                                               <t:or>
+                                                                       <address/>
+                                                                       <orgdiv/>
+                                                                       <orgname/>
+                                                               </t:or>
+                                                       </affiliation>
+                                               </t:or>
+                                       </author>
+                               </t:or>
+                       </authorgroup>
+
+               </t:titlepage-content>
+               
+               <t:titlepage-content t:side="verso"/>
+       </t:titlepage>
+</t:templates>
diff --git a/doc/html-titlepage-layout.xsl b/doc/html-titlepage-layout.xsl
new file mode 100644 (file)
index 0000000..2e70554
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" exclude-result-prefixes="#default"><xsl:import href="/usr/share/sgml/docbook/xsl-stylesheets/xhtml/chunk.xsl"/><xsl:output method="html" indent="yes" encoding="UTF-8"/><xsl:param name="chunk.section.depth" select="'1'"/><xsl:param name="chunker.output.method" select="'html'"/><xsl:param name="html.stylesheet" select="'docbook.css'"/><xsl:param name="html.stylesheet.type">text/css</xsl:param><xsl:param name="admon.graphics.extension" select="'.gif'"/><xsl:param name="admon.graphics" select="1"/><xsl:param name="callout.graphics.extension" select="'.gif'"/><xsl:param name="callouts.extension" select="'0'"/><xsl:param name="section.autolabel" select="'0'"/><xsl:param name="appendix.autolabel" select="'0'"/><xsl:param name="suppress.navigation">1</xsl:param><xsl:param name="shade.verbatim" select="'0'"/><xsl:param name="toc.max.depth">8</xsl:param><xsl:param name="toc.section.depth">2</xsl:param><xsl:param name="admon.style"><xsl:text>margin-left: 0.5in; margin-right: 1in;</xsl:text></xsl:param><xsl:param name="use.id.as.filename" select="'1'"/><xsl:param name="formal.title.placement">
+figure after
+example before
+equation after
+table before
+procedure before
+       </xsl:param><xsl:param name="generate.toc">
+appendix  toc
+/article   toc
+/book      toc,figure,table,example,equation
+chapter   toc
+part      toc
+preface   toc
+qandadiv  toc
+qandaset  toc
+reference toc
+section   toc
+set       toc
+       </xsl:param></xsl:stylesheet>
+
diff --git a/doc/images/Makefile.am b/doc/images/Makefile.am
new file mode 100644 (file)
index 0000000..3c385c0
--- /dev/null
@@ -0,0 +1,4 @@
+
+# $Id$
+
+EXTRA_DIST= classes.png img_arrow.png important.gif link_button_1.gif note.gif type-binary.png type-extendedbinary.png type-extendedstring.png type-folder.png type-link.png type-string.png
diff --git a/doc/images/classes.png b/doc/images/classes.png
new file mode 100644 (file)
index 0000000..5482ee1
Binary files /dev/null and b/doc/images/classes.png differ
diff --git a/doc/images/img_arrow.png b/doc/images/img_arrow.png
new file mode 100644 (file)
index 0000000..cfe245b
Binary files /dev/null and b/doc/images/img_arrow.png differ
diff --git a/doc/images/important.gif b/doc/images/important.gif
new file mode 100644 (file)
index 0000000..1e62ae3
Binary files /dev/null and b/doc/images/important.gif differ
diff --git a/doc/images/link_button_1.gif b/doc/images/link_button_1.gif
new file mode 100644 (file)
index 0000000..2b612c0
Binary files /dev/null and b/doc/images/link_button_1.gif differ
diff --git a/doc/images/note.gif b/doc/images/note.gif
new file mode 100644 (file)
index 0000000..f329d35
Binary files /dev/null and b/doc/images/note.gif differ
diff --git a/doc/images/type-binary.png b/doc/images/type-binary.png
new file mode 100644 (file)
index 0000000..8d310ab
Binary files /dev/null and b/doc/images/type-binary.png differ
diff --git a/doc/images/type-extendedbinary.png b/doc/images/type-extendedbinary.png
new file mode 100644 (file)
index 0000000..d24e4d4
Binary files /dev/null and b/doc/images/type-extendedbinary.png differ
diff --git a/doc/images/type-extendedstring.png b/doc/images/type-extendedstring.png
new file mode 100644 (file)
index 0000000..de6de60
Binary files /dev/null and b/doc/images/type-extendedstring.png differ
diff --git a/doc/images/type-folder.png b/doc/images/type-folder.png
new file mode 100644 (file)
index 0000000..f7e8c35
Binary files /dev/null and b/doc/images/type-folder.png differ
diff --git a/doc/images/type-link.png b/doc/images/type-link.png
new file mode 100644 (file)
index 0000000..75c6508
Binary files /dev/null and b/doc/images/type-link.png differ
diff --git a/doc/images/type-string.png b/doc/images/type-string.png
new file mode 100644 (file)
index 0000000..fcb6e3d
Binary files /dev/null and b/doc/images/type-string.png differ
diff --git a/doc/kdb.1 b/doc/kdb.1
new file mode 100644 (file)
index 0000000..7f19eb8
--- /dev/null
+++ b/doc/kdb.1
@@ -0,0 +1,372 @@
+.\"     Title: kdb
+.\"    Author: Avi Alkalay <avi at unix.sh>
+.\" Generator: DocBook XSL Stylesheets v1.73.2 <http://docbook.sf.net/>
+.\"      Date: March 2004
+.\"    Manual: 
+.\"    Source: Elektra Initiative
+.\"
+.TH "KDB" "1" "March 2004" "Elektra Initiative" ""
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+kdb \- Elektra key database command line administration tool
+.SH "SYNOPSIS"
+.HP 8
+\fBkdb get\fR [\fB\-dlr\fR] key/name
+.HP 8
+\fBkdb set\fR [\fB\-t\fR\ \fBtype\fR] [\fB\-d\fR] [\fB\-c\fR\ \fB"A\ comment\ about\ this\ key"\fR] [\fB\-m\fR\ \fBmode\fR] [\fB\-u\fR\ \fBuid\fR] [\fB\-g\fR\ \fBgid\fR] key/name "the\ value"
+.HP 8
+\fBkdb set\fR [\fB\-t\fR\ \fBtype\fR] [\fB\-m\fR\ \fBmode\fR] [\fB\-c\fR\ \fB"A\ comment"\fR] key/name \-\- "the\ value"
+.HP 8
+\fBkdb set\fR [\fB\-t\fR\ \fBtype\fR] [\fB\-b\fR\ \fBfile\fR] key/name
+.HP 7
+\fBkdb ls\fR [\fB\-lRfvs\fR] [key/dir\ |\ key/name]
+.HP 7
+\fBkdb ls\fR [\fB\-lRfvx\fR] [key/dir\ |\ key/name] > keys\&.xml
+.HP 9
+\fBkdb edit\fR [\fB\-R\fR] [key/dir\ |\ key/name]
+.HP 7
+\fBkdb rm\fR key/name
+.HP 7
+\fBkdb mv\fR key/src key/dest
+.HP 7
+\fBkdb ln\fR key/src key/dest
+.HP 11
+\fBkdb export\fR [\fB\-f\fR] system/some/tree\&.root > [file\&.xml]
+.HP 11
+\fBkdb import\fR < file\&.xml
+.HP 11
+\fBkdb import\fR file\&.xml
+.HP 12
+\fBkdb monitor\fR some/key/name
+Description.PP
+The
+\fBkdb\fR
+command provide ways to manipulate the Elektra keys database\&.
+.PP
+The subcommands implemented are very similar to regular UNIX commands like
+\fBls\fR, and
+\fBrm\fR, specially in their output and options\&.
+Subcommands.PP
+\fBget\fR
+.RS 4
+Get the value from the specified key\&. Accepts options:
+\fB\-d\fR,
+\fB\-l\fR,
+\fB\-f\fR,
+\fB\-s\fR
+.RE
+.PP
+\fBset\fR
+.RS 4
+Set the value to the specified key\&. Accepts options:
+\fB\-c\fR,
+\fB\-t\fR,
+\fB\-d\fR,
+\fB\-m\fR,
+\fB\-b\fR
+.RE
+.PP
+\fBls\fR
+.RS 4
+As the
+\fBls\fR(1)
+command, list key names for the specified key, or children keys, if specified a folder key\&. The
+\fB\-v\fR
+argument will make it show also the values of each key\&. The
+\fB\-d\fR
+(descriptive) will make it show the comment, key name and its value, as you are watching a plain text file\&. Accepts options:
+\fB\-x\fR,
+\fB\-d\fR,
+\fB\-l\fR,
+\fB\-f\fR,
+\fB\-v\fR,
+\fB\-R\fR,
+\fB\-s\fR
+.RE
+.PP
+\fBln\fR
+.RS 4
+Creates a key that is a symbolic links to another key\&.
+.RE
+.PP
+\fBmv\fR
+.RS 4
+Move, or renames a key\&. Currently it can\'t move keys across different filesystems\&.
+.RE
+.PP
+\fBrm\fR
+.RS 4
+As the
+\fBrm\fR(1)
+command, removes the key specified\&.
+.RE
+.PP
+\fBedit\fR
+.RS 4
+A very powerfull subcommand that lets you edit an XML representation of the keys\&. The parameters it accepts is usually a parent key, so its child keys will be gathered\&. Can be used with the
+\fB\-R\fR
+flag to work recursively\&. The editor used is the one set in the
+\fB$EDITOR\fR
+environment variable, or
+\fBvi\fR\&. After editing the keys,
+\fBkdb edit\fR
+will analyze them and commit only the changed keys, remove the keys removed, and add the keys added\&. This command is only available when
+\fI/usr/lib/libelektratools\&.so\fR
+is available\&.
+.RE
+.PP
+\fBexport\fR, \fBsave\fR
+.RS 4
+Export a subtree of keys to XML\&. If no subtree is defined right after the
+\fBexport\fR
+command,
+\fIsystem\fR
+and current
+\fIuser\fR
+trees will be exported\&. Output is written to standard output\&. The output encoding will allways be UTF\-8, regardeless of your system encoding\&. UTF\-8 is the most universal charset you can get when exchanging data between multiple systems\&. Accepts
+\fB\-f\fR\&.
+.RE
+.PP
+\fBimport\fR, \fBload\fR
+.RS 4
+Import an XML representation of keys and save it to the keys database\&. If no filename is passed right after the
+\fBimport\fR
+command, standard input is used\&. This command is only available when
+\fI/usr/lib/libelektratools\&.so\fR
+is available\&.
+.RE
+.PP
+\fBmonitor\fR, \fBmon\fR
+.RS 4
+Monitor a key for some value change\&. It will block your command line until a change in the key value is detected, then return its new value\&.
+.RE
+Options.PP
+\fB\-R\fR
+.RS 4
+Causes to work recursively\&. In
+\fBls\fR, will list recursively\&.
+.RE
+.PP
+\fB\-x\fR
+.RS 4
+Makes
+\fBls\fR
+output an XML representation of the keys, instead of an
+\fBls\fR\-compatible output\&.
+.RE
+.PP
+\fB\-l\fR
+.RS 4
+Causes to display long results\&. With
+\fBls\fR, will generate lists similar to
+\fBls \-l\fR\&. With
+\fBget\fR, will show also the key name\&.
+.RE
+.PP
+\fB\-a\fR
+.RS 4
+Causes
+\fBls\fR
+to display also inactive keys\&. Generate lists similar to
+\fBls \-a\fR\&. Inactive keys are keys which basename begins with a \'\&.\' (dot)\&. An example of inactive key:
+\fIsystem/sw/XFree/current/Monitor/\&.Monitor1\fR
+.RE
+.PP
+\fB\-f\fR
+.RS 4
+Causes to work with full key names\&. A full key name makes sense only on
+\fIuser/*\fR
+keys, and differentiate from the regular key names in specifying the owner user\&. If the current user is
+\fIsomeuser\fR, the
+\fIuser/some/key\fR
+full name is
+\fIuser:someuser/some/key\fR\&. Makes effect in
+\fBls\fR,
+\fBexport\fR
+and
+\fBget\fR
+subcommands\&.
+.RE
+.PP
+\fB\-d\fR
+.RS 4
+Causes
+\fBget\fR
+to work descriptivelly\&. When requesting a key it will show the comment, key name and its value in a fancy format\&.
+Causes
+\fBset\fR
+to mark the key as a directory key\&.
+.RE
+.PP
+\fB\-s\fR
+.RS 4
+Causes
+\fBget\fR
+and
+\fBls\fR
+to be more friendly to Shell scripts\&. For example, when requesting
+\fIuser/env/env2/PATH\fR, the output will be PATH="the value", that is, only the basename of the key will be showed and the value will be surrounded by \' " \'\&.
+.RE
+.PP
+\fB\-t type\fR
+.RS 4
+When
+\fBset\fRting a key\'s value, you can specify the type with this switch\&. Currently accepted types are
+\fBstring\fR
+for plain text,
+\fBbin\fR
+for binary as\-is values,
+\fBdir\fR
+to create folder keys and
+\fBlink\fR
+to create symbolic links between keys\&. Plain text are always stored as
+\fBUTF-8\fR(7)
+in Elektra, regardeless of your current encoding (\fB$LANG\fR)\&. If you want to force a value to be stored without the
+\fBUTF-8\fR(7)
+encoding (a bad idea), you can set it as binary\&. Binary values should be avoided, because they are black boxes for system administrators\&.
+.RE
+.PP
+\fB\-b filename\fR
+.RS 4
+Set the key value as the content of file
+\fIfilename\fR\&. This option is more usefull when setting binary keys\&.
+.RE
+.PP
+\fB\-m mode\fR
+.RS 4
+For the
+\fBset\fR
+command\&. Will set the key access permission to
+\fBmode\fR, which must be an octal number as for
+\fBchmod\fR(1)\&.
+.RE
+.PP
+\fB\-u uid\fR
+.RS 4
+Create the key with
+\fBuid\fR
+user ID\&. It can be a user name or a uid number\&.
+.RE
+.PP
+\fB\-g gid\fR
+.RS 4
+Create the key with
+\fBgid\fR
+group ID\&. It can be a group name or a gid number
+.RE
+.PP
+\fB\-c comment\fR
+.RS 4
+When
+\fBset\fRting keys, you can use this argument to set a descriptive comment for it\&. This comment is exactly as a comment in a plain text configuration file\&. The comment is stored as
+\fBUTF-8\fR(7)
+regardeless of your current encoding (\fB$LANG\fR)\&.
+.RE
+.PP
+\fB\-v\fR
+.RS 4
+With the
+\fBls\fR
+subcommand, will make it show also the value stored in the key\&.
+.RE
+.PP
+\fB\-\-\fR
+.RS 4
+With the
+\fBset\fR
+subcommand, everything after it will be considered the value, even text with dashes (\-)\&.
+.RE
+Best Practices When Creating Keys.PP
+When using Elektra to store your application\'s configuration and state, please keep in mind the following rules:
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'You are not allowed to create keys right under
+\fIsystem\fR
+or
+\fIuser\fR\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'You are not allowed to create folder keys right under
+\fIsystem\fR
+or
+\fIuser\fR\&. They are reserved for very essential OS subsystems\&.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+03'The keys for your application, called say
+\fIMyApp\fR, should be created under
+\fIsystem/sw/MyApp\fR
+and/or
+\fIuser/sw/MyApp\fR\&.
+.RE
+.SH "ENVIRONMENT"
+.PP
+\fBKDB_ROOT\fR
+if defined, prepends it to key names\&.
+.PP
+\fBKDB_BACKEND\fR
+defines the name of another backend plugin library to use
+ExamplesSetting Keys.PP
+bash$\fBkdb set \-c "My first key" user/example/key "Some nice value"\fR
+.PP
+bash$\fBkdb set user:luciana/example/key \-\- "Some \- nice \- value with dashes"\fR
+.PP
+bash#\fBKDB_ROOT=user:http/sw/httpd kdb set \-u nobody \-g http key "Some value"\fR
+.PP
+bash$\fBkdb set \-b image\&.png \-t bin user/example/binaryKey\fR
+.PP
+bash$\fBkdb set \-b file\&.txt user/example/regularKey\fR
+.PP
+bash#\fBkdb set \-t link system/sw/XFree/current system/sw/XFree/handmade\fR
+Getting Keys.PP
+bash$\fBKDB_ROOT=user/example kdb get some/key/name\fR
+.PP
+bash$\fBeval `kdb get \-s user/env/env1/PS1`\fR
+.PP
+bash$\fBKDB_BACKEND=gconf kdb get user/sw/gnome\-terminal/global/active_encodings\fR
+Listing.PP
+bash$\fBkdb ls \-laR user:valeria\fR
+.PP
+bash$\fBkdb ls \-lR system/sw/xorg/current\fR
+.PP
+bash$\fBKDB_ROOT=system/sw kdb ls \-lR xorg\fR
+.PP
+bash$\fBKDB_BACKEND=fstab kdb ls \-Rv system/filesystems\fR
+.PP
+bash$\fBeval `kdb ls \-Rvs user/env/env2`\fR
+Miscelaneous.PP
+bash#\fBkdb ln system/sw/xorg/handmade system/sw/xorg/current\fR
+.PP
+bash#\fBkdb mv system/sw/xorg/current system/sw/xorg/old\fR
+.PP
+bash#\fBkdb rm system/inittab/rc4\fR
+.PP
+bash$\fBKDB_BACKEND=gconf kdb rm user/gconfKey\fR
+XML Import and Export.PP
+bash#\fBkdb export user/sw/app | sed \-e \'s|/app/|/app2/|g\' | kdb import\fR
+.PP
+bash#\fBKDB_ROOT=system/sw kdb export myapp > myappconf\&.xml\fR
+.PP
+bash#\fBkdb import myappconf\&.xml\fR
+.PP
+bash$\fBKDB_BACKEND=gconf kdb export user/sw\fR
+.SH "SEE ALSO"
+.PP
+
+\fBelektra\fR(7),
+\fBelektra\fR(5)
+.SH "AUTHOR"
+.PP
+\fBAvi Alkalay\fR <\&avi at unix\&.sh\&>
+.br
+Linux Market Developer, Senior IT and Software Architect, IBM Linux Impact Team :: \fIibm\&.com/linux\fR
+.sp -1n
+.IP "" 4
+Author.
+.SH "COPYRIGHT"
+Copyright \(co 2004 Avi Alkalay
+.br
diff --git a/doc/kdb.1.xml b/doc/kdb.1.xml
new file mode 100644 (file)
index 0000000..99cea0d
--- /dev/null
@@ -0,0 +1,170 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+       <!ENTITY author SYSTEM "author.xml">
+       <!ENTITY rgcmd SYSTEM "rgcmd.xml">
+       <!ENTITY bestpract SYSTEM "bestpract.xml">
+       <!ENTITY example SYSTEM "rgexample.xml">
+]>
+
+
+<!--
+$Id$
+-->
+<refentry id="kdb">
+
+       <refentryinfo>
+               &author;
+       </refentryinfo>
+
+       <refmeta>
+               <refentrytitle>kdb</refentrytitle>
+               <manvolnum>1</manvolnum>
+       </refmeta>
+
+       <refnamediv>
+               <refname>kdb</refname>
+               <refpurpose>Elektra key database command line administration tool</refpurpose>
+       </refnamediv>
+
+       <refsynopsisdiv>
+               <cmdsynopsis><command>kdb get</command>
+                       <arg choice="opt">
+                               <option>-dlr</option>
+                       </arg>
+                       <arg choice="plain">key/name</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb set</command>
+                       <arg choice="opt">
+                               <option>-t</option>
+                               <option>type</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-d</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-c</option>
+                               <option>"A comment about this key"</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-m</option>
+                               <option>mode</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-u</option>
+                               <option>uid</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-g</option>
+                               <option>gid</option>
+                       </arg>
+                       <arg choice="plain">key/name</arg>
+                       <arg choice="plain">"the value"</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb set</command>
+                       <arg choice="opt">
+                               <option>-t</option>
+                               <option>type</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-m</option>
+                               <option>mode</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-c</option>
+                               <option>"A comment"</option>
+                       </arg>
+                       <arg choice="plain">key/name</arg>
+                       <arg choice="plain">--</arg>
+                       <arg choice="plain">"the value"</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb set</command>
+                       <arg choice="opt">
+                               <option>-t</option>
+                               <option>type</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-b</option>
+                               <option>file</option>
+                       </arg>
+                       <arg choice="plain">key/name</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb ls</command>
+                       <arg choice="opt">
+                               <option>-lRfvs</option>
+                       </arg>
+                       <arg choice="opt">key/dir | key/name</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb ls</command>
+                       <arg choice="opt">
+                               <option>-lRfvx</option>
+                       </arg>
+                       <arg choice="opt">key/dir | key/name</arg>
+                       <arg choice="plain">&gt;</arg>
+                       <arg choice="plain">keys.xml</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb edit</command>
+                       <arg choice="opt">
+                               <option>-R</option>
+                       </arg>
+                       <arg choice="opt">key/dir | key/name</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb rm</command>
+                       <arg choice="plain">key/name</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb mv</command>
+                       <arg choice="plain">key/src</arg>
+                       <arg choice="plain">key/dest</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb ln</command>
+                       <arg choice="plain">key/src</arg>
+                       <arg choice="plain">key/dest</arg>
+               </cmdsynopsis>
+
+               <cmdsynopsis><command>kdb export</command>
+                       <arg choice="opt">
+                               <option>-f</option>
+                       </arg>
+                       <arg choice="plain">system/some/tree.root</arg>
+                       <arg choice="plain">&#062;</arg>
+                       <arg choic="plain">file.xml</arg>
+               </cmdsynopsis>
+
+               <cmdsynopsis><command>kdb import</command>
+                       <arg choice="plain">&#060;</arg>
+                       <arg choice="plain">file.xml</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb import</command>
+                       <arg choice="plain">file.xml</arg>
+               </cmdsynopsis>
+               
+               <cmdsynopsis><command>kdb monitor</command>
+                       <arg choice="plain">some/key/name</arg>
+               </cmdsynopsis>
+       </refsynopsisdiv>
+
+       &rgcmd;
+       &bestpract;
+       
+       <refsection><title>Environment</title>
+               <para><envar>KDB_ROOT</envar> if defined, prepends it to key names.</para>
+               <para><envar>KDB_BACKEND</envar> defines the name of another backend plugin library to use</para>
+       </refsection>
+       
+       &example;
+       
+       <refsection><title>See Also</title>
+               <para>
+                       <citerefentry><refentrytitle>elektra</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+                       <citerefentry><refentrytitle>elektra</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+               </para>
+       </refsection>
+</refentry>
diff --git a/doc/overview.xml b/doc/overview.xml
new file mode 100644 (file)
index 0000000..956620f
--- /dev/null
@@ -0,0 +1,219 @@
+<!--
+$Id$
+-->
+
+               <section id="libarchitecture"><title>Library Linkage Architecture</title>
+                       <para>The Elektra library (<filename>libelektra.so</filename>) has 2 layers: public methods and backend access, according to the following architecture (these pictures were taken from the <ulink url="elektra.sxi">Elektra presentation</ulink>):</para>
+                       <para><graphic srccredit="genericarch" fileref="images/genericarch.png" label="generic architecture" align="center"/></para>
+                       <para>When using local backends such as the filesys backend, all key access happens in the actual process space as bellow:</para>
+                       <para><graphic srccredit="localbacks" fileref="images/localbacks.png" label="local backends" align="center"/></para>
+                       <para>A remote daemon backend is also possible as noted bellow:</para>
+                       <para><graphic srccredit="daemonback" fileref="images/daemonback.png" label="daemon backend" align="center"/></para>
+               </section>
+               <section id="rgfacts"><title>True Facts About Elektra</title>
+                       <itemizedlist>
+                               <listitem><para>It is much more an agreement then a piece of software. Relation is 99% to 1%.</para></listitem>
+                               <listitem><para>It is a simple and consistent API to help software developers programatically store and retrieve global and user-specific configuration parameters.</para></listitem>
+                               <listitem><para>All key-value pairs are stored in clear-text files, UTF-8 encoded. All old charsets are also supported, with automatic transparent conversion to and from UTF-8.</para></listitem>
+                               <listitem><para>API supports change notifications and multiple backends.</para></listitem>
+                               <listitem><para>It provides a unique namespace for all values. Anywhere, anytime, any program can preciselly access keys by their names. Security restrictions may obviously apply.</para></listitem>
+                               <listitem><para>It is designed to be secure and lightweight, to let even early boot-stage programs like <command>/sbin/init</command> to use it, instead of <filename>/etc/inittab</filename> file.</para></listitem>
+                               <listitem><para>It is designed to be easy to administrate with regular command line tools like <command>cat</command>, <command>vi</command>, <command>cp</command>, <command>ls</command>, <command>ln</command>. Its storage is 100% open.</para></listitem>
+                               <listitem><para>It tries to set distribution-independent naming standards to store things like hardware configuration, networking, user's session configuration, system's mime-types, parameters for kernel modules, etc, that are generally stored under <filename>/etc</filename >.</para></listitem>
+                               <listitem><para>It requires existing software to be changed to use its API. This will substitute hundreds of configuration-text-file parsing code, into clear Elektra's API key-value access methods.</para></listitem>
+                               <listitem><para>It is POSIX compliant. If it doesn't compile and run easily on some POSIX system, it should be easily modified to do so.</para></listitem>
+                       </itemizedlist>
+               </section>
+
+               <section id="rgnot"><title>Elektra Is Not</title>
+                       <itemizedlist>
+                               <listitem><para>Is NOT something that accesses SQL/relational databases.</para ></listitem>
+                               <listitem><para>Is NOT an OS service that can become unavailable and make system unusable. It is just a library to access files according to a namespace.</para ></listitem >
+                               <listitem><para>Is NOT an alternative to network information systems like LDAP or NIS. These are still required for networked environments.</para ></listitem >
+                               <listitem><para>Is NOT a Webmin-like or other GUI tool to be used by end users.</para ></listitem >
+                               <listitem><para>Is NOT an additional software layer to edit/generate existing configuration files.</para></listitem>
+                               <listitem><para>Is NOT a "configuration system", because one can't be created by simply writing some code. A configuration system is an ecosystem, and the Elektra Project tries to help build one.</para></listitem>
+                               <listitem><para>It doesn't know a thing about the semantics of each data it stores.</para></listitem>
+                       </itemizedlist>
+               </section>
+
+       
+       <section id="namespace"><title>Namespaces and Key Names</title>
+               <para>All keys are organized in a hierarchical tree with 2 Namespaces (subtrees) as showed by the picture:</para>
+               <para><graphic srccredit="roots" fileref="images/roots.png" label="roots" align="center"/></para>
+               <variablelist>
+                       <varlistentry><term><type>system</type></term>
+                               <listitem><para>Contains all subsystems and global application keys/configuration. Equivalent to files under <filename>/etc</filename> directory.</para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><type>user</type></term>
+                               <listitem><para>The current user's keys. Equivalent to the dotfiles in a user's <envar>$HOME</envar> directory. These keys are phisically stored under the owner user home directory. The many <filename>user:</filename><replaceable>username</replaceable> in the picture shows the full name of those trees. Read about user domains bellow for more.</para></listitem>
+                       </varlistentry>
+               </variablelist>
+               <section id="userdomains"><title>User Domains</title>
+                       <para>Different from the <filename>system</filename> namespace, the <filename>user</filename> namespace is dynamic. For example, the key <filename>user/env/PATH</filename> may have completely different values for users <replaceable>luciana</replaceable> and <replaceable>valeria</replaceable>. In this example, if <replaceable>valeria</replaceable> wants to access this key at <replaceable>luciana</replaceable>'s space, it should refer to <filename>user:luciana/env/PATH</filename>. Access permissions apply.</para>
+                       <para>User domains were implemented also to address situations when different user names (<envar>$USER</envar>) have same UID. So a user key is stored in his home directory based on the user name, not the UID.</para>
+               </section>
+               <section id="inactivekey"><title>Inactive Keys</title>
+                       <para>A great thing about text configuration files is that some configuration items can be there as an example, but inactive or commented. Elektra provides a very simple way to simulate this behavior: if the key name begins with a dot (.), it is considered inactive or commented. In real world applications, the Elektra API will ignore these keys by default, but the keys are still accessible if the developer wants to.</para>
+                       <para>These are some keys that have inactive subtrees:</para>
+                       <itemizedlist>
+                               <listitem><simpara><filename>system/sw/XFree/InputDevice/.Mouse3/Driver</filename>: All keys under <filename>.Mouse3/*</filename> subtree won't be read by default.</simpara></listitem>
+                               <listitem><simpara><filename>user:valeria/env/env2/.PATH</filename>: The <envar>$PATH</envar> environment variable <emphasis>won't</emphasis> be set when <replaceable>valeria</replaceable> login.</simpara></listitem>
+                               <listitem><simpara><filename>system/users/.louis/uid</filename>: The entire <filename>.louis/*</filename> subtree is inactive. This is the same as commenting the user entry from a configuration file.</simpara></listitem>
+                       </itemizedlist>
+                       <para>See bellow more examples of inactive keys.</para>
+               </section>
+               <section id="keyexamples"><title>Key Examples</title>
+                       <para>Here are some valid key names, and their values:</para>
+                       <para>The Elektra keys of the combined <filename>/etc/passwd</filename> and <filename>/etc/shadow</filename> entry for user 'nobody' would look like:</para>
+                       <itemizedlist>
+                               <listitem><simpara><filename>system/users/nobody/uid</filename>: 99</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/gid</filename>: 99</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/gecos</filename>: Nobody</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/home</filename>: /</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/shell</filename>: /sbin/nologin</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/password</filename>: *</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/passwdChangeBefore</filename>: 0</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/passwdChangeAfter</filename>: 99999</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/passwdWarnBefore</filename>: 7</simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/passwdDisableAfter</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/passwdDisabledSince</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/users/nobody/passwdReserved</filename>: </simpara></listitem>
+                       </itemizedlist>
+                       <para>The environment variables I want set, when I log in, with their full key name:</para>
+                       <itemizedlist>
+                               <listitem><simpara><filename>user:aviram/env/env1/JAVA_HOME</filename>: /usr/lib/jvm/java-1.4.1-ibm-1.4.1.0/jre</simpara></listitem>
+                               <listitem><simpara><filename>user:aviram/env/env2/PATH</filename>: $PATH:~/bin:$JAVA_HOME/bin</simpara></listitem>
+                               <listitem><simpara><filename>user:aviram/env/env2/PS1</filename>: \h:\w\$ </simpara></listitem>
+                               <listitem><simpara><filename>user:aviram/env/env3/PILOTRATE</filename>: 57600</simpara></listitem>
+                       </itemizedlist>
+                       <para>The entry in <filename>/etc/inittab</filename> that is responsible for starting X11 would look:</para>
+                       <itemizedlist>
+                               <listitem><simpara><filename>system/init/x/runlevels</filename>: 5</simpara></listitem>
+                               <listitem><simpara><filename>system/init/x/action</filename>: respawn</simpara></listitem>
+                               <listitem><simpara><filename>system/init/x/process</filename>: /etc/X11/prefdm -nodaemon</simpara></listitem>
+                       </itemizedlist>
+                       <para>The users database files and <filename>/etc/inittab</filename> were Elektrified to key-value pairs using the <command>users-convert</command> and <command>inittab-convert</command> scripts included with the distribution.</para>
+                       <para>An example of an elektrified <filename>/etc/X11/xorg.conf</filename> or <filename>/etc/X11/XF86Config</filename>:</para>
+                       <itemizedlist>
+                               <listitem><simpara><filename>system/sw/xorg/current/Layouts/Default Layout/Inputs/Keyboard0/CoreKeyboard</filename>:</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Layouts/Default Layout/Inputs/Mouse0/CorePointer</filename>:</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Layouts/Default Layout/Screens/Screen0/Absolute.x</filename>: 0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Layouts/Default Layout/Screens/Screen0/Absolute.y</filename>: 0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Layouts/Default Layout/Screens/Screen0/ScreenNumber</filename>: 0</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/Files/FontPath</filename>: unix/:7100</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Files/RgbPath</filename>: /usr/X11R6/lib/X11/rgb</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/Devices/Videocard0/BoardName</filename>: Intel 740 (generic)</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Devices/Videocard0/Driver</filename>: i740</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Devices/Videocard0/VendorName</filename>: Videocard vendor</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Keyboard0/Driver</filename>: keyboard</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Keyboard0/Options/XkbLayout</filename>: us_intl</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Keyboard0/Options/XkbModel</filename>: pc105</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Mouse0/Driver</filename>: mouse</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Mouse0/Options/Device</filename>: /dev/input/mice</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Mouse0/Options/Emulate3Buttons</filename>: yes</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Mouse0/Options/Protocol</filename>: IMPS/2</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/InputDevices/Mouse0/Options/ZAxisMapping</filename>: 4 5</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/DisplaySize.height</filename>: 230</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/DisplaySize.width</filename>: 300</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/HorizSync</filename>: 30.0 - 61.0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/ModelName</filename>: SyncMaster</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/Options/dpms</filename>:</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/VendorName</filename>: Monitor Vendor</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/Monitor0/VertRefresh</filename>: 56.0 - 75.0</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/.Monitor1/HorizSync</filename>: 30.0 - 61.0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/.Monitor1/ModelName</filename>: Impression</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/.Monitor1/Options/dpms</filename>:</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/.Monitor1/VendorName</filename>: Monitor Vendor</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Monitors/.Monitor1/VertRefresh</filename>: 56.0 - 75.0</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/DefaultDepth</filename>: 16</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/Device</filename>: Videocard0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/Displays/00/Depth</filename>: 16</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/Displays/00/Modes</filename>: 1024x768,800x600,640x480</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/Displays/00/Viewport.x</filename>:  0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/Displays/00/Viewport.y</filename>:  0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Screens/Screen0/Monitor</filename>:  Monitor0</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/dbe</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/dri</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/extmod</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/fbdevhw</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/freetype</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/glx</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/record</filename>: </simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/Modules/type1</filename>:</simpara></listitem>
+                               
+                               <listitem><simpara><filename>system/sw/xorg/current/DRI/Group</filename>: 0</simpara></listitem>
+                               <listitem><simpara><filename>system/sw/xorg/current/DRI/Mode</filename>: 0666</simpara></listitem>
+                       </itemizedlist>
+                       <para>Pay attention that the keys bellow <filename>system/sw/XFree/current/Monitor/.Monitor1</filename> are inactive because we have <filename>.Monitor1</filename> as their parent. So unless special options are used when calling the API, these keys will not be retrieved from the database.</para>
+                       <para>Throughout this text you will see other examples of key names.</para>
+               </section>
+       </section>
+
+       
+       <section id="datatypes"><title>Key Data Types</title>
+               <para>There are only two types of data that can be stored:</para>
+               <variablelist>
+                       <varlistentry><term><type>Text</type></term>
+                               <listitem><para>Handled as pure text. Regardeless of the charset being used, these values are always stored as UTF-8. This ensures very strong internationalization and migration capabilities, while keeping simplicity. If you don't want the Elektra framework to convert your non-ASCII text to UTF-8 (not recomended), you should use the Binary data format.</para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><type>Binary</type></term>
+                               <listitem><para>A stream of bytes, not necessarily text. It is recommended that you avoid using binary values because UNIX system administrators tend to consider them as unmanageable blackboxes. Anyway, the value will be encoded into pure text format based on hexadecimal digits, for openness and ease of administration. This data type should also be avoided because it is less efficient.</para></listitem>
+                       </varlistentry>
+               </variablelist>
+               <para>There are very good reasons why types like <type>Integer</type>, <type>Time</type>, <type>Font</type>, <type>List</type>, etc were not implemented: Elektra was designed to be usefull for any type of program, so having more specific data types implicates in the definition of value limits, separators in the storage format, etc, that may be good for some application and bad for other. So the semantics of the data is handled by the application. A program or framework may define its own special data handling methods using these essential basic types. See the <function>keyGetType()</function> and <function>keySetType()</function> methods documentation in the  <citerefentry><refentrytitle>kdb</refentrytitle><manvolnum>3</manvolnum></citerefentry> man page to understand how to set keys with your own data types.</para>
+               <para>There are more two types of keys:</para>
+               <variablelist>
+                       <varlistentry><term><type>Directory</type></term>
+                               <listitem><para>It can't store a value, but, as a directory in a filesystem, it serves as a way to group correlated keys.</para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><type>Link</type></term>
+                               <listitem><para>It is a link to another key. They work as symbolic links in the filesystem: when trying to access them, you will actually access the key they point to. The API also provides ways to access these special keys without dereferencing them.</para></listitem>
+                       </varlistentry>
+               </variablelist>
+       </section>
+
+       <section id="metadata"><title>Key Meta Data</title>
+               <para>Besides the key name and the value, each key has other attributes:</para>
+               <variablelist>
+                       <varlistentry><term><type>Owner's User and Group</type></term>
+                               <listitem><para>This is a system's UID and GID equal to the ones found in regular files' attributes.</para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><type>Access Permissions</type></term>
+                               <listitem><para>Filesystem-like access permissions for user, group and others.</para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><type>Modification, Access and Stat Times</type></term>
+                               <listitem><para>Last time a key was modified, readed and stated (listed), respectively.</para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><type>Key Comment</type></term >
+                               <listitem><para>Pretty much as a configuration file comment. Not intended to be used in GUI applications, because it isn't internationalizable.</para></listitem>
+                       </varlistentry>
+               </variablelist>
+               <section id="securityexample"><title>Fine Grained Security Example</title>
+                       <para>To show this metadata in action, this screen shows the <command>kdb</command> command listing keys and their attributes related to user <varname>nobody</varname>.</para>
+                       <screen><prompt>bash$</prompt> <command>kdb ls -Rlv system/users/nobody</command>
+-rw-r--r--   root  root    17 Mar 31 09:07 system/users/nobody/uid=99
+-rw-r--r--   root  root    17 Mar 31 09:07 system/users/nobody/gid=99
+-rw-r--r--   root  root    21 Mar 31 09:07 system/users/nobody/gecos=Nobody
+-rw-r--r--   root  root    16 Mar 31 09:07 system/users/nobody/home=/
+-rw-r--r--   root  root    28 Mar 31 09:07 system/users/nobody/shell=/sbin/nologin
+-rw-------   root  root    16 Mar 31 09:07 system/users/nobody/password
+-rw-------   root  root    16 Mar 31 09:07 system/users/nobody/passwdChangeBefore
+-rw-------   root  root    20 Mar 31 09:07 system/users/nobody/passwdChangeAfter
+-rw-------   root  root    16 Mar 31 09:07 system/users/nobody/passwdWarnBefore
+-rw-------   root  root    15 Mar 31 09:07 system/users/nobody/passwdDisableAfter
+-rw-------   root  root    15 Mar 31 09:07 system/users/nobody/passwdDisabledSince
+-rw-------   root  root    15 Mar 31 09:07 system/users/nobody/passwdReserved
+                       </screen>
+                       <para>We ran the <command>kdb</command> command without super-user credentials, asking for long (<option>-l</option>), recursive (<option>-R</option>) listing, and to show each key value (<option>-v</option>). But (since we are) regular user, we don't have permission to see the values of the <filename>system/users/nobody/passwd*</filename> fields.</para>
+                       <para>The users database files were elektrified to key-value pairs using the <command>users-convert</command> script included with the distribution.</para>
+               </section>
+       </section>
+
diff --git a/doc/rgcmd.xml b/doc/rgcmd.xml
new file mode 100644 (file)
index 0000000..96363f1
--- /dev/null
@@ -0,0 +1,117 @@
+
+<!--
+$Id$
+-->
+       <section id="kdbdesc"><title>Description</title>
+               <para>The <command>kdb</command> command provide ways to manipulate the Elektra keys database.</para>
+               <para>The subcommands implemented are very similar to regular UNIX commands like <command>ls</command>, and <command>rm</command>, specially in their output and options.</para>
+       </section>
+
+       <section id="kdbsubc"><title>Subcommands</title>
+               <variablelist>
+                       <varlistentry><term><command>get</command></term>
+                               <listitem><para>Get the value from the specified key. Accepts options: <option>-d</option>, <option>-l</option>, <option>-f</option>, <option>-s</option></para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>set</command></term>
+                               <listitem><para>Set the value to the specified key. Accepts options: <option>-c</option>, <option>-t</option>, <option>-d</option>, <option>-m</option>, <option>-b</option></para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>ls</command></term>
+                               <listitem><para>As the <citerefentry><refentrytitle>ls</refentrytitle><manvolnum>1</manvolnum></citerefentry> command, list key names for the specified key, or children keys, if specified a folder key. The <option>-v</option> argument will make it show also the values of each key. The <option>-d</option> (descriptive) will make it show the comment, key name and its value, as you are watching a plain text file. Accepts options: <option>-x</option>, <option>-d</option>, <option>-l</option>, <option>-f</option>, <option>-v</option>, <option>-R</option>, <option>-s</option></para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>ln</command></term>
+                               <listitem><para>Creates a key that is a symbolic links to another key.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>mv</command></term>
+                               <listitem><para>Move, or renames a key. Currently it can't move keys across different filesystems.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>rm</command></term>
+                               <listitem><para>As the <citerefentry><refentrytitle>rm</refentrytitle><manvolnum>1</manvolnum></citerefentry> command, removes the key specified.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>edit</command></term>
+                               <listitem><para>A very powerfull subcommand that lets you edit an XML representation of the keys. The parameters it accepts is usually a parent key, so its child keys will be gathered. Can be used with the <option>-R</option> flag to work recursively. The editor used is the one set in the <envar>$EDITOR</envar> environment variable, or <command>vi</command>. After editing the keys, <command>kdb edit</command> will analyze them and commit only the changed keys, remove the keys removed, and add the keys added. This command is only available when <filename>/usr/lib/libelektratools.so</filename> is available.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>export</command></term><term><command>save</command></term>
+                               <listitem><para>Export a subtree of keys to XML. If no subtree is defined right after the <command>export</command> command, <filename>system</filename> and current <filename>user</filename> trees will be exported. Output is written to standard output. The output encoding will allways be UTF-8, regardeless of your system encoding. UTF-8 is the most universal charset you can get when exchanging data between multiple systems. Accepts <option>-f</option>.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>import</command></term><term><command>load</command></term>
+                               <listitem><para>Import an XML representation of keys and save it to the keys database. If no filename is passed right after the <command>import</command> command, standard input is used. This command is only available when <filename>/usr/lib/libelektratools.so</filename> is available.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><command>monitor</command></term><term><command>mon</command></term>
+                               <listitem><para>Monitor a key for some value change. It will block your command line until a change in the key value is detected, then return its new value.</para></listitem>
+                       </varlistentry>
+               </variablelist>
+       </section>
+
+       <section id="kdbopt"><title>Options</title>
+               <variablelist>
+                       <varlistentry><term><option>-R</option></term>
+                               <listitem><para>Causes to work recursively. In <command>ls</command>, will list recursively.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><option>-x</option></term>
+                               <listitem><para>Makes <command>ls</command> output an XML representation of the keys, instead of an <command>ls</command>-compatible output.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><option>-l</option></term>
+                               <listitem><para>Causes to display long results. With <command>ls</command>, will generate lists similar to <command>ls -l</command>. With <command>get</command>, will show also the key name.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><option>-a</option></term>
+                               <listitem><para>Causes <command>ls</command> to display also inactive keys. Generate lists similar to <command>ls -a</command>. Inactive keys are keys which basename begins with a '.' (dot). An example of inactive key: <filename>system/sw/XFree/current/Monitor/.Monitor1</filename></para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><option>-f</option></term>
+                               <listitem><para>Causes to work with full key names. A full key name makes sense only on <filename>user/*</filename> keys, and differentiate from the regular key names in specifying the owner user. If the current user is <filename>someuser</filename>, the <filename>user/some/key</filename> full name is <filename>user:someuser/some/key</filename>. Makes effect in <command>ls</command>, <command>export</command> and <command>get</command> subcommands.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><option>-d</option></term>
+                               <listitem><para>Causes <command>get</command> to work descriptivelly. When requesting a key it will show the comment, key name and its value in a fancy format.</para></listitem>
+                               <listitem><para>Causes <command>set</command> to mark the key as a directory key.</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-s</option></term>
+                               <listitem><para>Causes <command>get</command> and <command>ls</command> to be more friendly to Shell scripts. For example, when requesting <filename>user/env/env2/PATH</filename>, the output will be PATH="the value", that is, only the basename of the key will be showed and the value will be surrounded by ' " '.</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-t type</option></term>
+                               <listitem><para>When <command>set</command>ting a key's value, you can specify the type with this switch. Currently accepted types are <type>string</type> for plain text, <type>bin</type> for binary as-is values, <type>dir</type> to create folder keys and <type>link</type> to create symbolic links between keys. Plain text are always stored as <citerefentry><refentrytitle>UTF-8</refentrytitle><manvolnum>7</manvolnum></citerefentry> in Elektra, regardeless of your current encoding (<envar>$LANG</envar>). If you want to force a value to be stored without the <citerefentry><refentrytitle>UTF-8</refentrytitle><manvolnum>7</manvolnum></citerefentry> encoding (a bad idea), you can set it as binary. Binary values should be avoided, because they are black boxes for system administrators.</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-b filename</option></term>
+                               <listitem><para>Set the key value as the content of file <filename>filename</filename>. This option is more usefull when setting binary keys.</para></listitem>
+                       </varlistentry>
+
+                       <varlistentry><term><option>-m mode</option></term>
+                               <listitem><para>For the <command>set</command> command. Will set the key access permission to <option>mode</option>, which must be an octal number as for <citerefentry><refentrytitle><command>chmod</command></refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-u uid</option></term>
+                               <listitem><para>Create the key with <option>uid</option> user ID. It can be a user name or a uid number.</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-g gid</option></term>
+                               <listitem><para>Create the key with <option>gid</option> group ID. It can be a group name or a gid number</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-c comment</option></term>
+                               <listitem><para>When <command>set</command>ting keys, you can use this argument to set a descriptive comment for it. This comment is exactly as a comment in a plain text configuration file. The comment is stored as <citerefentry><refentrytitle>UTF-8</refentrytitle><manvolnum>7</manvolnum></citerefentry> regardeless of your current encoding (<envar>$LANG</envar>).</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>-v</option></term>
+                               <listitem><para>With the <command>ls</command> subcommand, will make it show also the value stored in the key.</para></listitem>
+                       </varlistentry>
+                       
+                       <varlistentry><term><option>--</option></term>
+                               <listitem><para>With the <command>set</command> subcommand, everything after it will be considered the value, even text with dashes (-).</para></listitem>
+                       </varlistentry>
+               </variablelist>
+       </section>
diff --git a/doc/rgexample.xml b/doc/rgexample.xml
new file mode 100644 (file)
index 0000000..c66b15e
--- /dev/null
@@ -0,0 +1,39 @@
+<!--
+$Id$
+$LastChangedBy$
+-->
+       <section id="example"><title>Examples</title>
+               <section id="exset"><title>Setting Keys</title>
+                       <para><prompt>bash$ </prompt><command>kdb set -c "My first key" user/example/key "Some nice value"</command></para>
+                       <para><prompt>bash$ </prompt><command>kdb set user:luciana/example/key -- "Some - nice - value with dashes"</command></para>
+                       <para><prompt>bash# </prompt><command>KDB_ROOT=user:http/sw/httpd kdb set -u nobody -g http key "Some value"</command></para>
+                       <para><prompt>bash$ </prompt><command>kdb set -b image.png -t bin user/example/binaryKey</command></para>
+                       <para><prompt>bash$ </prompt><command>kdb set -b file.txt user/example/regularKey</command></para>
+                       <para><prompt>bash# </prompt><command>kdb set -t link system/sw/XFree/current system/sw/XFree/handmade</command></para>
+               </section>
+               <section id="exget"><title>Getting Keys</title>
+                       <para><prompt>bash$ </prompt><command>KDB_ROOT=user/example kdb get some/key/name</command></para>
+                       <para><prompt>bash$ </prompt><command>eval `kdb get -s user/env/env1/PS1`</command></para>
+                       <para><prompt>bash$ </prompt><command>KDB_BACKEND=gconf kdb get user/sw/gnome-terminal/global/active_encodings</command></para>
+               </section>
+               <section id="exls"><title>Listing</title>
+                       <para><prompt>bash$ </prompt><command>kdb ls -laR user:valeria</command></para>
+                       <para><prompt>bash$ </prompt><command>kdb ls -lR system/sw/xorg/current</command></para>
+                       <para><prompt>bash$ </prompt><command>KDB_ROOT=system/sw kdb ls -lR xorg</command></para>
+                       <para><prompt>bash$ </prompt><command>KDB_BACKEND=fstab kdb ls -Rv system/filesystems</command></para>
+                       <para><prompt>bash$ </prompt><command>eval `kdb ls -Rvs user/env/env2`</command></para>
+               </section>
+               <section id="exmisc"><title>Miscelaneous</title>
+                       <para><prompt>bash# </prompt><command>kdb ln system/sw/xorg/handmade system/sw/xorg/current</command></para>
+                       <para><prompt>bash# </prompt><command>kdb mv system/sw/xorg/current system/sw/xorg/old</command></para>
+                       <para><prompt>bash# </prompt><command>kdb rm system/inittab/rc4</command></para>
+                       <para><prompt>bash$ </prompt><command>KDB_BACKEND=gconf kdb rm user/gconfKey</command></para>
+               </section>
+               <section id="exxml"><title>XML Import and Export</title>
+                       <para><prompt>bash# </prompt><command>kdb export user/sw/app | sed -e 's|/app/|/app2/|g' | kdb import</command></para>
+                       <para><prompt>bash# </prompt><command>KDB_ROOT=system/sw kdb export myapp > myappconf.xml</command></para>
+                       <para><prompt>bash# </prompt><command>kdb import myappconf.xml</command></para>
+                       <para><prompt>bash$ </prompt><command>KDB_BACKEND=gconf kdb export user/sw</command></para>
+               </section>
+       </section>
+       
diff --git a/doc/society.xml b/doc/society.xml
new file mode 100644 (file)
index 0000000..b09d20d
--- /dev/null
@@ -0,0 +1,30 @@
+
+<!--
+$Id$
+$LastChangedBy$
+-->
+<section id="society"><title>A Society of Softwares, Bureocracy and Integration</title>
+       <para>Let's imagine that every Open Source project (and commercial software) out there are members of a society. They can't live alone each one in its own cave. XFree, Apache, Samba can't work without an OS, and an OS is useless without higher level software. KDE, Gnome need X. Nobody can think about setting up an Internet MTA without an anti-SPAM tool, and strong commercial anti-virus software. Apache needs third party plugins and extensions to add value to what it does. And all of them need a growing number of users to justify a purpose for them to exist.</para>
+       
+       <para>The way these members start to work together - and then really make the <emphasis>society</emphasis> evolve - is through their configurations. As an example, an advanced commodity video card must say to X Window System: "Hey! I'm providing this driver and parameters, and I guarantee for you and your customer super exciting fast graphics!! Let me show you how to do that". And then X must say: "That's great! Please proceed making my customer happy, without taking his and <emphasis>my</emphasis> time to show we can work together. And BTW lets make some co-marketing to advertise the value of our alliance."</para>
+       
+       <para>But the hypothetical conversation happening today is:</para>
+       
+       <simpara><emphasis>Commercial Driver:</emphasis> "Hey, XFree, I'd like to reach your customers 'cause they like that 'F' word in your name. I'd put together some code to work with your supply chain specification (driver API), but you have such a big network of brokers (OS distros), and each one showed me its set of paper forms (configuration files) I have to fill to work with you."</simpara>
+       
+       <simpara><emphasis>XFree:</emphasis> "Yeah, thats great. But look, I'm very focused now working on performance and architecture. You should really try to understand how each of these brokers work, and how to fill their forms."</simpara>
+       
+       <simpara><emphasis>Commercial Driver:</emphasis> "Well, thats quite impossible because they are too many, and that's just too much bureocracy for me to handle. So I'll keep myself focused on that other broker that is so easy to plug-and-play. And the best I can do for you is to write a white paper for your customers, explaining how to use a text editor to get us working together. And BTW I'm not able to provide support, because I can't understand your ecosystem of brokers and users."</simpara>
+       
+       <simpara><emphasis>XFree:</emphasis> "OK. That's life."</simpara>
+       
+       <simpara><emphasis>XFree Customer:</emphasis> "Oh boy, why is this Video Driver I just bought so difficult to install and manage? Why is so easy to do that in my other partition? Why the driver installs itself so easily there, and it is so complicated here?"</simpara>
+       
+       <simpara>(this conversation received a considerable amount of drama, it is just an example, and this specific entities in real life may not have exactly the issues showed here)</simpara>
+       
+       <para>How many advanced Linux/*BSD/GNU user have been asked to help a newbie friend to setup his video card? I know plenty of them.</para>
+       
+       <para>Because of the Bazar-way of developing Open Source Software, each software project still dodn't agree in a common way to store configurations, so this part of the software is still like 20 years ago: a human-readable text file format that each project invented for itself. This makes them relatively easy to be managed by human beings, but not sociable at all in this hipotetical software society. It is programatically difficult to make different software have "social relations" in the current way configurations are handled. An application's configuration is its personality.</para>
+       
+       <important><para>A key-value pair mechanism brings little or no additional value for a software that is using it to store its configurations/state. But it is vital to make it more "easy-going" in a society of softwares. This will benefit the society as a whole, and consequently all of its members.</para></important>
+</section>
diff --git a/doc/standards/Makefile.am b/doc/standards/Makefile.am
new file mode 100644 (file)
index 0000000..1fb5d2c
--- /dev/null
@@ -0,0 +1,4 @@
+
+# $Id$
+
+EXTRA_DIST= env.xml filesystems.xml network.xml signature.xml userdb.xml sysinit.xml
diff --git a/doc/standards/env.xml b/doc/standards/env.xml
new file mode 100644 (file)
index 0000000..82a4cd5
--- /dev/null
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+Subversion stuff
+
+$Id$
+
+-->
+
+<!--
+
+This specification-by-example defines an easier, more direct and more precise
+way for applications to set aliases and env vars for a user session.
+
+It will, in some ways, substitute the functionality of the files
+
+       /etc/profile
+       ~/.profile
+       ~/.bashrc
+       ~/.bash_profile
+
+Although on those files some can add code and some logic to define env vars,
+this key schema is usefull when you just want to set environments in an
+atomic way.
+
+The /etc/profile.d/elektraenv.sh script loads the environments declared by
+the keys in this schema.
+
+The key hierarchy proposed (as examples) is:
+
+Systemwide environments, as in /etc/profile:
+       system/
+         env/
+           env1/
+             JAVA_HOME              = /opt/IBMJava2
+           env2/
+             PATH                   = $PATH:$JAVA_HOME/bin
+             LIBPATH                = $LIBPATH:$JAVA_HOME/lib
+           env3/
+             EDITOR                 = vi
+           alias/
+             ll                     = ls -l
+
+User specific environment (loaded after system's), as in ~/.profile
+       user/
+         env/
+           env1/
+           env2/
+             PATH                   = $PATH:~/bin
+             LIBPATH                = $LIBPATH:~/lib
+           env3/
+             PS1                    = \h:\w\$ 
+             EDITOR                 = kate
+           alias/
+             ls                     = ls -Fh -color=auto
+             ll                     = ls -l -Fh -color=auto
+             vi                     = gvim -x
+
+Note that we have env1, env2 and env3 levels.
+Thats because a script has a natural order for the things to happen, so we
+can easily define $PATH using $JAVA_HOME, but after defining $JAVA_HOME.
+Since there is no such natural order when accessing keys and values, the
+elektraenv script will fetch env1, env2, env3 in this order, then envvars
+can be defined dependent on previously defined other envvars, as
+with $PATH depending on $JAVA_HOME.
+
+In addition, the elektraenv script will fetch system/env keys before it
+fetches user/env keys.
+
+Avi Alkalay <avi@unix.sh>
+Nov 2005
+
+-->
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd">
+
+<!-- The systemwide environment and aliases -->
+       <keyset parent="system/env/env1">
+               <key basename="JAVA_HOME" value="/opt/IBMJava2"/>
+       </keyset>
+
+
+       <keyset parent="system/env/env2">
+               <key basename="PATH">
+                       <value>$PATH:$JAVA_HOME/bin</value>
+                       <comment>Since we are under env2, and $JAVA_HOME was defined in env1, at this point, $JAVA_HOME is already set.</comment>
+               </key>
+               
+               <key basename="LIBPATH" value="$LIBPATH:$JAVA_HOME/lib"/>
+       </keyset>
+
+
+       <keyset parent="system/env/env3">
+               <key basename="EDITOR" value="vi"/>
+       </keyset>
+
+
+       <keyset parent="system/env/alias">
+               <key basename="ll" value="ls -l"/>
+       </keyset>
+
+
+
+
+<!-- The user specific environment and aliases -->
+       <keyset parent="user/env/env2">
+               <key basename="PATH"    value="$PATH:~/bin"/>
+               <key basename="LIBPATH" value="$LIBPATH:~/lib"/>
+       </keyset>
+       
+       
+       <keyset parent="user/env/env3">
+               <key basename="PS1"    value="\h:\w\$ "/>
+               <key basename="EDITOR" value="kate"/>
+       </keyset>
+               
+               
+       <keyset parent="user/env/alias">
+               <key basename="ls" value="ls -Fh --color=tty"/>
+               <key basename="ll" value="ls -l -Fh --color=tty"/>
+               <key basename="vi" value="gvim -x"/>
+       </keyset>
+
+</keyset>
\ No newline at end of file
diff --git a/doc/standards/filesystems.xml b/doc/standards/filesystems.xml
new file mode 100644 (file)
index 0000000..55ba792
--- /dev/null
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+Subversion stuff
+
+$Id$
+
+-->
+
+<!--
+
+This specification-by-example comes to substitute the functionality of /etc/fstab file.
+
+The key hierarchy proposed is:
+
+       system/
+         filesystems/
+           [Some Filesystem pseudo-name]
+             device     =  /dev/hda6
+             mpoint     =  /mnt/work
+             type       =  vfat
+             options    =  uid=aviram,gid=aviram,iocharset=utf8,posix
+             dumpfreq   =  0
+             passno     =  0
+
+           [rootfs]
+             device     =  LABEL=/
+             mpoint     =  /
+             type       =  ext3
+             options    =  default
+             dumpfreq   =  1
+             passno     =  1
+
+           [cdrecorder]
+             device     =  /dev/hdc
+             mpoint     =  /media/cdrecorder
+             type       =  auto
+             options    =  noauto,managed,pamconsole
+             dumpfreq   =  0
+             passno     =  0
+
+
+The Elektra's "fstab" backend generates a tree like this using the system's
+/etc/fstab, so the command:
+
+       $ KDB_BACKEND=fstab kdb export system/filesystems
+
+will generate an XML document like this for your /etc/fstab.
+
+Avi Alkalay <avi@unix.sh>
+11-2005
+
+-->
+
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+        parent="system/filesystems">
+
+
+       <keyset parent="mediacdrecorder">
+               <!-- This keyset is equivalent to the following /etc/fstab line:
+
+                         /dev/hdc /media/cdrecorder auto pamconsole,exec,noauto,managed 0 0
+
+               -->
+               <key basename="device">
+                       <value>/dev/hdc</value>
+                       <comment>The device or label to be mounted</comment>
+               </key>
+
+
+               <key basename="mpoint">
+                       <value>/media/cdrecorder</value>
+                       <comment>Moint point someplace in the filesystem</comment>
+               </key>
+
+
+               <key basename="type">
+                       <value>auto</value>
+                       <comment>Filesystem type. See fs(5)</comment>
+                       <comment>Could be ext3, reiserfs, jfs, ntfs, vfat, etc.</comment>
+               </key>
+               
+               
+               <key basename="options">
+                       <value>pamconsole,exec,noauto,managed</value>
+                       <comment>Filesystem specific options. See mount(8)</comment>
+               </key>
+
+               
+               <key basename="dumpfreq">
+                       <value>0</value>
+                       <comment>Dump frequency in days</comment>
+               </key>
+               
+
+               <key basename="passno">
+                       <value>0</value>
+                       <comment>Pass number on parallel fsck</comment>
+               </key>
+               
+       </keyset>
+
+
+
+
+
+
+       <keyset parent="rootfs">
+               <!-- This keyset is equivalent to the following /etc/fstab line:
+                         
+                         LABEL=/  /   ext3  defaults  1 1
+                         
+               -->
+               <key basename="device"   value="LABEL=/"/>
+               <key basename="mpoint"   value="/"/>
+               <key basename="type"     value="ext3"/>
+               <key basename="options"  value="defaults"/>
+               <key basename="dumpfreq" value="1"/>
+               <key basename="passno"   value="1"/>
+       </keyset>
+
+
+
+
+
+
+
+       <keyset parent="Windows and Linux shared filesystem">
+               <!-- This keyset is equivalent to the following /etc/fstab line:
+
+                         /dev/hda6  /mnt/work  vfat  uid=aviram,gid=aviram,iocharset=utf8,posix   0 0
+
+               -->
+               <key basename="device"   value="/dev/hda6"/>
+               <key basename="mpoint"   value="/mnt/work"/>
+               <key basename="type"     value="vfat"/>
+               <key basename="options"  value="uid=aviram,gid=aviram,iocharset=utf8,posix"/>
+               <key basename="dumpfreq" value="0"/>
+               <key basename="passno"   value="0"/>
+       </keyset>
+       
+</keyset>
\ No newline at end of file
diff --git a/doc/standards/network.xml b/doc/standards/network.xml
new file mode 100644 (file)
index 0000000..c089a78
--- /dev/null
@@ -0,0 +1,460 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Subversion stuff
+
+$Id$
+
+-->
+
+<!--
+
+This is a specification-by-example key hierarchy for a system's network configuration.
+It covers the functionality currently provided by these files:
+
+  /etc/hosts
+  /etc/resolv.conf
+  /etc/sysconfig/network
+  /etc/sysconfig/network-scripts/ifcfg-NNN
+  /etc/sysconfig/network-scripts/keys-NNN
+  /etc/sysconfig/network-scripts/route-NNN
+  (where NNN are arbitrary Unix network interfaces names as eth0)
+
+
+  Follows the key structure proposed here with some examples.
+  The surrounding "[ ]" on some key names are there to denote a user defined
+  key name.
+
+  system/
+    network/
+      profile     =default
+      profiles/
+      +-default/
+      | | hostname    =example.domain.com
+      | | nameserver  =ns1.domain.com,ns2.domain.com,ns3.domain.com
+      | | search      =domain.com,sub1.domain.com,sub2.domain.com
+      | +-interfaces/
+      | | +-Loopback/
+      | | |   name         =Loopback
+      | | |   device       =lo0
+      | | |   onboot       =1
+      | | |   ip           =127.0.0.1
+      | | +-[SymbolicName1]/
+      | | |   name         =SymbolicName1
+      | | |   devicename   =Intel Corporation 82540EP Gigabit Eth...
+      | | |   device       =eth0
+      | | |   HWaddress    =00:01:02:AB:DE:34
+      | | |   onboot       =1
+      | | |   ip           =10.100.8.100
+      | | |   netmask      =255.255.255.0
+      | | |   gateway      =10.100.8.1
+      | | |   type         =ethernet
+      | | |   usercontrol  =0
+      | | |   ipv6init     =0
+      | | |   bootproto    =static
+      | | |   routes/
+      | | |   +-[route1]/
+      | | |   |   destination =10.100.9.0
+      | | |   |   netmask     =255.255.255.0
+      | | |   |   gateway     =10.100.9.0
+      | | |   +-[otherroute]/
+      | | |   |   destination =10.100.10.0
+      | | |   |   netmask     =255.255.255.0
+      | | |   =   gateway     =10.100.8.3
+      | | +-[WirelessDevice1]/
+      | | |   name         =WirelessDevice1
+      | | |   devicename   =Cisco Aironet Wireless 802.11b
+      | | |   device       =eth1
+      | | |   HWaddress    =01:0E:61:13:A9:C7
+      | | |   onboot       =0
+      | | |   gateway      =10.100.8.1
+      | | |   usercontrol  =1
+      | | |   ipv6init     =0
+      | | |   bootproto    =dhcp
+      | | |   peerdns      =1
+      | | |   type         =wireless
+      | | |      ++ wireless stuff ++
+      | | |   mode         =infrastructure
+      | | |   channel      =1
+      | | |   essid        =Office Network
+      | | |   key          =ABC123
+      | = =   dhcphostname =something.domain.com
+      +-[Office]/
+        | inherits         =default
+        + interfaces\
+        | +-[SymbolicName1]/
+        | |   bootproto    =dhcp
+        | |   dhcphostname =something.domain.com
+        | |   peerdns      =1
+        | +- -routes/
+        =       (nothing, to eliminate its parent)
+
+
+
+Each key bellow has a comment about its purpose.
+This XML file can be imported with a 'kdb import' command.
+
+Change the "parent" attribute on the first <keyset> to import
+it to a test tree.
+
+Avi Alkalay, Nov 2005
+<avi at unix dot sh>
+
+-->
+
+
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+        parent="system/network">
+
+
+       <key basename="profile">
+               <value>default</value>
+               <comment>The network configuration profile name that should be used as a default.</comment>
+               <comment>A profile contains an entire network configuration.</comment>
+       </key>
+
+
+
+       <keyset parent="profiles/default">
+               <key basename="hostname">
+                       <value>example.domain.com</value>
+                       <comment>The fully qualified host name</comment>
+               </key>
+
+
+
+               <key basename="nameserver">
+                       <value>ns1.domain.com, ns2.domain.com, ns1.organization.org</value>
+                       <comment>Comma separated list of name servers.</comment>
+               </key>
+
+
+
+               <key basename="search">
+                       <value>domain.com, sub1.domain.com, sub2.domain.com, domain2.com</value>
+                       <comment>Comma separated search domains. Domains that will be tested together with a non FQDN host.</comment>
+               </key>
+
+
+
+
+               <keyset parent="hosts">
+                       <!--
+                               The system/network/hosts folder is equivalent to the /etc/hosts file.
+                               Contains one key per IP address for each IP you would put on /etc/hosts,
+                               and the value is a comma separated list of hostnames
+                       -->
+
+                       <key basename="127.0.0.1">
+                               <value>localhost,localhost.localdomain,example.domain.com</value>
+                               <comment>The localhost. Check that I have also example.domain.com since this is my hostname as noted on system/network/hostname above.</comment>
+                       </key>
+
+
+                       <key basename="10.80.50.1">
+                               <value>mta,mta.domain.com,mailhub</value>
+                               <comment>The IP address of our mail server, with several aliases.</comment>
+                       </key>
+               </keyset>
+
+
+
+
+               <keyset parent="interfaces">  <!-- Interfaces section begins -->
+
+                       <key basename="LoopBack/name">  <!-- The "Loopback" device configuration follows -->
+                               <value>LoopBack</value>
+                               <comment>This a symbolic name for the device, and use to be identical to this key's parent directory name. This name is intended to be used in user interfaces. If this name is renamed, the parent directory key should be renamed too.</comment>
+                       </key>
+
+
+                       <key basename="LoopBack/device">
+                               <value>lo</value>
+                               <comment>The Unix device name to be used with 'ifconfig' command</comment>
+                       </key>
+
+
+                       <key basename="LoopBack/ip">
+                               <value>127.0.0.1</value>
+                               <comment>The fixed IP address of this interface.</comment>
+                       </key>
+
+
+                       <key basename="LoopBack/netmask">
+                               <value>255.0.0.0</value>
+                       </key>
+
+
+                       <key basename="LoopBack/broadcast">
+                               <value>127.255.255.255</value>
+                               <comment>If you're having problems with gated making 127.0.0.0/8 a martian, you can change this to something else (255.255.255.255, for example).</comment>
+                       </key>
+
+
+                       <key basename="LoopBack/onboot">
+                               <value>1</value>
+                               <comment>1 for automatic activation on boot, 0 for only manual</comment>
+                       </key>
+
+
+
+
+                       <key basename="SymbolicName1/name"> <!-- The "SymbolicName1" device configuration follows -->
+                               <value>SymbolicName1</value>
+                               <comment>This a symbolic name for the device, and use to be identical to this key's parent directory name. This name is intended to be used in user interfaces. If this name is renamed, the parent directory key should be renamed too.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/devicename">
+                               <value>Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile)</value>
+                               <comment>The device name as returned by its chipset</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/device">
+                               <value>eth0</value>
+                               <comment>The UNIX device name.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/ip">
+                               <value>10.100.8.100</value>
+                               <comment>The fixed IP address of this interface. Will be used only if bootproto key for this interface is not dhcp.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/netmask">
+                               <value>255.255.255.0</value>
+                               <comment>The netmask. Will be used only if bootproto key for this interface is not dhcp.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/HWaddress">
+                               <value>00:0D:60:12:A8:C6</value>
+                               <comment>The MAC address of this network interface</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/bootproto">
+                               <value>static</value>
+                               <comment>The protocol type to be used to configure this interface. DHCP, BOOTP or static.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/ipv6init">
+                               <value>0</value>
+                               <comment>Weather this interface should be initialized with IPv6 too. Valid values are 0 or no and 1 or yes.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/onboot">
+                               <value>1</value>
+                               <comment>1 for automatic activation on boot, 0 for only manual</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/type">
+                               <value>ethernet</value>
+                               <comment>The phisical type of this network interface. Ethernet, Wireless, TokenRing, etc</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/usercontrol">
+                               <value>1</value>
+                               <comment>1 to let regular users control this interface, 0 to be controled only by root</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/peerdns">
+                               <value>1</value>
+                               <comment>1 or yes means the DNS server will be provided by the remote peer.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/gateway">
+                               <value>10.100.8.1</value>
+                               <comment>Default gateway for the LAN connected to this interface.</comment>
+                       </key>
+
+
+                       <!-- Here are 2 static routes, "route1" and "otherrout", associated to this interface -->
+                       <key basename="SymbolicName1/routes/route1/gateway">
+                               <value>10.100.8.2</value>
+                               <comment>The IP address that provides us a gateway to the symbolic "route1" destination</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/routes/route1/destination">
+                               <value>10.100.9.0</value>
+                               <comment>The symbolic "route1" destination is really this IP class plus netmask bellow.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/routes/route1/netmask">
+                               <value>255.255.255.0</value>
+                               <comment>The size and details of the destination network through its netmask.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/routes/otherroute/gateway">
+                               <value>10.100.8.3</value>
+                               <comment>The IP address that provides us a gateway to the symbolic "otherroute" destination</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/routes/otherroute/destination">
+                               <value>10.100.10.0</value>
+                               <comment>The symbolic "otherroute" destination is really this IP class plus netmask bellow.</comment>
+                       </key>
+
+
+                       <key basename="SymbolicName1/routes/otherroute/netmask">
+                               <value>255.255.255.0</value>
+                               <comment>The size and details of the destination network through its netmask.</comment>
+                       </key>
+
+
+
+
+
+                       <key basename="WirelessDevice1/name"> <!-- The "WirelessDevice1" device configuration follows -->
+                               <value>WirelessDevice1</value>
+                               <comment>This a symbolic name for the device, and use to be identical to this key's parent directory name. This name is intended to be used in user interfaces. If this name is renamed, the parent directory key should be renamed too.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/devicename">
+                               <value>AIRONET Wireless Communications Cisco Aironet Wireless 802.11b</value>
+                               <comment>The device name as returned by its chipset</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/device">
+                               <value>eth1</value>
+                               <comment>The UNIX device name.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/essid">
+                               <value>Office Network</value>
+                               <comment>The wireless session ID name to associate with. If empty, automatic behavior should be assumed.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/channel">
+                               <value>1</value>
+                               <comment>The wireless channel to use</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/mode">
+                               <value>infrastructure</value>
+                               <comment>The wireless mode to use. Mode or Infastructure.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/rate">
+                               <value>11M</value>
+                               <comment>The wireless speed to use. Common values are "11M", "auto", "5.5M", "1M" and "2M".</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/key" mode="0600">
+                               <value>ABC123</value>
+                               <comment>The wireless key for security. Use to be an hex number without the "0x" prefix. Pay attention to the "mode" attirbute, that differs from others, being more restricted (rw-------)</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/HWaddress">
+                               <value>01:0E:61:13:A9:C7</value>
+                               <comment>The MAC address of this network interface</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/bootproto">
+                               <value>dhcp</value>
+                               <comment>The protocol type to be used to configure this interface. DHCP, BOOTP or static.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/dhcphostname">
+                               <value>something.domain.com</value>
+                               <comment>The hostname we'll tell to the DHCP server, for dynDNS purposes.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/ipv6init">
+                               <value>0</value>
+                               <comment>Weather this interface should be initialized with IPv6 too. Valid values are 0 or no and 1 or yes.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/onboot">
+                               <value>1</value>
+                               <comment>1 for automatic activation on boot, 0 for only manual</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/type">
+                               <value>wireless</value>
+                               <comment>The phisical type of this network interface. Ethernet, Wireless, TokenRing, etc</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/usercontrol">
+                               <value>1</value>
+                               <comment>1 to let regular users control this interface, 0 to be controled only by root</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/peerdns">
+                               <value>1</value>
+                               <comment>1 or yes means the DNS server will be provided by the remote peer.</comment>
+                       </key>
+
+
+                       <key basename="WirelessDevice1/gateway">
+                               <value>10.100.8.1</value>
+                               <comment>Default gateway for the LAN connected to this interface.</comment>
+                       </key>
+               </keyset> <!-- parent="interfaces" -->
+       </keyset> <!-- parent="profiles/default" -->
+
+
+
+
+       <keyset parent="profiles/Office">
+               <!--
+                       This "Office" profile should be identical to "default" with the exception
+                       that the "SymbolicName1" interface should be activated by dhcp, instead of
+                       a static IP as on the "default" profile.
+               -->
+               <key basename="inherits">
+                       <value>default</value>
+                       <comment>The profile name that is used as a base to build this "Office" profile.</comment>
+                       <comment>This way, we don't have to redefine all keys again, but only the keys that change in relation to the inherited profile. In this case, the "default" profile .</comment>
+               </key>
+
+
+
+
+               <keyset parent="interfaces">
+                       <key basename="SymbolicName1/bootproto">
+                               <value>dhcp</value>
+                               <comment>The protocol type to be used to configure this interface. DHCP, BOOTP or static.</comment>
+                       </key>
+
+                       <key basename="SymbolicName1/dhcphostname">
+                               <value>something.domain.com</value>
+                               <comment>The hostname we'll tell to the DHCP server, for dynDNS purposes.</comment>
+                       </key>
+
+
+                       <!-- We'll define an empty "routes" folder here,
+                       to clean the static route table inherited by the "default" profile. -->
+                       <key basename="SymbolicName1/routes" type="directory"/>
+               </keyset> <!-- parent="interfaces" -->
+       </keyset> <!-- parent="profiles/Office" -->
+</keyset>
diff --git a/doc/standards/signature.xml b/doc/standards/signature.xml
new file mode 100644 (file)
index 0000000..3956499
--- /dev/null
@@ -0,0 +1,325 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+
+Subversion stuff
+
+$Id$
+
+-->
+
+<!--
+
+The purpose of this specification-by-example is to define a standard way for systems to provide their own signature.
+It is important because today there is no safe way for somebody (a program) to know exactly on which
+OS (platform, version, update level, edition) it is running.
+
+This is particulary useful for installation programs, when they must take decision about where to put files they'll
+install, since each OS have slight differences about their filesystem organization, etc.
+
+Its purpose is similar to the files installed by redhat-release or SuSE-release packages, but better:
+
+/etc/issue
+/etc/issue.net
+/etc/redhat-release
+/etc/ssh/ssh_host*key*
+
+This is a very simple structure, and here follows the signature for a Red Hat Enterprise Linux 4 update 2 (Nanant):
+
+system/signature
+  provider     = Red Hat
+  os           = Linux
+  type         = redhat
+  edition      = AS
+  version      = 4
+  update       = 2
+  architecture = ia32
+  readable     = Red Hat Enterprise Linux 4 AS update 2 (Nanant)
+  releasename  = Nanant
+  url          = http://www.redhat.com/
+  hostname     = mozart.br.ibm.com
+  hostid       = 001f0700
+
+
+
+
+A Novell Linux Desktop would have this signature:
+
+system/signature
+  provider     = Novell
+  os           = Linux
+  type         = suse
+  edition      = NLD
+  version      = 10
+  update       = 0
+  architecture = ia32
+  readable     = Novell Linux Desktop
+  url          = http://www.novell.com/
+  hostname     = desktop.mycompany.com
+  hostid       = 001f0700
+
+
+
+A Fedora 4 system for Opteron processos would be:
+
+system/signature
+  provider     = Fedora
+  os           = Linux
+  type         = redhat
+  version      = 4
+  architecture = x86_64
+  readable     = Fedora Core 4
+  url          = http://fedora.redhat.com/
+  hostname     = development.mycompany.com
+  hostid       = 001f0700
+
+
+
+Additionaly, under this key, you can put SSH private and public keys, like this:
+
+system/signature
+  ssh_host_dsa_key     = 1234676456474
+  ssh_host_dsa_key.pub = 654321234
+  ssh_host_rsa_key     = 1234676456474
+  ssh_host_rsa_key.pub = 654321234
+  ssh_host_key         = 1234676456474
+  ssh_host_key.pub     = 654321234
+
+
+Avi Alkalay
+<avi at unix.sh>
+Nov 2005
+
+-->
+
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+        parent="system/signature">
+
+
+       <key basename="provider">
+               <value>Red Hat</value>
+               <comment>The company or organization that provides this operating system.</comment>
+               <comment>Common values would be SuSE, Debian, CetnOS, Microsoft, etc.</comment>
+       </key>
+
+
+
+
+
+
+       <key basename="os">
+               <value>Linux</value>
+               <comment>The simple name of this OS. Could be "OpenBSD", "Windows", etc</comment>
+       </key>
+
+
+
+
+
+
+       <key basename="type">
+               <value>redhat</value>
+               <comment>A name for whom the system looks like.</comment>
+               <comment>This is the most important key since it provides the hint about the filesystem layout, packaging naming conventions, etc</comment>
+               <comment>Could be "redhat" for RHEL, CentOS, Fedora, Mandriva, Whitebox, Scientific Linux, etc.</comment>
+               <comment>"debian" for Debian, Ubuntu, KUbuntu and all Debian-derived distro's</comment>
+               <comment>"BSD" for all *BSD systems</comment>
+               <comment>"suse" for SLES, OpenSuSE and Novell Linux Desktop</comment>
+               <comment>Or "slackware", "aix", "solaris" etc for other systems</comment>
+       </key>
+
+
+
+
+
+
+       <key basename="readable">
+               <value>Red Hat Enterprise Linux 4 AS</value>
+               <comment>A nice human readable system name.</comment>
+       </key>
+
+
+
+
+
+
+       <key basename="releasename">
+               <value>Nanant</value>
+               <comment>An internal name that providers use to give to their products.</comment>
+               <comment>Could be "Potato" for Debian, "Vista" for Windows, etc.</comment>
+       </key>
+
+
+
+
+
+
+       <key basename="edition">
+               <value>AS</value>
+               <comment>The edition of this OS. Common values are "Home Edition", "ES", "WS", Professional, etc</comment>
+       </key>
+
+
+
+
+
+
+
+
+       <key basename="version">
+               <value>4</value>
+               <comment>The version of this OS.</comment>
+               <comment>Could be "95" or "2000" for popular Windows versions, "7.3" for old Red Hat versions, etc.</comment>
+       </key>
+
+
+
+
+
+
+
+
+
+       <key basename="update">
+               <value>2</value>
+               <comment>The update or patch level of a system. Could be "SP3" for Windows systems, etc.</comment>
+       </key>
+
+
+
+
+
+
+
+
+
+       <key basename="architecture">
+               <value>ia32</value>
+               <comment>The platform this OS is installed on. Popular names are "x86_64", "ppc", "s390", "s390x", "sparc", "ia64".</comment>
+       </key>
+
+
+
+
+       <key basename="url">
+               <value>http://www.redhat.com</value>
+               <comment>Some URL the provider wants to be published.</comment>
+       </key>
+
+
+
+
+       <key basename="hostname">
+               <value>mozart.br.ibm.com</value>
+               <comment>The current hostname.</comment>
+       </key>
+
+
+
+       <key basename="hostid">
+               <value>007f0100</value>
+               <comment>The hostid as from the `hostid` command.</comment>
+       </key>
+
+
+
+       <key basename="ssh_host_dsa_key" mode="0600">
+               <value>-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQCguDE4+ZUx0JeKeypSCaGCXpDZ9W7BUykE8JV5aQ1M2cafv/lP
+LF/nBrHEQ2sY21e8aliuDKLxG6XWlcS1XePzsDVBlPsHwt3C0CfZh6IJkPCqU028
+1wV49q3LMJ6q1YfuWP1p3QZ3QuuxNpAiQob7iMNTR276tXy5llzUsgFRaQIVANEB
+Dc2HPwuN3T46gWatFRGf8SIgsdh54ygfffewfwf42dsxccaChIz2m/ZN7KDp0MxJ
+PR1Unk8lwY84PGepZcbWM7aJyijnKq1LKf8o8jsAMTV8BL6NR4SgDQ5jCT/VFcEu
+VZNj1MFHu91R4QGpl4OC0DfNcO8vqbSRwQDIrKP4+ZzrT6z25y1+I9skVlftox4W
+Fdvv77QCgYAOIP+QDKa0MdFrHsSdW8OwQXzOI9GZ6rMKj8Hnlq6lV5KGk0d1Bw86
+7x9zN27EnyT8v4uoagba6SKReXX40znPIH247G4PuntIPNf63OnIptuYwYBG03gZ
+6N5Agq6c3VFJMtXfPZOeQEdCRqFzbAkvADsmRUFymwT5KJxwqoKJ5QIVAIoN1f+Y
+cKz8nTtQbm2eVIXGuJCb
+-----END DSA PRIVATE KEY-----</value>
+               <comment>The SSH host DSA private key, with restricted access rights.</comment>
+       </key>
+
+
+
+
+
+       <key basename="ssh_host_dsa_key.pub" mode="0644">
+               <value>ssh-dss AAAAB3NzaC1kc3fsdhtttthwtgvhgerrc4p7KlIJoYJekNn1bsFTKQTwlXlpDUzZxp+/+U8sX+cGscRDaxjbV7xqWK4MovEbpdaVxLVd4/OwNUGU+wfC3cLQJ9mHogmQ8KpTTbzXBXj2rcswnqrVh+5Y/WndBndC67E2kCJChvuIw1NHbvq1fLmWXNSyAVFpAAAAFQDRAQ3Nhz8Ljd0+OoFmrRURlPEiNwAAAIBGli6BpycCe4j6fEjKtoKEjPab9k3soOnQzEk9HVSeTyXBjzg8Z6llxtYztonKKOcqrUsp/yjyOwAxNXwEvo1HhKANDmMJP9UVwS5Vk2PUwUe73VHhAamXg4LQN81w7y+ptJHBAMiso/j5nOtPrPbnLX4j2yRWV+2jHhYV2+/vtAAAAIAOIP+QDKa0MdFrHsSdW8OwQXzOI9GZ6rMKj8Hnlq6lV5KGk0d1Bw867x9zN27EnyT8v4uoagba6SKReXX40znPIH247G4PuntIPNf63OnIptuYwYBG03gZ6N5Agq6c3VFJMtXfPZOeQEdCRqFzbAkvADsmRUFymwT5KJxwqoKJ5Q==</value>
+               <comment>The SSH host DSA public key, world readable.</comment>
+       </key>
+
+
+
+
+
+       <key basename="ssh_host_key" type="binary" mode="0600">
+               <value>
+53534820 50524956 41544520 4b455920 46494c45 20464f52 4d415420 312e310a
+00000000 00000000 04000400 beab5cd0 35bc1a52 6c17e3c7 a87ecf1b 9158a565
+e14470a1 ade7f4c7 ba846eb3 39193529 dc24e0f4 cf16f87e 6ca44868 3f768dd6
+7eef557f e8dd23b6 3a055005 8a5811ce 00ed3e89 eb543497 e80113ba 8f5dfa8f
+0e2f8574 3914d1b5 104da21c eee90e84 6f47acf0 c82f3e65 51840e79 ed336aa6
+d342f579 9ecf67b0 35693f7d 00062300 436bf01a db1adb04 0082bea6 0b1d884c
+904a1f02 97897b86 b3d1615b 790f7f63 2d09891c e0ba6970 7ae5531d 2404ab92
+f103086f e1a99538 f706f235 5fe20333 506529ec 9a366a0a fb4cacbe 1af84c7d
+9ee71ace 083f477e 2b5c1b97 61353e15 0c8d05da 42d0ac87 2e2d7020 ddef6e91
+bcd3328f 6316355c 99a44006 604dd958 1e319c97 344e6d9f 6b01ff48 8d79943c
+e70564ce 6089c609 13984747 518fa7ba d55b1d49 97f21418 a39d0d54 6b320cdb
+c3676464 578a45c7 dcc609bb 872b87b8 31156dc0 94be1d17 a15d3b02 00d12d26
+c26df169 530f33ae 0ece96b4 ffc894d9 3a8cdef6 9060e775 67a5ab14 4091f315
+77c4dc88 eaf07b2f bf2f01b7 99ad0303 4c4b80da 840a2775 1bae875e 9f0200e9
+59ab243e 36f359cb 196f67bd 221c10e0 7639e6be 560586df da28abfa 519e0e5a
+b273c902 948f7d23 b4fd8af7 74852adb 5308b8c0 e5251ee1 a39612e9 d7586300
+000000
+               </value>
+               <comment>The SSH host private key, with restricted access rights.</comment>
+       </key>
+
+
+
+
+
+       <key basename="ssh_host_key.pub" type="string" mode="0644">
+               <value>1024 35 133892595653256457463456456345345488979733150777187284158956215390108993297627905677371390137055710482921314394380186549930047947484792463530275815344585642129311166497767148739660518671459802791497180732174353699608837459155915281166640936134956874158021502283692507013138334991381984009372215977800337276797</value>
+               <comment>The SSH host public key, world readable.</comment>
+       </key>
+
+
+
+
+
+       <key basename="ssh_host_rsa_key" mode="0600">
+               <value>-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDqcKzcwbCTe7l11vIdCxvcsNNABW0ASQXn2f3ssRGmjqn08k1y
+/94uQiVG3bEWdPpkeo3WM8THm+8seLcm/2oEt3IMBpmMDVD44XIpHSI3CBOtYDbU
+FVa3VP9+0qCFrIzVLJcdlWGpQDeuEcYjcSzqhj3GdmMhviDFBE6iHfk0JwIBIwKB
+gQCgwkqmASifpUv5CGuBoTetRg0zNuuobJZV108Is+7YnFdI4Koi+JhaO/xN2diE
+bXh/afrcBj3KtBG4GEMTbVAB6+3L3/KKVQ32ryuIwvOS8dhDGIqwK26ZgNqYLwOI
+WRXo1zvyGpVLt90xQrpfxhdth4645ggvgrVMRGxccdoTCwJBAPmJ74/o+op/VYJ9
+eqBcoAy8Hsta7N99H9JIVa0tjRkS/58l9DKhSEIL2EcdXk4yyWAaKFqWg+DU93cs
+px/7EYkCQQDwgqh6xDJxcpuff9xgd7YFUYIREaZL2Ez8Iws2OHxm5Iyo5GhRUVKS
+CUNJWPl1z52EBWLSqc1ZH2fh/WExxhwvAkEA8mi81PDzYfgJ7Hnk1ktZpfiS8W5F
+MN/zBsns1B2fAmo6JZKVcwMS/lSmNnRM+4HK76RhtxXfMi33tZkXYOVEOwJBAKTr
+4T49ZGsMwnv/55KbOvxVJf0TareqQ2sQtzsfaz89okftFFUEkGQjm9qGJ2a6QCAD
+sXqDEHeggb+JLLRqlvsCQQDZSk/2ZjPyp7t3J1e1dDW82Fxs9XhipCzOMRi7KLWF
+K8IltIGrmgMfkn3bDDlKAvoHIH+hrmlghEx2MEuDG6nY
+-----END RSA PRIVATE KEY-----</value>
+               <comment>The SSH host RSA private key, with restricted access rights.</comment>
+       </key>
+
+
+
+
+
+       <key basename="ssh_host_rsa_key.pub" mode="0644">
+               <value>ssh-rsa AAAAhtrhgrdsfg5gfdfgGREWGEEREFGrefgerrgerdbyHQsb3LDTQAVtAEkF59n97LERpo6p9PJNcv/eLkIlRt2xFnT6ZHqN1jPEx5vvLHi3Jv9qBLdyDAaZjA1Q+OFyKR0iNwgTrWA21BVWt1T/ftKghayM1SyXHZVhqUA3rhHGI3Es6oY9xnZjIb4gxQROoh35NCc=</value>
+               <comment>The SSH host RSA public key, world readable.</comment>
+       </key>
+
+
+
+
+</keyset>
diff --git a/doc/standards/sysinit.xml b/doc/standards/sysinit.xml
new file mode 100644 (file)
index 0000000..f6be053
--- /dev/null
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<!--
+        
+       Subversion stuff
+        
+       $Id$
+        
+-->
+
+<!--
+
+       This specification-by-example defines a key tree layout for system initialization
+       Its purpose is similar to the Unix well-known file /etc/inittab
+        
+       Avi Alkalay
+       <avi at unix.sh>
+       Nov 2005
+
+-->
+
+
+
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+        parent="system/init">
+
+
+       <!-- This ID is very important. It defines the default runlevel -->
+       <key parent="id">
+               <key basename="runlevels" value="5"/>
+               <key basename="action" value="initdefault"/>
+               <key basename="process"/>
+       </key>
+       
+       
+       <!-- This key defines the sequence to run INIT tasks -->
+       <key parent="sequence">
+               <comment>Defines the sequence ID entries will be run.</comment>
+               <comment>Since we migrated from the stream text file (/etc/inittab)</comment>
+               <comment>to a tree of key-value pairs that doesn't have a sequence at all,</comment>
+               <comment>we need a way to define the sequence the INIT tasks will be run.</comment>
+               <comment>We do that using the system/init/sequence key.</comment>
+               
+               <value>id,si,l0,l1,l2,l3,l4,l5,l6,ca,pf,pr,1,2,3,4,5,6,x</value>
+       </key>
+
+
+
+       <key parent="1">
+               <key basename="runlevels" value="2345"/>
+               <key basename="action" value="respawn"/>
+               <key basename="process" value="/sbin/mingetty tty1"/>
+       </key>
+
+
+       <key parent="2">
+               <key basename="runlevels" value="2345"/>
+               <key basename="action" value="respawn"/>
+               <key basename="process" value="/sbin/mingetty tty2"/>
+       </key>
+
+
+
+
+       <key parent="3">
+               <comment>Init ID name</comment>
+               
+               <key basename="runlevels" value="2345">
+                       <comment>Active runlevels to execute this ID</comment>
+               </key>
+               
+               <key basename="action" value="respawn">
+                       <comment>Approach</comment>
+               </key>
+               
+               <key basename="process" value="/sbin/mingetty tty2">
+                       <comment>Program to execute</comment>
+               </key>
+       </key>
+
+
+
+
+       <key parent="ca">
+               <key basename="runlevels"/>
+               <key basename="action" value="ctrlaltdel"/>
+               <key basename="process" value="/sbin/shutdown -t3 -r now"/>
+       </key>
+
+
+
+
+       <key parent="l3">
+               <key basename="runlevels" value="3"/>
+               <key basename="action" value="wait"/>
+               <key basename="process" value="/etc/rc.d/rc 3"/>
+       </key>
+
+
+
+       
+       <key parent="pf">
+               <key basename="runlevels"/>
+               <key basename="action" value="powerfail"/>
+               <key basename="process">
+                       <value>/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"</value>
+               </key>
+       </key>
+
+</keyset>
diff --git a/doc/standards/userdb.xml b/doc/standards/userdb.xml
new file mode 100644 (file)
index 0000000..fab7f49
--- /dev/null
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+        
+       Subversion stuff
+        
+       $Id$
+        
+        -->
+
+<!--
+        
+       This specification-by-example defines a key tree layout for users, groups and password.
+        
+       Its purpose is similar to the Unix well-known files:
+        
+        /etc/passwd
+        /etc/group
+        /etc/shadow
+        
+       It defines 2 trees, system/users and system/groups
+       All keys must be owned by root with RO permissions to others, exept for the
+       password keys which should be readable only to root.
+        
+       In this example we'll define the 'jdoe' user and 'root' keys.
+        
+       Avi Alkalay
+       <avi at unix.sh>
+       Nov 2005
+
+-->
+
+
+
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+        parent="system">
+
+
+
+       <key basename="users/jdoe">
+               <!-- The 'jdoe' record -->
+
+               <!-- Real name -->
+               <key basename="gecos" value="John Doe"/>
+
+               <!-- User and group IDs -->
+               <key basename="uid" value="500"/>
+               <key basename="gid" value="800"/>
+
+               <!-- Home directory and shell program -->
+               <key basename="home" value="/root"/>
+               <key basename="shell" value="/bin/bash"/>
+
+               <!-- Password metainfo as /etc/shadow.
+                         Check the 'mode' attribute, meaning they are secure keys -->
+               <key basename="passwdChangeAfter" mode="0600" value="99999"/>
+               <key basename="passwdChangeBefore" mode="0600" value="0"/>
+               <key basename="passwdDisableAfter" mode="0600"/>
+               <key basename="passwdDisabledSince" mode="0600"/>
+               <key basename="passwdReserved" mode="0600"/>
+               <key basename="passwdWarnBefore" mode="0600" value="7"/>
+
+               <!-- Obsolete key.... from /etc/passwd -->
+               <key basename="password" mode="0644" value="x"/>
+               
+               <!-- Real password goes here in encrypted form -->
+               <key basename="shadowPassword" mode="0600" value="an encrypted passwd should appear here"/>
+       </key>
+
+       
+       <!-- The group which jdoe is member of -->
+       <key basename="groups/guests">
+               <key basename="gid" value="800"/>
+               <key basename="members" value="jdoe,miriam,ana"/>
+       </key>
+       
+
+
+
+
+
+
+       
+
+       <!-- The 'root' record -->
+       
+       <key basename="users/root">
+               <key basename="gecos" value="root"/>
+               <key basename="uid" value="0"/>
+               <key basename="gid" value="0"/>
+               <key basename="home" value="/root"/>
+               <key basename="shell" value="/bin/bash"/>
+               <key basename="passwdChangeAfter" mode="0600" value="99999"/>
+               <key basename="passwdChangeBefore" mode="0600" value="0"/>
+               <key basename="passwdDisableAfter" mode="0600"/>
+               <key basename="passwdDisabledSince" mode="0600"/>
+               <key basename="passwdReserved" mode="0600"/>
+               <key basename="passwdWarnBefore" mode="0600" value="7"/>
+               <key basename="password" mode="0644" value="x"/>
+               <key basename="shadowPassword" mode="0600" value="an encrypted passwd should appear here"/>
+       </key>
+
+
+       <key basename="groups/root">
+               <key basename="gid" value="0"/>
+               <key basename="members" value="root"/>
+       </key>
+
+
+
+       <!-- Just another group that has root as a member -->
+       <key basename="groups/sys">
+               <key basename="gid" value="3"/>
+               <key basename="members" value="root,bin,adm"/>
+       </key>
+       
+
+</keyset>
diff --git a/doc/storage.xml b/doc/storage.xml
new file mode 100644 (file)
index 0000000..7dc899a
--- /dev/null
@@ -0,0 +1,70 @@
+
+<!--
+$Id$
+$LastChangedBy$
+-->
+       <note><para>This section is provided for the sake of the openness of Elektra. You should not access the Elektra's key files directly. You should use the API or the <citerefentry><refentrytitle>kdb</refentrytitle><manvolnum>1</manvolnum></citerefentry> command for that.</para></note>
+       <section id="keystor"><title>Elektra Key Storage Strategy</title>
+               <para>Elektra implements a very simple way to store the key-value pairs. The value (and some metainfo) for each key is stored in a single file. The key name (and some of its context) is sufficient to find the file name that stores the value.</para>
+               <para>The <filename>system/*</filename> keys are stored under <filename>/etc/kdb/</filename>, and the <filename>user/*</filename> keys can be found under each user's <filename>$HOME/.kdb/</filename>.</para>
+               <para>Here are some examples of key names, and where Elektra goes to look for them in the disk.</para>
+               <variablelist>
+                       <varlistentry><term><filename>system/sw/XFree86/screen0/driver</filename></term>
+                               <listitem><para>Maps to: <filename>/etc/kdb/system/sw/XFree86/screen0/driver</filename></para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><filename>user/env/PATH</filename></term>
+                               <listitem><para>Maps to: <filename>~$USER/.kdb/user/env/PATH</filename></para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><filename>user:luciana/env/PATH</filename></term >
+                               <listitem><para>Maps to: <filename>~luciana/.kdb/user/env/PATH</filename></para></listitem>
+                       </varlistentry>
+                       <varlistentry><term><filename>system/mime.types/some.mime</filename></term>
+                               <listitem><para>Maps to: <filename>/etc/kdb/system/mime.types/some.mime</filename></para></listitem>
+                       </varlistentry>
+               </variablelist>
+               <para>Some may think that one file per key will consume many filesystem i-nodes. Actually, when not using Reiser4 filesystem, it may consume some more disk space, and it may also be not so efficient than reading one single text file, as KConfig does. But Elektra's nature lets applications load their keys on demand; so it is possible to avoid the read-all-use-some approach. Writing updated keys back to disk is also more robust, because unchanged keys won't be touched, different from a single file approach, that must be entirelly rewritten.</para>
+               <para>Besides that, big applications (like Mozilla, Konqueror, KDE, Gnome) key gathering time is a very small part of what they have to do to start up. And the benefits of an homogeneous key database to the entire system are much bigger then these issues. Think about a common integration between everything, flexibility, security granularity and openness.</para>
+       </section>
+
+       <section id="noxml"><title>XML, Storage Backends and Elektra</title>
+               <para>This document you are just reading was written in DocBook XML. XML is a wonderfull technology, but brings no value to this software. Two main goals of the Elektra Project are to be lightweight, and to be accessible by early boot stage programs like <command>/sbin/init</command> and the <filename class="directory">/etc/rc.d</filename> scripts.</para>
+               <para>XML parsing libraries are memory eaters, not so fast as we can expect, and they are usualy located under <filename class="directory">/usr/lib</filename>, which may be unavailable to these early boot stage needs.</para>
+               <para>Some discussions asked for a sort of plugin architecture to let user decide the format to store keys content. Well, the info that goes into the key file is not big deal as you'll see, and sometimes, too many options is bad business, and not the path for the Elektra Project.</para>
+               <para>So no XML, no plugin architecture, no sophistication. Lets keep it simple and generic. A very straight forward text based file format was defined to store a single key value.</para>
+       </section>
+                       
+       <section id="keyinternal"><title>Key Files Format</title>
+               <para>Inside Elektra key database, each key is a file, and every file is a key. So most of the key's metainformation are actually its file attributes, as you can see in a regular <citerefentry><refentrytitle>ls</refentrytitle><manvolnum>1</manvolnum></citerefentry> command output.</para>
+               <para>So what needs to be stored inside the key file is the data type (binary or text), key comment and the actual data. The format of each key file is:</para>
+               <screen>File Format Version
+Data Type
+As many lines of
+comments as we want (UTF-8 encoded)
+&#060;DATA&#062;
+The data encoded as text
+               </screen>
+               <para>So if we set the key <filename>system/hw/eth0/driver</filename> as type <type>String</type> and value "<replaceable>3com</replaceable>", and comment "<replaceable>The driver for my network interface</replaceable>", we'll find the file <filename>/etc/kdb/system/hw/eth0/driver</filename> containing:</para>
+               <screen>RG002
+40
+The driver for my network interface
+&#060;DATA&#062;
+3com
+               </screen>
+               <para>Other example: setting <filename>user/tmp/proprietary</filename> as <type>Binary</type>, and value "<replaceable>A groovy data I want to hide</replaceable>", and comment "<replaceable>Stay away from here</replaceable>", you'll get in <filename>~$USER/.kdb/user/tmp/proprietary</filename> the following:</para>
+               <screen>RG002
+20
+Stay away from here
+&#060;DATA&#062;
+41206772 6f6f7679 20646174 61204920 77616e74 20746f20 68696465
+               </screen>
+               <para>The current data types are:</para>
+               <variablelist>
+                       <varlistentry><term>Between 20 and 39</term>
+                               <listitem><simpara>Binary value. The data will be encoded into a text format. Today only type 20 is used, and means a raw stream of bytes with no special semantics to Elektra. The other values are reserved for future use; being treated still as binary values but possibly with some semantics to Elektra or a higher level application.</simpara></listitem>
+                       </varlistentry>
+                       <varlistentry><term>40 up to 254</term>
+                               <listitem><simpara>Text, UTF-8 encoded. Values higher then 40 are reserved for future or application specific implementations of more abstract data types, like time/date, color, font, etc. But always represented as pure text that can be edited in any text editor like <citerefentry><refentrytitle>vi</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</simpara></listitem>
+                       </varlistentry>
+               </variablelist>
+               <para>Types between 0 and 19 are used only internaly in the API, and will never appear into a key file. They are used to define meta keys as directory, link, etc.</para>
+       </section>
diff --git a/elektra.mandriva.spec b/elektra.mandriva.spec
new file mode 100644 (file)
index 0000000..99b45c6
--- /dev/null
@@ -0,0 +1,189 @@
+%define name    elektra 
+%define version 0.6.3
+%define release %mkrel 1 
+
+Name:          %{name}
+Version:       %{version}
+Release:       %{release}
+Source:        http://aleron.dl.sourceforge.net/sourceforge/elektra/%{name}-%{version}.tar.bz2
+Group:         System Environment/Libraries
+License:       BSD
+URL:           http://elektra.sourceforge.net
+BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+BuildRequires: doxygen docbook-style-xsl db4-devel libGConf1-devel libxml2-devel libtool libxslt-proc
+Summary:       A key/value pair database to store software configurations
+
+%define DTDVERSION 0.1.1
+
+%description
+The Elektra Project provides a framework to store generic configuration data
+in an hierarchical key-value pair database, instead of a human-readable only
+text file.
+
+This way any software can read/save his configuration using a consistent API.
+Also, applications can be aware of other applications configurations,
+leveraging easy application integration.
+
+
+%package devel
+Summary:      Include files and API documentation for Elektra Project
+Group:        Development/System
+Requires:     elektra = %{version}-%{release}
+
+%description devel
+The Elektra Project provides a framework to store generic configuration data
+in an hierarchical key-value pair database, instead of a human-readable only
+text file.
+
+This way any software can read/save his configuration using a consistent API.
+Also, applications can be aware of other applications configurations,
+leveraging easy application integration.
+
+This package contains the include files and API manual pages to use the Elektra
+API in C.
+
+It also provides the framework to create storage backends to libelektra.so
+
+
+%package backend-gconf
+Summary:      A GConf backend for Elektra
+Group:        System Environment/Libraries
+Requires:     elektra
+
+%description backend-gconf
+The Elektra Project provides a framework to store generic configuration data
+in an hierarchical key-value pair database, instead of a human-readable only
+text file.
+
+This way any software can read/save his configuration using a consistent API.
+Also, applications can be aware of other applications configurations,
+leveraging easy application integration.
+
+This package contains a GConf backend for Elektra, to let Elektra use a GConf
+daemon to store its keys.
+
+%package backend-berkeleydb
+Summary:      Include files and API documentation for Elektra Project
+Group:        System Environment/Libraries
+Requires:     elektra
+
+
+%description backend-berkeleydb
+The Elektra Project provides a framework to store generic configuration data
+in an hierarchical key-value pair database, instead of a human-readable only
+text file.
+
+This way any software can read/save his configuration using a consistent API.
+Also, applications can be aware of other applications configurations,
+leveraging easy application integration.
+
+This package contains a Berkeley DB backend for Elektra, to let Elektra use
+Berkeley DB databases to store its keys.
+
+%debug_package
+
+%prep
+%setup
+
+%build
+%configure \
+     --bindir=/bin \
+     --sbindir=/sbin \
+     --libdir=/%{_lib} \
+     --with-docdir=%{_defaultdocdir}/elektra-%{version} \
+     --with-develdocdir=%{_defaultdocdir}/elektra-devel-%{version} \
+     --disable-xmltest \
+     --with-docbook=%{_datadir}/sgml/docbook/xsl-stylesheets
+%make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR=$RPM_BUILD_ROOT install
+# Move .a files to -devel package
+mv $RPM_BUILD_ROOT/%{_lib}/libelektra.a $RPM_BUILD_ROOT/%{_libdir}
+
+# Prepare devel files
+rm $RPM_BUILD_ROOT/%{_lib}/libelektra.so
+ln -sf ../../%{_lib}/libelektra.so.2 $RPM_BUILD_ROOT/%{_libdir}/libelektra.so
+
+# Remove old .la files
+#rm $RPM_BUILD_ROOT/usr/lib/libelektra-*.a
+rm $RPM_BUILD_ROOT/%{_lib}/*.la
+rm $RPM_BUILD_ROOT/%{_lib}/elektra/*.la
+rm $RPM_BUILD_ROOT/%{_libdir}/*.la
+
+# Remove a file that conflicts with other packages
+rm $RPM_BUILD_ROOT/%{_mandir}/man3/key.3*
+
+# Remove documentation from 'make install', to let RPM package it allone
+rm -rf $RPM_BUILD_ROOT/%{_defaultdocdir}
+rm -rf scripts/Makefile*
+rm -rf examples/Makefile*
+rm -rf examples/.deps
+rm -rf doc/standards/Makefile*
+mv doc/elektra-api/html doc/elektra-api/api-html
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/ldconfig
+# Backwards compatibility, from the Linux Registry days
+# if [ -d /etc/registry -a ! -d /etc/kdb ]; then
+#      mv /etc/registry /etc/kdb
+#      ln -s kdb /etc/registry
+# fi
+
+# Create basic key structure for apps
+kdb set -t dir system/sw
+kdb set system/sw/kdb/current/schemapath "%{_datadir}/sgml/elektra-0.1.1/elektra.xsd"
+
+
+%postun -p /sbin/ldconfig
+
+
+
+%files
+%defattr(-,root,root,-)
+/bin/*
+/sbin/*
+/%{_lib}/*elektra.so*
+%dir /%{_lib}/elektra
+/%{_lib}/elektra/*elektra-daemon.so*
+/%{_lib}/elektra/*elektra-filesys.so*
+/%{_lib}/elektra/*elektra-default.so*
+/%{_lib}/elektra/*elektra-ddefault.so*
+/%{_lib}/elektra/*elektra-fstab.so*
+#/%{_lib}/elektra/*elektra-ini.so*
+%{_libdir}/*elektratools.so.*
+%{_libdir}/elektra/*elektratools.so
+%{_sysconfdir}/profile.d/*
+%doc AUTHORS COPYING ChangeLog README INSTALL
+%doc scripts doc/standards
+%{_mandir}/man1/*
+%{_mandir}/man7/*
+%{_mandir}/man5/*
+%{_datadir}/sgml/*
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/*
+%{_libdir}/*.a
+%{_libdir}/libelektra.so
+%{_libdir}/libelektratools.so
+%{_libdir}/pkgconfig/*
+%doc examples doc/elektra-api/api-html
+%{_mandir}/man3/*
+
+# %files backend-gconf
+# %defattr(-,root,root,0755)
+# %{_prefix}/%{_libdir}/*gconf.so*
+
+%files backend-berkeleydb
+%defattr(-,root,root,0755)
+/%{_lib}/elektra/*berkeleydb.so*
+
+
+%changelog
+* Sat Aug 6 2006 Yannick Lecaillez <sizon5@gmail.com> 0.6.3
+- Adapted to Mandriva from the spec for Fedora made by Avi Alkalay
diff --git a/elektra.pc.in b/elektra.pc.in
new file mode 100644 (file)
index 0000000..4469486
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: A key/value pair database to store software configurations
+Version: @VERSION@
+Libs: -L@libdir@ -lelektra-static @LIBICONV@
+Libs.private: -lelektra-static @privatelibs@
+Cflags: -I@includedir@
+
diff --git a/elektra.spec.in b/elektra.spec.in
new file mode 100644 (file)
index 0000000..5561a31
--- /dev/null
@@ -0,0 +1,238 @@
+%define tempdocdir %{_prefix}/elektra
+
+Name:          elektra
+Version:       @VERSION@
+Release:       9
+Source:        http://prdownloads.sourceforge.net/elektra/%{name}-%{version}.tar.gz
+Group:         System Environment/Libraries
+License:       BSD
+URL:           http://www.libelektra.org
+# Requires:      libxml2
+BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: doxygen db4-devel libxml2-devel docbook-style-xsl libxslt
+
+# Requires section:
+# libxml2: used only by libkdbtools, to import keys from XML
+#
+# BuildRequires section:
+# doxygen: to build the API documentation
+# libxslt, docbook-style-xsl: to build man pages from docbook with xsltproc program
+# db4-devel: for the Berkeley DB backend
+# libxml2-devel: for the kdbtools library build
+# gettext-devel: for the autoconf ICONV macro only
+# automake, autoconf: for the build system
+# libtool: not sure
+# GConf2-devel: for the testing GConf2 backend for Elektra
+
+
+Summary:       A key/value pair database to store software configurations
+
+
+
+%description
+Elektra provides a universal and secure framework to store configuration
+parameters in a hierarchical key-value pair mechanism, instead of each
+program using its own text configuration files. This allows any program
+to read and save its configuration with a consistent API, and allows
+them to be aware of other applications' configurations, permitting
+easy application integration. While architecturally similar to other OS
+registries, Elektra does not have most of the problems found in those
+implementations.
+
+%package devel
+Summary:      Include files and API documentation to build elektrified programs
+Group:        Development/System
+Requires:     pkgconfig elektra = %{version}-%{release}
+
+%description devel
+Elektra provides a universal and secure framework to store configuration
+parameters in a hierarchical key-value pair tree.
+
+This package contains development specific things as include files and
+static libraries to create elektrified programs.
+
+
+#%package backend-gconf
+#Summary:      A GConf backend for Elektra
+#Group:        System Environment/Libraries
+#Requires:     elektra
+#add this in the main RPM description: BuildRequires: GConf2-devel
+
+
+#%description backend-gconf
+#Elektra provides a universal and secure framework to store configuration
+#parameters in a hierarchical key-value pair mechanism, instead of each
+#program using its own text configuration files. This allows any program
+#to read and save its configuration with a consistent API, and allows
+#them to be aware of other applications' configurations, permitting
+#easy application integration. While architecturally similar to other OS
+#registries, Elektra does not have most of the problems found in those
+#implementations.
+
+#This package contains a GConf backend for Elektra, to let Elektra use a GConf
+#daemon to store its keys.
+
+
+%package backend-berkeleydb
+Summary:      Elektra backend that stores key/value pairs in berkeley DB databases
+Group:        System Environment/Libraries
+Requires:     elektra
+
+
+%description backend-berkeleydb
+Elektra provides a universal and secure framework to store configuration
+parameters in a hierarchical key-value pair tree.
+
+This package contains a Berkeley DB backend for Elektra, to let Elektra use
+Berkeley DB databases to store its keys.
+
+# Uncomment and join the '%' char to 'debug_package' if you want to build debug RPMs
+# % debug_package
+
+%prep
+%setup -q
+%build
+
+#CFLAGS="-g -O0" \
+
+%configure \
+     --bindir=/bin \
+     --sbindir=/sbin \
+     --libdir=/%{_lib} \
+     --with-docdir=%{tempdocdir}/elektra-%{version} \
+     --with-develdocdir=%{tempdocdir}/elektra-devel-%{version} \
+     --with-docbook=%{_datadir}/sgml/docbook/xsl-stylesheets
+
+#     --prefix=%{_prefix} \
+#     --exec-prefix=/ \
+
+
+# Add these options if using a plain ./configure instead of RPM's % configure macro
+# --sysconfdir=%{_sysconfdir} \
+# --mandir=%{_mandir}
+
+make %{?_smp_mflags} all
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+make DESTDIR=$RPM_BUILD_ROOT install
+
+# Remove statically linked kdb
+rm $RPM_BUILD_ROOT/bin/kdb_static
+
+# Move .a files to -devel package
+mv $RPM_BUILD_ROOT/%{_lib}/libelektra.a $RPM_BUILD_ROOT%{_libdir}
+
+# Prepare devel files
+#mv $RPM_BUILD_ROOT/%{_lib}/libelektra.so $RPM_BUILD_ROOT%{_libdir}/
+rm $RPM_BUILD_ROOT/%{_lib}/libelektra.so
+ln -sf ../../%{_lib}/libelektra.so.2 $RPM_BUILD_ROOT%{_libdir}/libelektra.so
+#rm $RPM_BUILD_ROOT/%{_lib}/libelektra.so
+
+# Remove .la files
+#rm $RPM_BUILD_ROOT/usr/lib/libelektra-*.a
+rm $RPM_BUILD_ROOT/%{_lib}/*.la
+rm $RPM_BUILD_ROOT/%{_lib}/elektra/*.la
+rm $RPM_BUILD_ROOT%{_libdir}/*.la
+
+# Remove a file that conflicts with other packages
+rm $RPM_BUILD_ROOT%{_mandir}/man3/key.3*
+
+# Remove documentation from 'make install', to let RPM package it alone
+rm -rf $RPM_BUILD_ROOT%{tempdocdir}
+rm -rf scripts/Makefile*
+rm -rf examples/Makefile*
+rm -rf examples/.deps
+rm -rf doc/standards/Makefile*
+mv doc/elektra-api/html doc/elektra-api/api-html
+
+
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+#rm -rf $RPM_BUILD_DIR/%{name}
+
+
+
+
+%post
+/sbin/ldconfig
+# Backwards compatibility, from the Linux Registry days
+#if [ -d /etc/registry -a ! -d /etc/kdb ]; then
+#   $MOVE /etc/registry /etc/kdb
+#   $LINK -s kdb /etc/registry
+#fi
+
+# Make the daemon startable
+# chkconfig --add kdbd
+
+# Create basic key structure for apps
+kdb set -t dir system/sw
+kdb set system/sw/kdb/schemapath "%{_datadir}/sgml/elektra-0.1.1/elektra.xsd"
+
+
+%preun
+# Remove the daemon from init tasks
+# chkconfig --del kdbd
+
+
+%postun -p /sbin/ldconfig
+
+
+
+%files
+%defattr(-,root,root,-)
+/bin/*
+#%attr(4755,root,root) /sbin/kdbd
+/sbin/kdbd
+/%{_lib}/*elektra.so*
+%dir /%{_lib}/elektra
+/%{_lib}/elektra/*elektra-daemon.so*
+/%{_lib}/elektra/*elektra-filesys.so*
+/%{_lib}/elektra/*elektra-default.so*
+/%{_lib}/elektra/*elektra-ddefault.so*
+/%{_lib}/elektra/*elektra-fstab.so*
+#/%{_lib}/elektra/*elektra-ini.so*
+%{_libdir}/*elektratools.so.*
+%{_libdir}/elektra/*elektratools.so
+%{_sysconfdir}/profile.d/*
+%{_sysconfdir}/init.d/*
+%doc AUTHORS COPYING ChangeLog README INSTALL
+%doc scripts doc/standards
+%{_mandir}/man1/*
+%{_mandir}/man7/*
+%{_mandir}/man5/*
+%{_datadir}/sgml/*
+
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/*
+%{_libdir}/*.a
+%{_libdir}/libelektra.so
+%{_libdir}/libelektratools.so
+%{_libdir}/pkgconfig/*
+%doc examples doc/elektra-api/api-html
+%{_mandir}/man3/*
+
+
+
+#%files backend-gconf
+#%defattr(-,root,root,-)
+#%{_libdir}/elektra/*gconf.so*
+
+%files backend-berkeleydb
+%defattr(-,root,root,-)
+/%{_lib}/elektra/*berkeleydb.so*
+
+
+
+%changelog
+* Thu Sep 07 2006 Avi Alkalay <avi@unix.sh> 0.6.4-1
+- Version update
+* Thu Jun 02 2006 Avi Alkalay <avi@unix.sh> 0.6.2-1
+- Initial packaging
+
diff --git a/elektra.xml b/elektra.xml
new file mode 100644 (file)
index 0000000..87ccdb2
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE project-listing SYSTEM "http://freshmeat.net/backend/fm-projects-0.4.dtd">
+<project-listing>
+  <project>
+    <project_id>45092</project_id>
+    <date_added>2004-02-25 12:43:19</date_added>
+    <date_updated>2005-10-19 04:42:14</date_updated>
+    <projectname_short>elektra</projectname_short>
+    <projectname_full>Elektra Project</projectname_full>
+    <desc_short>A key-value pair framework for storing configurations.</desc_short>
+    <desc_full>Elektra provides a universal and secure framework\r
+to store configuration parameters in a\r
+hierarchical key-value pair mechanism, instead of\r
+each program using its own text configuration\r
+files. This allows any program to read and save\r
+its configuration with a consistent API, and\r
+allows them to be aware of other applications'\r
+configurations, permitting easy application\r
+integration. While architecturally similar to\r
+other OS registries, Elektra does not have most of\r
+the problems found in those implementations.\r
+</desc_full>
+    <vitality_score>733.70</vitality_score>
+    <vitality_percent>0.17</vitality_percent>
+    <vitality_rank>868</vitality_rank>
+    <popularity_score>1782.95</popularity_score>
+    <popularity_percent>3.23</popularity_percent>
+    <popularity_rank>1132</popularity_rank>
+    <rating>6.94</rating>
+    <rating_count>13</rating_count>
+    <rating_rank>322</rating_rank>
+    <subscriptions>85</subscriptions>
+    <branch_name>Default</branch_name>
+    <url_project_page>http://freshmeat.net/projects/elektra/</url_project_page>
+    <url_homepage>http://freshmeat.net/redir/elektra/48188/url_homepage/</url_homepage>
+    <url_tgz>http://freshmeat.net/redir/elektra/48188/url_tgz/</url_tgz>
+    <url_bz2></url_bz2>
+    <url_zip></url_zip>
+    <url_changelog></url_changelog>
+    <url_rpm></url_rpm>
+    <url_deb></url_deb>
+    <url_osx></url_osx>
+    <url_bittorrent></url_bittorrent>
+    <url_bsdport></url_bsdport>
+    <url_autopackage></url_autopackage>
+    <url_jar></url_jar>
+    <url_purchase></url_purchase>
+    <url_cvs>http://freshmeat.net/redir/elektra/48188/url_cvs/</url_cvs>
+    <url_bugtracker>http://freshmeat.net/redir/elektra/48188/url_bugtracker/</url_bugtracker>
+    <url_list>http://freshmeat.net/redir/elektra/48188/url_list/</url_list>
+    <url_mirror></url_mirror>
+    <url_demo></url_demo>
+    <license>BSD License (revised)</license>
+    <latest_release>
+      <latest_release_version>0.5.1</latest_release_version>
+      <latest_release_id>209675</latest_release_id>
+      <latest_release_date>2005-10-19 04:42:13</latest_release_date>
+    </latest_release>
+    <screenshot_thumb>http://download.freshmeat.net/screenshots/45092_thumb.png</screenshot_thumb>
+    <authors>
+      <author>
+        <author_name>Avi Alkalay</author_name>
+        <author_url>http://freshmeat.net/~aviram/</author_url>
+        <author_role>Owner</author_role>
+      </author>
+    </authors>
+    <descriminators>
+      <trove_id>15</trove_id>
+      <trove_id>45</trove_id>
+      <trove_id>810</trove_id>
+      <trove_id>147</trove_id>
+      <trove_id>253</trove_id>
+      <trove_id>4</trove_id>
+      <trove_id>233</trove_id>
+      <trove_id>3</trove_id>
+      <trove_id>200</trove_id>
+      <trove_id>201</trove_id>
+      <trove_id>164</trove_id>
+      <trove_id>1023</trove_id>
+      <trove_id>11</trove_id>
+      <trove_id>178</trove_id>
+      <trove_id>835</trove_id>
+      <trove_id>185</trove_id>
+      <trove_id>1089</trove_id>
+      <trove_id>165</trove_id>
+      <trove_id>912</trove_id>
+      <trove_id>955</trove_id>
+      <trove_id>809</trove_id>
+      <trove_id>136</trove_id>
+      <trove_id>188</trove_id>
+    </descriminators>
+    <dependencies>
+    </dependencies>
+  </project>
+</project-listing>
diff --git a/elektracpp.pc.in b/elektracpp.pc.in
new file mode 100644 (file)
index 0000000..c701053
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@ulibdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: A cpp binding for elektra.
+Version: @VERSION@
+Requires: elektra
+Libs: -L@ulibdir@ -lelektra-cpp
+Cflags: -I@includedir@
+
diff --git a/elektratools.pc.in b/elektratools.pc.in
new file mode 100644 (file)
index 0000000..c3e13c3
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@ulibdir@
+includedir=@includedir@
+
+Name: @PACKAGE@
+Description: High level elektra functions (XML import/export)
+Version: @VERSION@
+Requires: elektra
+Libs: -L@ulibdir@ -lelektratools
+Cflags: -I@includedir@
+
diff --git a/examples/.deps/application.Po b/examples/.deps/application.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/binary.Po b/examples/.deps/binary.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/cascading.Po b/examples/.deps/cascading.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/functional.Po b/examples/.deps/functional.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/getset.Po b/examples/.deps/getset.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/hello.Po b/examples/.deps/hello.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/hellokdb.Po b/examples/.deps/hellokdb.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/iterate.Po b/examples/.deps/iterate.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/keyNewExample.Po b/examples/.deps/keyNewExample.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/keyset.Po b/examples/.deps/keyset.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/lookup.Po b/examples/.deps/lookup.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/reference.Po b/examples/.deps/reference.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/small.Po b/examples/.deps/small.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/.deps/sort.Po b/examples/.deps/sort.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644 (file)
index 0000000..0708c43
--- /dev/null
@@ -0,0 +1,54 @@
+# $Id$
+
+AM_CFLAGS = $(CSTDFLAGS) $(COPTFLAGS) $(CDBGFLAGS)
+INCLUDES = -I$(top_srcdir)/src/include
+
+EXTRA_DIST = small.c application.c binary.c keyNewExample.c lookup.c hellokdb.c hierarchy.xml keyset.xml iterate.c reference.c sort.c getset.c
+
+# # Examples will only compile with make check
+check_PROGRAMS = small application binary keyNewExample lookup cascading keyset hello functional hellokdb iterate reference sort getset
+
+application_SOURCES = application.c
+application_LDADD = ../src/libelektra/libelektra.la
+
+binary_SOURCES = binary.c
+binary_LDADD = ../src/libelektra/libelektra.la
+
+keyNewExample_SOURCES = keyNewExample.c
+keyNewExample_LDADD = ../src/libelektra/libelektra.la
+
+lookup_SOURCES = lookup.c
+lookup_LDADD = ../src/libelektra/libelektra.la
+
+cascading_SOURCES = cascading.c
+cascading_LDADD = ../src/libelektra/libelektra.la
+
+keyset_SOURCES = keyset.c
+keyset_LDADD = ../src/libelektra/libelektra.la
+
+hello_SOURCES = hello.c
+hello_LDADD = ../src/libelektra/libelektra.la
+
+functional_SOURCES = functional.c
+functional_LDADD = ../src/libelektra/libelektra.la
+
+small_SOURCES = small.c
+small_LDADD = ../src/libelektra/libelektra.la
+
+hellokdb_SOURCES = hellokdb.c
+hellokdb_LDADD = ../src/libelektra/libelektra.la
+
+iterate_SOURCES = iterate.c
+iterate_LDADD = ../src/libelektra/libelektra.la
+
+reference_SOURCES = reference.c
+reference_LDADD = ../src/libelektra/libelektra.la
+
+sort_SOURCES = sort.c
+sort_LDADD = ../src/libelektra/libelektra.la
+
+getset_SOURCES = getset.c
+getset_LDADD = ../src/libelektra/libelektra.la
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
diff --git a/examples/application.c b/examples/application.c
new file mode 100644 (file)
index 0000000..4f0aef0
--- /dev/null
@@ -0,0 +1,203 @@
+/*********************************************************
+
+To compile this example:
+
+       $ cc `pkg-config --libs elektra` `pkg-config --cflags elektra` -o application application.c
+               or static
+       $ cc -c `pkg-config --cflags elektra` application.c
+
+This application shows how to read (resp. update) and save
+configuration using elektra.
+
+**********************************************************/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <kdb.h>
+
+#define MY_APP_ROOT   "/sw/MyApp/current"
+
+int generateConfig(KeySet *myConfig)
+{
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir1",
+                       KEY_VALUE, "directory value",
+                       KEY_COMMENT, "this is the first directory",
+                       KEY_DIR, KEY_END));
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir1/key1",
+                       KEY_VALUE, "the first application key",
+                       KEY_COMMENT, "some useful comment",
+                       KEY_DIR, KEY_END));
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir1/key2",
+                       KEY_VALUE, "the second application key",
+                       KEY_COMMENT, "some useful comment",
+                       KEY_DIR, KEY_END));
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir1/key3",
+                       KEY_VALUE, "the third application key",
+                       KEY_COMMENT, "some useful comment",
+                       KEY_DIR, KEY_END));
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir2",
+                       KEY_VALUE, "directory value",
+                       KEY_DIR, KEY_END));
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir3",
+                       KEY_VALUE, "directory value",
+                       KEY_DIR, KEY_END));
+       ksAppendKey(myConfig, keyNew("user/sw/MyApp/current/dir4",
+                       KEY_VALUE, "directory value",
+                       KEY_DIR, KEY_END));
+}
+
+
+/* Read config keys for this application */
+int readConfig(KDB * handle, KeySet *myConfig)
+{
+       int rc = 0;
+       int size = 0;
+
+       /* Get all value keys for this application */
+       if (kdbGetByName(handle, myConfig, "system" MY_APP_ROOT, 0) == -1)
+       {
+               rc = -1;
+               perror("Couldn't get system configuration. Reason");
+       } else {
+               size = (int)ksGetSize(myConfig);
+               printf("Retrieved %d keys\n", size);
+       }
+
+       if (kdbGetByName(handle, myConfig,"user" MY_APP_ROOT,  0) == -1)
+       {
+               rc = -1;
+               perror("Couldn't get user configuration. Reason");
+       } else {
+               size = (int)ksGetSize(myConfig);
+               printf("Retrieved %d keys\n", size);
+       }
+
+       if (rc == -1) return -1;
+       return size;
+}
+
+
+/* Change some keys */
+void changeConfig(KeySet *myConfig)
+{
+       Key *current;
+       
+       ksRewind(myConfig);
+       while ((current=ksNext(myConfig))) {
+               char keyName[MAX_KEY_LENGTH];
+               char value[MAX_KEY_LENGTH];
+               
+               keyGetFullName(current,keyName,sizeof(keyName));
+               keyGetString(current,value,sizeof(value));
+               
+               printf("Key %s was %s. ", keyName, value);
+               
+               /* Add "- modified" to the end of the string */
+               strcat(value,"- modified");
+               
+               /* change the key value */
+               keySetString(current,value);
+               
+               /* reget it, just as an example */
+               keyGetString(current,value,sizeof(value));
+               
+               printf("Now is %s\n", value);
+       }
+}
+
+/* Make keys empty */
+void emptyConfig(KeySet *myConfig)
+{
+       ksRewind(myConfig);
+       while (ksNext(myConfig))
+       {
+               keySetString(ksCurrent(myConfig), "");
+       }
+}
+
+
+/* Make keys empty */
+void printConfig(KeySet *myConfig)
+{
+       ksRewind(myConfig);
+       while (ksNext(myConfig))
+       {
+               printf ("Key %s, Value %s, Comment %s\n",
+                       (char*)keyName(ksCurrent(myConfig)),
+                       (char*)keyValue(ksCurrent(myConfig)),
+                       (char*)keyComment(ksCurrent(myConfig)));
+       }
+}
+
+
+
+/* Save the modified keys */
+int saveConfig(KDB * handle, KeySet *myConfig)
+{
+       return kdbSet(handle,myConfig,0,0);
+}
+
+
+int main(int argc, char **argv)
+{
+       int command;
+       KeySet *myConfig=ksNew(0);
+       KDB * handle=kdbOpen();
+
+       printf ("Following commands are available:\n\n");
+       printf ("g ... generate new Configuration\n");
+       printf ("c ... change Configuration\n");
+       printf ("r ... read Configuration\n");
+       printf ("s ... save Configuration\n");
+       printf ("p ... print Configuration\n");
+       printf ("e ... empty Configuration\n");
+       printf ("d ... clear Configuration\n");
+
+       while (1)
+       {
+               /* Get configuration values, and just continue if there is no error */
+
+               command = fgetc (stdin);
+               if (command == 'q') break;
+               switch (command)
+               {
+                       case 'g':
+                               generateConfig(myConfig);
+                               break;
+                       case 'c':
+                               changeConfig(myConfig);
+                               break;
+                       case 'r':
+                               readConfig(handle, myConfig);
+                               break;
+                       case 's':
+                               saveConfig(handle, myConfig);
+                               break;
+                       case 'p':
+                               printConfig(myConfig);
+                               break;
+                       case 'e':
+                               emptyConfig(myConfig);
+                               break;
+                       case 'd':
+                               ksClear(myConfig);
+                               break;
+                       case '\n': case '\r': case '\f':
+                               break;
+                       default:
+                               printf ("unkown command\n");
+                               break;
+               }
+       }
+
+       kdbClose(handle);
+
+       /* Free all keys and resources in the key set */
+       ksDel(myConfig);
+
+       return 0;
+}
+
diff --git a/examples/binary.c b/examples/binary.c
new file mode 100644 (file)
index 0000000..19193d2
--- /dev/null
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kdb.h>
+#include <stdio.h>
+
+/*
+ * Save this program as binary.c
+ * Compile and run it as bellow.
+ *
+ * Change the data type of 'i' from int to char, and to float,
+ * and to double, and recompile it.
+ *
+ * You should not use binary types.
+ * Consider a text representation of
+ * your types and write them as string.
+ *
+ * cc -L/lib -lkdb binary.c -o binary; ./binary; cat user/tmp/bin
+ *
+ */
+
+int main(void)
+{
+       int i=25;
+       Key *key=keyNew("user/examples/bin",KEY_END);
+       Key *back;
+       KDB * handle= kdbOpen();
+
+       keySetBinary(key,&i,sizeof(i));
+       kdbSetKey(handle,key);
+       keyDel(key);
+
+       back=keyNew("user/examples/bin", KEY_END);
+       kdbGetKey(handle,back);
+       printf("Got back: %d\n",*(int*)keyValue(back));
+       keyRemove(back);
+       kdbSetKey(handle,key);
+       keyDel(back);
+
+       kdbClose(handle);
+       return 0;
+}
diff --git a/examples/cascading.c b/examples/cascading.c
new file mode 100644 (file)
index 0000000..d037709
--- /dev/null
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <kdb.h>
+
+void BailOut (char * msg)
+{
+       perror (msg);
+}
+
+int main()
+{
+       KDB * h;
+       KeySet * myConfig = ksNew(0);
+       Key    * myKey;
+       char   * myValue;
+
+       /* Open the Key Database */
+       if ((h = kdbOpen()) != 0)
+               BailOut ("Could not open Key Database");
+       
+       /* Get myapps keyset */
+       if (kdbGetByName(h, myConfig, "/examples", 0 ) == -1)
+               BailOut ("Could not get Keys");
+
+       /* Find the key in the keyset */
+       if ((myKey = ksLookupByName (myConfig, "/examples/key", 0)) == NULL)
+               BailOut ("Could not Lookup Key");
+
+       /* Get the value of the key */
+       if ((myValue = (char*) keyValue (myKey)) == NULL)
+               BailOut ("Could not get Keyvalue");
+               
+       /* Actually print the key */
+       printf ("%s\n", myValue);
+       /* Close the Key database */
+       kdbClose(h);
+       ksDel(myConfig);
+       return 0;
+}
+
diff --git a/examples/functional.c b/examples/functional.c
new file mode 100644 (file)
index 0000000..c3006a3
--- /dev/null
@@ -0,0 +1,153 @@
+#include <kdb.h>
+
+#include <stdlib.h>
+
+/**A functional access to keys.
+ *
+ * Instead of writing your own loop you can write
+ * a function working with a key and pass it to
+ * this method.
+ *
+ * The function will be executed for all keys in
+ * the keyset.
+ *
+ * @param ks the keyset to work with
+ * @param func the function to execute on every key of the keyset
+ * @return the sum of all return values
+ * @return -1 if any function returned -1, the execution will stop there, but
+ *     ksCurrent() will tell you where it stopped.
+ * @see ksFilter()
+ */
+int ksForEach (KeySet *ks, int (*func) (Key *k))
+{
+       int rc = 0;
+       int ret = 0;
+       Key *current;
+
+       cursor_t cursor = ksGetCursor (ks);
+       ksRewind (ks);
+       while ((current = ksNext (ks)) != 0)
+       {
+               rc = func (current);
+               if (rc == -1) return -1;
+               ret += rc;
+       }
+       ksSetCursor(ks, cursor);
+       return ret;
+}
+
+
+/**Filter a keyset.
+ *
+ * filter is executed for every key in the keyset result. When it returns 0,
+ * the key will be dropped, when it returns 1 it will be ksAppendKey()ed to result,
+ * when it returns -1 the processing will be stopped. You can use ksCurrent()
+ * on input to see where the problem was. Because of this input is not const,
+ * apart from ksCurrent() the input will not be changed. The keys that have
+ * been in result before will stay untouched.
+ *
+ * @param result is the keyset where keys are added.
+ * @param input is the keyset the filter works on.
+ * @param filter is the function to execute on every key of the keyset to decide if
+ *     it should be ksAppendKey()ed to the result.
+ * @return the number of keys added on success
+ * @return 0 when nothing was done
+ * @return -1 when filter returned an error (-1), ksCurrent() of input will
+ *     be the problematic key.
+ * @see ksForEach()
+ **/
+int ksFilter (KeySet *result, KeySet *input, int (*filter) (Key *k))
+{
+       int rc = 0;
+       int ret = 0;
+       Key *current;
+
+       cursor_t cursor = ksGetCursor (input);
+       ksRewind (input);
+       while ((current = ksNext (input)) != 0)
+       {
+               rc = filter (current);
+               if (rc == -1) return -1;
+               else if (rc != 0)
+               {
+                       ++ ret;
+                       ksAppendKey(result, keyDup (current));
+               }
+       }
+       ksSetCursor(input, cursor);
+       return ret;
+}
+
+
+Key *global_a;
+
+int add_string (Key *check) { return keySetString (check, "string"); }
+int add_comment (Key *check) { return keySetComment (check, "comment"); }
+int has_a (Key *check) { return keyName(check)[5]=='a'; }
+int below_a (Key *check) { return keyIsBelow(global_a, check); }
+int direct_below_a (Key *check) { return keyIsDirectBelow(global_a, check); }
+
+int sum_helper (Key *check) { return atoi(keyValue(check)); }
+int below_30 (Key *check) { return atoi(keyValue(check))<30; }
+int find_80 (Key *check) { int n=atoi(keyValue(check)); return n>70?-1:1; }
+
+int main()
+{
+       Key *found;
+       KeySet *out;
+       KeySet *ks = ksNew (64,
+               keyNew ("user/a/1", KEY_END),
+               keyNew ("user/a/2", KEY_END),
+               keyNew ("user/a/b/1", KEY_END),
+               keyNew ("user/a/b/2", KEY_END),
+               keyNew ("user/ab/2", KEY_END),
+               keyNew ("user/b/1", KEY_END),
+               keyNew ("user/b/2", KEY_END),
+               KS_END);
+       KeySet *values = 0;
+       KeySet *values_below_30 = 0;
+
+       global_a = keyNew ("user/a", KEY_END);
+
+       ksForEach (ks, add_string);
+       ksForEach (ks, add_comment);
+
+       out = ksNew (0);
+       ksFilter (out, ks, has_a);
+       ksDel (out);
+
+       out = ksNew (0);
+       ksFilter (out, ks, below_a);
+       ksDel (out);
+
+       out = ksNew (0);
+       ksFilter (out, ks, direct_below_a);
+       ksDel (out);
+
+       ksDel (ks);
+       keyDel (global_a); global_a = 0;
+
+       values = ksNew (64,
+               keyNew ("user/a", KEY_VALUE, "40", KEY_END),
+               keyNew ("user/b", KEY_VALUE, "20", KEY_END),
+               keyNew ("user/c", KEY_VALUE, "80", KEY_END),
+               keyNew ("user/d", KEY_VALUE, "24", KEY_END),
+               keyNew ("user/e", KEY_VALUE, "32", KEY_END),
+               keyNew ("user/f", KEY_VALUE, "12", KEY_END),
+               keyNew ("user/g", KEY_VALUE, "43", KEY_END),
+               KS_END);
+
+       /* add together */
+       ksForEach (values, sum_helper);
+
+       values_below_30 = ksNew (0);
+       ksFilter (values_below_30, values, below_30);
+       ksForEach (values_below_30, sum_helper);
+
+       ksForEach (values, find_80);
+       found = ksCurrent (values); /* here is user/c */
+       found = ksLookupByName (values, "user/c", 0); /* should find the same */
+       ksDel (values);
+       ksDel (values_below_30);
+}
+
diff --git a/examples/getset.c b/examples/getset.c
new file mode 100644 (file)
index 0000000..d447ead
--- /dev/null
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <kdb.h>
+
+#define BUFFER_SIZE 4000
+
+int main(void)
+{
+       char buffer[BUFFER_SIZE+1];
+       KDB *h = kdbOpen();
+       kdbSetString(h, "user/example", "");
+       kdbSetString(h, "user/example/hello", "Hello World");
+       kdbGetString(h, "user/example/hello", buffer, BUFFER_SIZE);
+       printf ("%s\n", buffer);
+       kdbRemove(h, "user/example/hello");
+       kdbRemove(h, "user/example");
+       kdbClose (h);
+}
+
diff --git a/examples/hello.c b/examples/hello.c
new file mode 100644 (file)
index 0000000..09e88bd
--- /dev/null
@@ -0,0 +1,12 @@
+#include <kdb.h>
+#include <stdio.h>
+
+int main(void)
+{
+       Key *k = keyNew("user/hello", KEY_VALUE, "Hello World", KEY_END);
+       printf ("%s\n", (char*)keyValue(k));
+       keyDel (k);
+
+       return 0;
+}
+
diff --git a/examples/hellokdb.c b/examples/hellokdb.c
new file mode 100644 (file)
index 0000000..5b6bb4d
--- /dev/null
@@ -0,0 +1,15 @@
+#include <kdb.h>
+#include <stdio.h>
+
+int main(void)
+{
+       Key *k = keyNew ("user/hello", KEY_END);
+       KDB *handle = kdbOpen();
+
+       if (kdbGetKey (handle, k) == -1) printf ("could not get key\n");
+       else printf ("%s\n", (char*) keyValue(k));
+       kdbClose (handle);
+       keyDel (k);
+
+       return 0;
+}
diff --git a/examples/hierarchy.xml b/examples/hierarchy.xml
new file mode 100644 (file)
index 0000000..158181f
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<!--
+       This is the new hierarchical XML style that supports nested <key>s and
+       two new attributes: parent and basename, being
+       "parent" + "/" + "basename" = "name".
+       
+       The ksFromXML() and ksFromXMLfile() supports this format since version 0.5.
+-->
+
+
+<!--
+       Subversion stuff.
+       
+       $Id$
+       
+ -->
+
+
+<keyset xmlns="http://elektra.sourceforge.net"
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:schemaLocation="http://elektra.sourceforge.net elektra.xsd"
+
+               parent="user/tmp">
+
+       <!-- The wrapping keyset is not really necessary. The key element
+                can be a root key -->
+       
+       <key basename="dir1"> <!-- complete name is user/tmp/dir1 -->
+               <key basename="xyz"> <!-- complete name is user/tmp/dir1/xyz -->
+                       <value>abc</value>
+                       <comment>comment1</comment>
+               </key>
+               <key basename="abc"> <!-- complete name is user/tmp/dir1/abc -->
+                       <value>xyz</value>
+                       <comment>comment1</comment>
+               </key>
+               <key basename="dir2"> <!-- complete name is user/tmp/dir1/dir2 -->
+                       <key basename="def"> <!-- complete name is user/tmp/dir1/dir2/def -->
+                               <value>cecece</value>
+                       </key>
+                       <key basename="uvw"> <!-- complete name is user/tmp/dir1/dir2/uvw -->
+                               <value>tatata</value>
+                       </key>
+               </key>
+       </key>
+</keyset>
+
diff --git a/examples/iterate.c b/examples/iterate.c
new file mode 100644 (file)
index 0000000..1fe9fcf
--- /dev/null
@@ -0,0 +1,86 @@
+/* iterate.c
+ *
+ * This program shows some possibilites how to iterate
+ * over a KeySet in an elegant way. */
+
+#include <kdb.h>
+#include <stdio.h>
+
+/*
+ * This function demonstrates how easy it is to extend
+ * elektra. We use ksNext() and keyIsBelow() to implement a function
+ * which skips to the next directory. */
+Key* ksNextDir(KeySet *ks)
+{
+       Key *cur;
+       Key *startKey = ksCurrent(ks);
+
+       if (!startKey) return (ksNext(ks));
+
+       while ((cur = ksNext(ks)) != 0)
+       {
+               if(!keyIsBelow(startKey, cur))
+                       return cur;
+       }
+
+       return 0;
+}
+
+int main(void)
+{
+       Key *cur=0;
+       Key *found=0;
+       KeySet *ks = ksNew (30,
+               keyNew ("user/dir1", KEY_DIR, KEY_END),
+               keyNew ("user/dir1/key1", KEY_VALUE, "value1", KEY_END),
+               keyNew ("user/dir1/key2", KEY_VALUE, "value2", KEY_END),
+               keyNew ("user/dir1/key3", KEY_VALUE, "value3", KEY_END),
+               keyNew ("user/dir1/key4", KEY_VALUE, "value4", KEY_END),
+               keyNew ("user/dir1/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               keyNew ("user/dir1/.inactive2", KEY_COMMENT, "additional information", KEY_END),
+               keyNew ("user/dir2", KEY_DIR, KEY_END),
+               keyNew ("user/dir2/key1", KEY_VALUE, "value1", KEY_END),
+               keyNew ("user/dir2/key2", KEY_VALUE, "value2", KEY_END),
+               keyNew ("user/dir2/key3", KEY_VALUE, "value3", KEY_END),
+               keyNew ("user/dir2/key4", KEY_VALUE, "value4", KEY_END),
+               keyNew ("user/dir3", KEY_DIR, KEY_END),
+               keyNew ("user/dir3/key1", KEY_VALUE, "value1", KEY_END),
+               keyNew ("user/dir3/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               keyNew ("user/dir3/.inactive2", KEY_COMMENT, "a users comment", KEY_END),
+               keyNew ("user/dir4", KEY_DIR, KEY_END),
+               keyNew ("user/dir5", KEY_DIR, KEY_END),
+               KS_END);
+
+       printf ("Iterate over all keys:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               printf ("%s\n", keyName(cur));
+       }
+
+       printf ("\nIterate over all directories:\n");
+       ksRewind(ks);
+       while ((cur=ksNextDir(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               printf ("%s\n", keyName(cur));
+       }
+
+       printf ("\nLookup and then iterate:\n");
+       found = ksLookupByName(ks, "user/dir2", 0);
+       printf ("Found key %s\n", keyName(found));
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys direct below and prints their name */
+               if (keyIsDirectBelow(found, cur)==0) break;
+               printf ("%s\n", keyName(cur));
+       }
+
+       printf ("\nIterate over inactive keys:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over inactive keys and prints their name */
+               if (keyIsInactive(cur)==0) continue;
+               printf ("%s %s\n", keyName(cur), keyComment(cur));
+       }
+
+       return 0;
+}
diff --git a/examples/keyNewExample.c b/examples/keyNewExample.c
new file mode 100644 (file)
index 0000000..449c695
--- /dev/null
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <kdb.h>
+
+int main() {
+       KeySet *ks=ksNew(0);
+       Key *key=0;
+
+       printf ("Generate some keys...");
+
+       ksAppendKey(ks,keyNew("user/sw",KEY_END));  /* a simple key */
+
+       ksAppendKey(ks,keyNew(0));     /* an empty key */
+
+       ksAppendKey(ks,keyNew("system/sw",
+               KEY_END));
+
+       ksAppendKey(ks,keyNew("user/tmp/ex1",
+               KEY_VALUE,"some data",        /* with a simple value */
+               KEY_END));                    /* end of args */
+
+       ksAppendKey(ks,keyNew("user/tmp/ex2",
+               KEY_VALUE,"some data",        /* with a simple value */
+               KEY_MODE,0777,                /* permissions */
+               KEY_END));                    /* end of args */
+
+       ksAppendKey(ks,keyNew("user/tmp/ex4",
+               KEY_TYPE, KEY_TYPE_BINARY,     /* type and value size */
+               KEY_COMMENT,"value is truncated",
+               KEY_SIZE, 7,
+               KEY_VALUE,"some data",        /* value that will be truncated to 7 bytes */
+               KEY_UID,0,                    /* root uid */
+               KEY_END));                    /* end of args */
+
+       ksAppendKey(ks,keyNew("user/tmp/ex5",
+               KEY_VALUE,"some data",        /* value  */
+               KEY_OWNER,"root",            /* owner (not uid) is root */
+               KEY_COMMENT,"some comment",   /* a comment */
+               KEY_END));                    /* end of args */
+
+       ksAppendKey(ks,keyNew("user/env/alias/ls",  /* a key we know we have */
+               KEY_END));                    /* do nothing more */
+
+       ksAppendKey(ks,keyNew("user/env/alias/ls",  /* same key, to compare in output */
+               KEY_OWNER,"root",            /* set new owner (not uid) as root */
+               KEY_COMMENT,"new comment",    /* set new comment */
+               KEY_END));                    /* end of args */
+
+       key=keyNew("user/test//", KEY_END);
+
+       /* we are providing a lot of '/' to see it being removed */
+       keySetName(key,"system");
+       keySetName(key,"user");
+       keySetName(key,"user:aviram");
+       keySetName(key,"user///abc//////def///");
+       keySetName(key,"user:root///aaa//////bbb///");
+
+       keyAddBaseName(key,"tmp");
+       keyAddBaseName(key,"////ex6///exx7///");
+       keySetBaseName(key,"ex8");
+       keySetBaseName(key,"ex9");
+       keyAddBaseName(key,"///exxx9///ex10///ex\\/11///");
+       keySetBaseName(key,"ex12");
+       keySetBaseName(key,"ex13///");
+       ksAppendKey(ks,key);
+
+       ksDel(ks);
+
+       printf ("finished\n");
+
+       return 0;
+}
+
diff --git a/examples/keyset.c b/examples/keyset.c
new file mode 100644 (file)
index 0000000..a7a9cbd
--- /dev/null
@@ -0,0 +1,65 @@
+#include <kdb.h>
+#include <stdio.h>
+
+void f (const Key * source)
+{
+       Key * dup = keyDup (source);
+       printf ("\tin f\n");
+
+       keyDel (dup);
+}
+
+void g (const Key * source, KeySet * ks)
+{
+       Key * dup = keyDup (source);
+       printf ("\tin g\n");
+
+       ksAppendKey (ks, dup);
+}
+
+void h (Key *k)
+{
+       Key * c = keyNew("user/from/h", KEY_END);
+       printf ("\tin h\n");
+
+       keyCopy (k, c);
+       keyDel (c);
+       /* the caller will see the changed key k */
+}
+
+int main()
+{
+       Key * origKey;
+       KeySet * ks = ksNew(0);
+
+       Key * key = keyNew ("user/test/name",
+                       KEY_VALUE, "myvalue",
+                       KEY_END);
+       printf ("Created key %s with value %s\n",
+                       keyName(key), keyValue(key));
+
+       f(key);
+       printf ("Key is unchanged with value %s\n",
+                       keyValue(key));
+
+       g(key, ks);
+       printf ("A duplication was appended in keyset with name %s\n",
+                       keyName(ksHead(ks)));
+
+       h(key);
+       printf ("Key has changed to name %s with value %s\n",
+                       keyName(key), keyValue(key));
+
+       /* key is yet independent */
+       keyDel (key);
+
+       ksRewind (ks);
+       origKey = ksNext (ks);
+       key = keyDup (origKey);
+       printf ("A duplication of the key %s with value %s\n",
+                       keyName(key), keyValue(key));
+
+       keyDel (key);
+       ksDel (ks);
+       return 0;
+}
diff --git a/examples/keyset.xml b/examples/keyset.xml
new file mode 100644 (file)
index 0000000..bca96ab
--- /dev/null
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+<!-- Generated by Elektra API. Total of 7 keys. -->
+
+
+<!--
+       This file was generated (comments edited later) by the command:
+       
+          bash$ kdb export user/tmp > keyset.xml
+          
+       You can fully re-import it with command:
+       
+          bash$ kdb import keyset.xml
+       
+  -->
+
+<!--
+       Subversion stuff.
+       
+       $Id$
+       $LastChangedBy$
+       
+ -->
+  
+  
+<keyset xmlns="http://elektra.sourceforge.net"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://elektra.sourceforge.net elektra.xsd">
+
+
+<key name="user/tmp/binaryKey"
+     type="string" mode="0664">
+
+<!--
+       Created with command:
+       kdb set user/tmp/binaryKey "some fake-binary value"
+  -->
+
+     <value><![CDATA[some fake-binary value]]></value>
+</key>
+
+
+
+
+
+<key name="user/tmp/dir1"
+     type="directory" mode="0775">
+
+<!--
+       Created with command:
+       kdb set -t dir user/tmp/dir1
+  -->
+
+</key>
+
+
+
+
+
+<key name="user/tmp/key with user-defined binary type"
+     type="25" mode="0664">
+
+<!--
+       Created with command:
+       kdb set -t 25 \
+               -c "User defined, semanticfull binary types are between KEY_TYPE_BINARY (>=20) and KEY_TYPE_STRING (<40)" \
+               "user/tmp/key with user-defined binary type" \
+               "binary-semantic value"
+  -->
+  
+     <value>
+62696e61 72792d73 656d616e 74696320 76616c75 65
+
+</value>
+     <comment><![CDATA[User defined, semanticfull binary types are between KEY_TYPE_BINARY (>=20) and KEY_TYPE_STRING (<40)]]></comment>
+</key>
+
+
+
+
+
+<key name="user/tmp/key with user-defined type"
+     type="45" mode="0664">
+
+<!--
+       Created with command:
+       kdb set -t 45 \
+               -c "User defined, semanticfull string types are bigger then KEY_TYPE_STRING, or 40" \
+               "user/tmp/key with user-defined type" \
+               "a semanticfull value"
+  -->
+  
+     <value><![CDATA[a semanticfull value]]></value>
+</key>
+
+
+
+
+
+<key name="user/tmp/link"
+     type="link" mode="0666">
+
+<!--
+       Created with command:
+       kdb ln ./regularKey user/tmp/link  
+  -->
+
+     <value><![CDATA[./regularKey]]></value>
+</key>
+
+
+
+
+
+<key name="user/tmp/realBinary"
+     type="binary" mode="0664">
+
+<!--
+       Created with command:
+       kdb set -t bin -b /tmp/file.bin \
+               -c "random real binary value" \
+               user/tmp/realBinary
+               
+       /tmp/file.bin was a file containing 50 random bytes.
+       
+  -->
+  
+     <value>
+7f454c46 01010100 00000000 00000000 02000300 01000000 809d0408 34000000
+18480100 00000000 34002000 08002800 1f00
+
+</value>
+     <comment><![CDATA[random real binary value]]></comment>
+</key>
+
+
+
+
+
+<key name="user/tmp/regularKey"
+     type="string" mode="0664">
+
+<!--
+       Created with command:
+       kdb set user/tmp/regularKey "some value"
+  -->
+  
+     <value><![CDATA[some value]]></value>
+</key>
+
+
+
+
+</keyset>
diff --git a/examples/lookup.c b/examples/lookup.c
new file mode 100644 (file)
index 0000000..3521a26
--- /dev/null
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <kdb.h>
+
+int main(int argc,char **argv) {
+       KeySet *ks;
+       Key *key=0;
+       KDB *handle = kdbOpen();
+
+       ks=ksNew(0);
+
+       kdbGetByName(handle,ks,"system/sw/xorg",0);
+       kdbClose(handle);
+
+       ksRewind(ks);
+       key=ksLookupByName(ks,"system/sw/xorg/current/screens/screen0/displays/00/depth",
+               KDB_O_NOCASE);
+
+       printf("*************** Name matching\n\n");
+       if (key) {
+               /* keyToStream(key,stdout,0); */
+               
+               key=ksCurrent(ks);
+               
+               /* keyToStream(key,stdout,0);  should be the same */
+       }
+}
diff --git a/examples/reference.c b/examples/reference.c
new file mode 100644 (file)
index 0000000..9187e12
--- /dev/null
@@ -0,0 +1,49 @@
+#include <kdb.h>
+#include <stdio.h>
+
+void f(Key *k)
+{
+       printf ("\tf called with %s\n", keyName(k));
+       keySetName (k, "user/delete");
+       keyDel (k);
+}
+
+void h(Key *k)
+{
+       printf ("\th called with %s\n", keyName(k));
+       keyIncRef (k);
+
+       f(k);
+
+       keyDecRef (k);
+}
+
+int main(void)
+{
+       Key *k = keyNew(0);
+       printf ("key has ref %d\n", keyGetRef(k));
+
+       f(k);
+       printf ("key is now deleted\n");
+
+       k = keyNew(0);
+       keyIncRef (k);
+       printf ("key has ref %d\n", keyGetRef(k));
+
+       f(k);
+       printf ("key has now name %s\n", keyName(k));
+
+       f(k);
+
+       keyDecRef (k);
+       printf ("key has ref %d", keyGetRef(k));
+       keyDel (k);
+       printf ("key is now deleted\n");
+
+       k = keyNew(0);
+       h(k);
+       keyDel (k);
+       printf ("key is now deleted\n");
+
+       return 0;
+}
diff --git a/examples/small.c b/examples/small.c
new file mode 100644 (file)
index 0000000..211d238
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <kdb.h>
+
+void BailOut (char * msg)
+{
+       fprintf (stderr, "%s\n", msg);
+       exit (1);
+}
+
+int main(void)
+{
+       KDB * h;
+       KeySet * myConfig = ksNew(0);
+       Key    * myKey;
+       char   * myValue;
+
+       /* Open the Key Database */
+       if (!(h = kdbOpen()))
+               BailOut ("Could not open Key Database");
+
+       /* Get the hello world keyset */
+       if (kdbGetByName(h, myConfig, "/", 0) == -1)
+               BailOut ("Could not get Keys");
+
+       /* Find the key in the keyset */
+       if ((myKey = ksLookupByName (myConfig, "/hello", 0)) == NULL)
+               BailOut ("Could not Lookup Key");
+
+       /* Get the value of the key */
+       if ((myValue = (char*) keyValue (myKey)) == NULL)
+               BailOut ("Could not get Keyvalue");
+
+       /* Actually print the key */
+       printf ("%s\n", myValue);
+       /* Close and free KeySet */
+       ksDel(myConfig);
+       /* Close the Key database */
+       kdbClose(h);
+
+       return 0;
+}
+
diff --git a/examples/sort.c b/examples/sort.c
new file mode 100644 (file)
index 0000000..ae6545d
--- /dev/null
@@ -0,0 +1,123 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <kdb.h>
+
+void ksUnsort (KeySet *ks)
+{
+       Key *cur;
+       size_t size = 0;
+       KeySet *randks=ksNew(0); /*This is the final randomized keyset*/
+       KeySet *tempks=ksNew(0); /*Temporary storage for keys not chosen to be inserted*/
+
+       while (ksGetSize(ks) > 0)
+       {
+               ksRewind(ks);
+               size = ksGetSize(ks);
+               /* printf ("iterating %d\n", size); */
+               while ((cur=ksPop(ks)) != 0)
+               {
+                       /* printf ("\titerating %s\n", keyName(cur)); */
+                       if (!(rand()%size)) ksAppendKey(randks, cur);
+                       else ksAppendKey(tempks,cur);
+               }
+               ksAppend(ks, tempks);
+               ksCopy(tempks,0);
+       }
+
+       ksCopy (ks, randks);
+
+       ksDel (randks);
+       ksDel (tempks);
+}
+
+int main(void)
+{
+       Key *cur;
+       Key *found;
+       KeySet *ks = ksNew (30,
+               keyNew ("user/rem2", KEY_REMOVE, KEY_DIR, KEY_END),
+               keyNew ("user/rem1/key2", KEY_REMOVE, KEY_END),
+               keyNew ("user/rem1/key1", KEY_REMOVE, KEY_END),
+               keyNew ("user/rem1", KEY_REMOVE, KEY_DIR, KEY_END),
+               keyNew ("user/dir1", KEY_DIR, KEY_END),
+               keyNew ("user/dir1/key1", KEY_VALUE, "value1", KEY_END),
+               keyNew ("user/dir1/key2", KEY_VALUE, "value2", KEY_END),
+               keyNew ("user/dir1/key3", KEY_VALUE, "value3", KEY_END),
+               keyNew ("user/dir1/key4", KEY_VALUE, "value4", KEY_END),
+               keyNew ("user/dir1/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               keyNew ("user/dir1/.inactive2", KEY_COMMENT, "additional information", KEY_END),
+               keyNew ("user/dir2", KEY_DIR, KEY_END),
+               keyNew ("user/dir2/key1", KEY_VALUE, "value1", KEY_END),
+               keyNew ("user/dir2/key2", KEY_VALUE, "value2", KEY_END),
+               keyNew ("user/dir2/key3", KEY_VALUE, "value3", KEY_END),
+               keyNew ("user/dir2/key4", KEY_VALUE, "value4", KEY_END),
+               keyNew ("user/dir3", KEY_DIR, KEY_END),
+               keyNew ("user/dir3/key1", KEY_VALUE, "value1", KEY_END),
+               keyNew ("user/dir3/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               keyNew ("user/dir3/.inactive2", KEY_COMMENT, "a users comment", KEY_END),
+               keyNew ("user/dir4", KEY_DIR, KEY_END),
+               keyNew ("user/dir5", KEY_DIR, KEY_END),
+               KS_END);
+
+       srand (23);
+       ksUnsort (ks);
+
+       printf ("Keys are now unsorted:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               if (keyNeedRemove(cur)) printf ("%s (Need Remove)\n", keyName(cur));
+               else printf ("%s\n", keyName(cur));
+       }
+
+       found = ksLookupByName (ks, "user/dir1/key3", 0);
+       printf ("\nKeys are sorted after ksLookupByName:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               if (keyNeedRemove(cur)) printf ("%s (Need Remove)\n", keyName(cur));
+               else printf ("%s\n", keyName(cur));
+       }
+
+       keySetName(found, "user/something/else");
+       found = ksLookupByName (ks, "user/something/else", 0);
+       printf ("\nKeys are *not* sorted after ksLookupByName because of changed name.\n");
+       printf ("The search for user/something/else found: %s\n", keyName(found));
+       printf ("The order is:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               if (keyNeedRemove(cur)) printf ("%s (Need Remove)\n", keyName(cur));
+               else printf ("%s\n", keyName(cur));
+       }
+
+       printf ("\nSo we have to sort manually using ksSort()!\n");
+       ksSort (ks);
+       printf ("The same is valid for keyRemove().");
+       found = ksLookupByName (ks, "user/something/else", 0);
+       keyRemove (found);
+       found = ksLookupByName (ks, "user/something/else", 0);
+       printf ("\nKeys are *not* sorted after ksLookupByName because of changed name.\n");
+       printf ("The search for user/something/else (Need Remove) found: %s\n", keyName(found));
+       printf ("The order is:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               if (keyNeedRemove(cur)) printf ("%s (Need Remove)\n", keyName(cur));
+               else printf ("%s\n", keyName(cur));
+       }
+
+       printf ("\nSo we have to sort manually using ksSort()!\n");
+       ksSort (ks);
+       printf ("Now we get:\n");
+       ksRewind(ks);
+       while ((cur=ksNext(ks)) != 0)
+       {       /* Iterates over all keys and prints their name */
+               if (keyNeedRemove(cur)) printf ("%s (Need Remove)\n", keyName(cur));
+               else printf ("%s\n", keyName(cur));
+       }
+
+
+       return 0;
+}
diff --git a/m4/extensions.m4 b/m4/extensions.m4
new file mode 100644 (file)
index 0000000..b2a2431
--- /dev/null
@@ -0,0 +1,42 @@
+# Enable extensions on systems that normally disable them.
+
+# Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This file is only needed in autoconf <= 2.59.  Newer versions of autoconf
+# have a macro AC_USE_SYSTEM_EXTENSIONS with identical semantics.
+
+# gl_USE_SYSTEM_EXTENSIONS
+# ------------------------
+# Enable extensions on systems that normally disable them,
+# typically due to standards-conformance issues.
+AC_DEFUN([gl_USE_SYSTEM_EXTENSIONS], [
+  AC_BEFORE([$0], [AC_COMPILE_IFELSE])
+  AC_BEFORE([$0], [AC_RUN_IFELSE])
+
+dnl  AC_REQUIRE([AC_GNU_SOURCE])
+  AC_REQUIRE([AC_AIX])
+  AC_REQUIRE([AC_MINIX])
+
+  AH_VERBATIM([__EXTENSIONS__],
+[/* Enable extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif])
+  AC_CACHE_CHECK([whether it is safe to define __EXTENSIONS__],
+    [ac_cv_safe_to_define___extensions__],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM([
+         #define __EXTENSIONS__ 1
+         AC_INCLUDES_DEFAULT])],
+       [ac_cv_safe_to_define___extensions__=yes],
+       [ac_cv_safe_to_define___extensions__=no])])
+  test $ac_cv_safe_to_define___extensions__ = yes &&
+    AC_DEFINE([__EXTENSIONS__])
+  AC_DEFINE([_POSIX_PTHREAD_SEMANTICS])
+])
diff --git a/packaging/elektra.spec b/packaging/elektra.spec
new file mode 100644 (file)
index 0000000..505e82c
--- /dev/null
@@ -0,0 +1,93 @@
+
+Name:       elektra
+Summary:    A key/value pair database to store software configurations
+Version:    0.7.0
+Release:    1
+Group:      System/Libraries
+License:    BSD
+URL:        http://elektra.sourceforge.net
+Source0:    elektra-%{version}.tar.gz
+Patch0:     fix-autogen-failure.patch
+Patch1:        static.patch
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+BuildRequires:  pkgconfig(libxml-2.0)
+BuildRequires:  gettext-devel
+BuildRequires:  libtool-ltdl-devel
+
+
+%description
+A framework to get system and user values The objective of the Elektra Project is to help create a pervasive, 
+ ubiquitous configuration system. This is an entire ecosystem, much
+ more than a piece of code. This section lists the work of some 
+ people that are helping to build it.
+
+
+
+%package devel
+Summary:    Framework to get system and user values
+Group:      Development/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description devel
+Framework to get system and user values, key/value pair database
+
+
+%prep
+%setup -q -n %{name}
+
+# fix-autogen-failure.patch
+%patch0 -p1
+%patch1 -p1
+
+%build
+export CFLAGS+="  -fno-short-enums -DSQLFS_BACKEND -DTUNNING_ELEKTRA_0_7 -fpic"
+touch config.rpath
+
+%autogen --disable-static
+%configure \
+    --disable-static \
+    --without-docbook \
+    --enable-cpp=no \
+    --enable-hosts=no \
+    --enable-shared=yes
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%makeinstall
+
+
+rm -rf %{buildroot}/usr/lib/*.la
+rm -rf %{buildroot}/usr/lib/elektra/*.la
+
+pushd %{buildroot}/usr/lib
+ln -s elektra/libelektra-ddefault.so
+ln -s elektra/libelektra-default.so
+ln -s elektra/libelektra-filesys.so
+popd
+
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+
+
+
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING
+%{_libdir}/*.so.*
+%{_libdir}/libelektra-*.so
+%{_libdir}/elektra/*.so.*
+%{_libdir}/elektra/*.so
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/*.h
+%{_libdir}/pkgconfig/*.pc
+%{_libdir}/libelektra.so
+%{_libdir}/*.a
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
new file mode 100644 (file)
index 0000000..b75f218
--- /dev/null
@@ -0,0 +1,12 @@
+# $Id$
+
+elektraenvdir = $(sysconfdir)/profile.d
+elektrainitdir = $(sysconfdir)/init.d
+elekscriptdir = $(docdir)/scripts
+
+dist_elektraenv_SCRIPTS = elektraenv.sh
+dist_elektrainit_SCRIPTS = kdbd
+dist_elekscript_DATA = benchmark-createtree convert-hosts convert-inittab \
+  convert-users convert-fstab convert-hwconfKudzu convert-network \
+  convert-xfree update-backend kdbd
+
diff --git a/scripts/benchmark-createtree b/scripts/benchmark-createtree
new file mode 100755 (executable)
index 0000000..9cf3398
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# $Id$
+
+ROOT="user/bench"
+LEVELS=3
+DIRS=5
+KEYS=2
+
+genLevel() {
+       local level=$1
+       local myRoot=$2
+       local pad
+       local counter=0
+       
+       while [ $counter -lt $level ]; do
+               pad=`echo -n " $pad"`
+               counter=`expr $counter + 1`
+       done
+
+#      echo "Generating keys under $myRoot" >&2
+#      echo "$pad<key type=\"dir\" basename=\"$level-$myRoot\">"
+       
+       # Generate local keys
+       local k=0
+       while [ $k -lt $KEYS ]; do
+               type=`expr $RANDOM$RANDOM % 255`
+               [ $type -lt 20 ] && type=20;
+               echo "$pad<key type=\"$type\" basename=\"$k-${RANDOM}${RANDOM}\" value=\"$k ${RANDOM}${RANDOM}\"><comment>${RANDOM}${RANDOM}${RANDOM}${RANDOM}</comment></key>"
+               keyCounter=`expr $keyCounter + 1`
+#              kdb set -t $type -c "${RANDOM}${RANDOM}" \
+#                      "$myRoot/${RANDOM}${RANDOM}" \
+#                      "${RANDOM}${RANDOM}"
+
+               k=`expr $k + 1`
+       done
+       
+       if [ $level -lt $LEVELS ]; then
+               # Generate sublevels
+               local d=0
+               while [ $d -lt $DIRS ]; do
+                       echo "$pad<key type=\"directory\" basename=\"dir-$level-$d\">"
+                       keyCounter=`expr $keyCounter + 1`
+                       genLevel `expr $level + 1` "${RANDOM}${RANDOM}"
+                       echo "$pad</key>"
+                       d=`expr $d + 1`
+               done
+       fi
+       
+}
+
+keyCounter=0
+
+echo "<keyset parent=\"$ROOT\">"
+genLevel 1 ${RANDOM}
+echo "<!-- $keyCounter keys generated -->"
+echo "</keyset>"
diff --git a/scripts/convert-fstab b/scripts/convert-fstab
new file mode 100755 (executable)
index 0000000..facce3d
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+########################################################################
+##
+## This script will read /etc/fstab and create an
+## equivalent key tree under $ROOT.
+##
+## The correct way to run it is:
+##
+## # ./convert-fstab > fstab.xml
+## # kdb import fstab.xml
+##
+##
+## To make tests you can do:
+##
+## $ ROOT=user/test ./convert-fstab
+##
+## Avi Alkalay <avi@unix.sh>
+## April 2004
+##
+## $Id$
+##
+########################################################################
+
+
+[ -z "$ROOT" ] && ROOT="system/filesystems"
+
+num=0
+
+echo "<keyset parent=\"$ROOT\">"
+
+cat /etc/fstab | while read dev mpoint type options dumpfreq passno; do
+       
+       expr match "$dev" '#.*' > /dev/null && continue
+       test $dev || continue
+
+       case "$mpoint" in 
+               "none")
+                       num=$(( $num + 1 ))
+                       fsname="$type$num"
+                       ;;
+               '/')
+                       fsname="roofs"
+                       ;;
+               *)
+                       fsname=`echo $mpoint | sed -e 's^/^^g'`
+                       ;;
+       esac
+
+       
+       echo "  <key basename=\"$fsname\">"
+       echo "    <key basename=\"device\" value=\"$dev\"><comment>Device or Label</comment></key>"
+       echo "    <key basename=\"mpoint\" value=\"$mpoint\"><comment>Mount Point</comment></key>"
+       echo "    <key basename=\"type\" value=\"$type\"><comment>Filesystem type. See fs(5)</comment></key>"
+       echo "    <key basename=\"options\" value=\"$options\"><comment>Filesystem specific options. See mount(8)</comment></key>"
+       echo "    <key basename=\"dumpfreq\" value=\"$dumpfreq\"><comment>Dump frequency in days</comment></key>"
+       echo "    <key basename=\"passno\" value=\"$passno\"><comment>Pass number on parallel fsck</comment></key>"
+       echo "  </key>"
+       echo
+done
+
+echo "</keyset>"
diff --git a/scripts/convert-hosts b/scripts/convert-hosts
new file mode 100755 (executable)
index 0000000..841151e
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+########################################################################
+##
+## This script will read /etc/hosts and create an
+## equivalent registry tree under $ROOT.
+##
+## The correct way to run it is:
+##
+## # ./hosts-convert | sh -e
+##
+##
+## To make tests you can do:
+##
+## $ ROOT=user/test ./hosts-convert | sh -e
+##
+## Vladimir Shabanov <virl@mail.ru>
+## June 2004
+##
+## $Id$
+##
+########################################################################
+
+
+[ -z "$RG" ] && RG="kdb"
+[ -z "$ROOT" ] && ROOT="system/network/hosts"
+
+
+cat /etc/hosts | sed /"#"/d | while read -a hosts; do
+
+       echo $RG set -c \"Hostname\" $ROOT/${hosts[0]}/hostname \'${hosts[1]}\'
+       
+       total_words=`echo ${hosts[*]} | wc -w`
+       for (( i=2; $((i < $total_words)); i++ )); do
+       num=$((i-2))
+       echo $RG set -c \"Hostname alias\" $ROOT/${hosts[0]}/host_alias$num \'${hosts[$i]}\'
+       done
+       
+done
diff --git a/scripts/convert-hwconfKudzu b/scripts/convert-hwconfKudzu
new file mode 100755 (executable)
index 0000000..a442219
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+########################################################################
+##
+## This script will create a registry tree based on the detected HW
+## located in /etc/sysconfig/hwconf. That file is parsed and
+## re-generated at every boot by kudzu.
+##
+## This script must calculate a unique ID for each device. It can't
+## be sequential because sequence may change. It can't be only the
+## description of device, because you may have 2 identical devices.
+##
+## It will generate 2 trees for faster searches:
+##    system/hw/byClass    and
+##    system/hw/byBus
+## The entries in the second tree are links to the first.
+##
+## The correct way to run it is:
+##
+## bash# ./hwconfKudzu-convert | sh -e
+##
+##
+## To make tests you can do:
+##
+## bash$ ROOT=user/test ./hwconfKudzu-convert | sh -e
+##
+## Avi Alkalay <avi@unix.sh>
+## Apr 2004
+##
+## $Id$
+##
+########################################################################
+
+
+$HWFILE="/etc/sysconfig/hwconf";
+$ROOT="system/hw";
+$RG="kdb";
+
+
+
+sub printEntry {
+       if (%entry) {
+               $entry{desc}=~s/^\"(.*)\"$/$1/; # remove "
+               
+               $uniqDevID=$entry{desc};
+               $uniqDevID=~s/[\W|\s]*//g;
+               if (defined($entry{pcifn})) {
+                       $uniqDevID.=$entry{pcifn};
+               } elsif (defined($entry{usbbus})) {
+                       $uniqDevID.=$entry{usbbus};
+               } elsif (defined($entry{device})) {
+                       $uniqDevID.=$entry{device};
+               } else {
+                       printf STDERR "Can't calculate unique identifier for \"$entry{desc}\"\n";
+                       return;
+               }
+               
+               # Some escaping and treatments
+               $entry{desc}=~s/\'/\\'/g;       # escape '
+               $entry{desc}=~s/\"/\\"/g;       # escape "
+               $entry{desc}=~s/\(/\\\(/g;      # escape (
+               $entry{desc}=~s/\)/\\\)/g;      # escape )
+               $entry{desc}=~s/\|/\\\|/g;      # escape |
+               $entry{desc}=~s/\#/\\\#/g;      # escape #
+               
+               foreach $key (keys(%entry)) {
+                       print("$RG set $ROOT/byClass/$entry{class}/$uniqDevID/$key " );
+                       print("-- $entry{$key}\n");
+               }
+
+               # Make link for the 'byBus' tree
+               # print("$RG set -t dir \"$ROOT/byBus/$entry{bus}\"\n");
+               print("$RG ln \"$ROOT/byClass/$entry{class}/$uniqDevID\" \"$ROOT/byBus/$entry{bus}/$uniqDevID\"\n\n");
+       }
+}
+
+
+
+open(HWFILE);
+
+while(<HWFILE>) {
+       SWITCH: {
+               if (/^-$/) {
+                       printEntry(%entry);
+                       undef(%entry);
+                       last SWITCH;
+               }
+#              if (/^class\:\s*(.*)/) {
+#                      $entry{class}=$1;
+#                      last SWITCH;
+#              }
+#              if (/^bus\:\s*(.*)/) {
+#                      $entry{bus}=$1;
+#                      last SWITCH;
+#              }
+               /(.*)\:\s+(.*)/;
+               $entry{$1}=$2;
+       }
+}
+
+close(HWFILE);
+
+# Handle last entry
+printEntry(%entry);
diff --git a/scripts/convert-inittab b/scripts/convert-inittab
new file mode 100755 (executable)
index 0000000..d61301f
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+########################################################################
+##
+## This script will read the /etc/inittab and create an
+## equivalent key tree under $ROOT.
+##
+## The correct way to use it is:
+##
+## $ ./inittab-convert > init.xml
+## # kdb import init.xml
+##
+## To make tests you can do:
+##
+## $ ROOT=user/test ./inittab-convert > init.xml
+##
+## Avi Alkalay <avi@unix.sh>
+## March 2004
+##
+## $Id$
+##
+########################################################################
+
+
+[ -z "$RG" ] && RG="kdb"
+[ -z "$ROOT" ] && ROOT="system"
+
+echo "<keyset parent=\"$ROOT/init\">"
+echo
+
+cat /etc/inittab | sed -e "s/\#.*\$//g; s/\"/\'/g;" | grep -v '^$' |
+               awk -F : '{
+                       if (seq) seq=seq","$1;
+                       else seq=$1;
+                       
+                       printf "   <key basename=\"%s/runlevels\" value=\"%s\"><comment>Active runlevels</comment></key>\n", $1,$2;
+                       
+                       printf "   <key basename=\"%s/action\" value=\"%s\"><comment>Approach</comment></key>\n", $1, $3;
+                       
+                       printf "   <key basename=\"%s/process\" value=\"%s\"><comment>Program to execute</comment></key>\n", $1, $4;
+
+                       printf "\n";
+               } END {
+                       printf "   <key basename=\"sequence\" value=\"%s\"><comment>Sequence the entries will run</comment></key>\n", seq;
+               }'
+
+echo
+echo "</keyset>"
diff --git a/scripts/convert-network b/scripts/convert-network
new file mode 100755 (executable)
index 0000000..177905e
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+########################################################################
+##
+## This script will read network configurations under /etc/ and 
+## create an equivalent registry tree under $ROOT.
+##
+## The correct way to run it is:
+##
+## # ./net-convert | sh -e
+##
+##
+## To make tests you can do:
+##
+## $ ROOT=user/test ./net-convert | sh -e
+##
+## Avi Alkalay <avi@unix.sh>
+## March 2004
+##
+########################################################################
+
+
+[ -z "$RG" ] && RG="kdb"
+[ -z "$ROOT" ] && ROOT="system"
+
+PROFILES_HOME="/etc/sysconfig/networking/profiles"
+
+# Get list of profiles
+cd $PROFILES_HOME
+ls | while read profile; do
+       
+
+done
diff --git a/scripts/convert-users b/scripts/convert-users
new file mode 100755 (executable)
index 0000000..9484c53
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+########################################################################
+##
+## This script will read the /etc/{passwd,group,shadow} and create an
+## equivalent registry tree under $ROOT.
+##
+## The correct way to run it is:
+##
+## # ./users-convert | sh -e
+##
+##
+## To make tests you can do:
+##
+## $ ROOT=user/test ./users-convert | sh -e
+##
+## Avi Alkalay <avi@unix.sh>
+## March 2004
+##
+## $Id$
+##
+########################################################################
+
+
+[ -z "$RG" ] && RG="kdb"
+[ -z "$ROOT" ] && ROOT="system"
+
+
+cat /etc/passwd | awk -F : "{
+       print \"$RG set $ROOT/users/\" \$1 \"/uid \'\" \$3 \"\'\";
+       print \"$RG set $ROOT/users/\" \$1 \"/password \'\" \$2 \"\'\";
+       print \"$RG set $ROOT/users/\" \$1 \"/gid \'\" \$4 \"\'\";
+       print \"$RG set $ROOT/users/\" \$1 \"/gecos \'\" \$5 \"\'\";
+       print \"$RG set $ROOT/users/\" \$1 \"/home \'\" \$6 \"\'\";
+       print \"$RG set $ROOT/users/\" \$1 \"/shell \'\" \$7 \"\'\";
+       print \"\";
+}"
+
+echo
+echo
+
+cat /etc/group | awk -F : "{
+       print \"$RG set $ROOT/groups/\" \$1 \"/gid \'\" \$3 \"\'\";
+       print \"$RG set $ROOT/groups/\" \$1 \"/members \'\" \$4 \"\'\";
+       print \"\";
+}"
+
+echo
+echo
+
+if [ -r /etc/shadow ]; then
+       cat /etc/shadow | awk -F : "{
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/shadowPassword \'\" \$2 \"\'\";
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/passwdChangeBefore \'\" \$4 \"\'\";
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/passwdChangeAfter \'\" \$5 \"\'\";
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/passwdWarnBefore \'\" \$6 \"\'\";
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/passwdDisableAfter \'\" \$7 \"\'\";
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/passwdDisabledSince \'\" \$8 \"\'\";
+               print \"$RG set -m 0600 $ROOT/users/\" \$1 \"/passwdReserved \'\" \$9 \"\'\";
+               print \"\";
+       }"
+fi
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/convert-xfree b/scripts/convert-xfree
new file mode 100755 (executable)
index 0000000..f9281b8
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+########################################################################
+##
+## This script will create a registry tree based on
+## my /etc/X11/XF85Config. It doesn't parse anything, since this
+## is pretty dificult to do.
+##
+## The correct way to run it is:
+##
+## # ./xfree86-convert | sh -e
+##
+##
+## To make tests you can do:
+##
+## $ ROOT=user/test ./xfree-convert | sh -e
+##
+## Avi Alkalay <avi@unix.sh>
+## Apr 2004
+##
+########################################################################
+
+
+[ -z "$RG" ] && RG="kdb"
+[ -z "$ROOT" ] && ROOT="system/sw/xorg"
+
+
+
+echo "$RG set $ROOT"
+echo "$RG set $ROOT/Device"
+echo "$RG set $ROOT/Device/Videocard0"
+echo "$RG set $ROOT/Device/Videocard0/BoardName      'ATI Radeon Mobility 7500'"
+echo "$RG set $ROOT/Device/Videocard0/Driver         'radeon'"
+echo "$RG set $ROOT/Device/Videocard0/VendorName     'ATI/IBM'"
+echo
+echo "$RG set $ROOT/DRI/Group      '0'"
+echo "$RG set $ROOT/DRI/Mode       '0666'"
+echo
+echo "$RG set $ROOT/Files/FontPath      'unix/:7100'"
+echo "$RG set $ROOT/Files/RgbPath       '/usr/X11R6/lib/X11/rgb'"
+echo
+echo "$RG set $ROOT/InputDevice/DevInputMice/Driver                      'mouse'"
+echo "$RG set $ROOT/InputDevice/DevInputMice/Option/Device               '/dev/psaux'"
+echo "$RG set $ROOT/InputDevice/DevInputMice/Option/Emulate3Buttons      'no'"
+echo "$RG set $ROOT/InputDevice/DevInputMice/Option/Protocol             'PS/2'"
+echo "$RG set $ROOT/InputDevice/DevInputMice/Option/ZAxisMapping         '4 5'"
+echo
+echo "$RG set $ROOT/InputDevice/Keyboard0/Driver                'keyboard'"
+echo "$RG set $ROOT/InputDevice/Keyboard0/Option/XkbLayout      'us_intl'"
+echo "$RG set $ROOT/InputDevice/Keyboard0/Option/XkbModel       'pc105'"
+echo "$RG set $ROOT/InputDevice/Keyboard0/Option/XkbRules       'xfree86'"
+echo
+echo "$RG set $ROOT/InputDevice/Mouse0/Driver                      'mouse'"
+echo "$RG set $ROOT/InputDevice/Mouse0/Option/Device               '/dev/input/mice'"
+echo "$RG set $ROOT/InputDevice/Mouse0/Option/Emulate3Buttons      'no'"
+echo "$RG set $ROOT/InputDevice/Mouse0/Option/Protocol             'IMPS/2'"
+echo "$RG set $ROOT/InputDevice/Mouse0/Option/ZAxisMapping         '4 5'"
+echo
+echo "$RG set $ROOT/Module/Load      'dbe,extmod,fbdevhw,glx,record,freetype,type1,dri'"
+echo
+echo "$RG set $ROOT/Monitor/Monitor0/ModelName             'LCD Panel 1024x768'"
+echo "$RG set $ROOT/Monitor/Monitor0/VendorName            'IBM T40 monitor'"
+echo "$RG set $ROOT/Monitor/Monitor0/Option/dpms           '1'"
+echo "$RG set $ROOT/Monitor/Monitor0/HorizSync/max         '48.5'"
+echo "$RG set $ROOT/Monitor/Monitor0/HorizSync/min         '31.5'"
+echo "$RG set $ROOT/Monitor/Monitor0/VertRefresh/max       '70'"
+echo "$RG set $ROOT/Monitor/Monitor0/VertRefresh/min       '40'"
+echo
+echo "$RG set $ROOT/Screen/Screen0/Monitor             'Monitor0'"
+echo "$RG set $ROOT/Screen/Screen0/Device              'Videocard0'"
+echo "$RG set $ROOT/Screen/Screen0/DefaultDepth        '16'"
+echo "$RG set $ROOT/Screen/Screen0/Display/Depth       '16'"
+echo "$RG set $ROOT/Screen/Screen0/Display/Modes       '1024x768,800x600,640x480'"
+echo
+echo "$RG set $ROOT/ServerLayout/Default/Screen                      'Screen0'"
+echo "$RG set $ROOT/ServerLayout/Default/InputDevice/CoreKeyboard    'Keyboard0'"
+echo "$RG set $ROOT/ServerLayout/Default/InputDevice/AlwaysCore      'DevInputMice'"
+echo "$RG set $ROOT/ServerLayout/Default/InputDevice/CorePointer     'Mouse0'"
+echo
+echo "$RG ln `basename $ROOT` system/sw/XFree/current"
+echo
+echo
diff --git a/scripts/elektraenv.sh b/scripts/elektraenv.sh
new file mode 100755 (executable)
index 0000000..6239ecd
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+###########################################################################
+##
+## This is a /etc/profile.d script to set user environment and aliases
+## based on Elektra keys under 'user/env'.
+##
+## Bellow user/env there must be three priorities for environment
+## variables:
+##
+##     user/env/env1
+##     user/env/env2
+##     user/env/env3
+##
+## You should distribute your environment variables according to their
+## dependencies. For example, if we want to
+## set PATH as $JAVA_HOME/bin:$PATH, we'll have to set
+##
+##     user/env/env1/JAVA_HOME
+##     user/env/env2/PATH
+##
+## This way it is guaranteed that the variables under user/env/env1 are
+## set before those under user/env/env2, and before those under
+## user/env/env3
+##
+## The folder user/env/alias contains keys to define shell aliases.
+## For instance user/env/alias/ls may contain 'ls -Fh', to set an alias
+## to the 'ls' command.
+##
+## Avi Alkalay <avi at unix dot sh>
+## April 2004
+##
+## $Id$
+##
+###########################################################################
+
+
+if [ -z "$KDB" ]; then
+       KDB=kdb
+fi
+
+FILE="`mktemp -t elektraenv.XXXXXXXXX`"
+
+
+readEnvTree() {
+       local keysAvailable=0
+       local stage=0
+       local key
+
+       for stage in 1 2 3; do
+               echo "# Stage $stage"
+               $KDB ls $1/env$stage 2>/dev/null | while read key; do
+                       if [ -z $keysAvailable ]; then
+                               keysAvailable=1
+                               echo "echo Setting environment from the Elektra key database under '$1'"
+                       fi
+                       # This stuff is so complicated, with sed etc, because
+                       # we need to superescape a '\$' for envs like PS1
+                       echo -n "export "
+                       $KDB get -s $key | sed -e 's/\([^\\]\)\\\$/\1\\\\\$/g;'
+               done
+       done
+
+       echo
+       echo "# Aliases"
+       $KDB ls $1/alias 2>/dev/null | while read key; do
+               echo alias `$KDB get -s $key`
+       done
+}
+
+
+
+########################
+##
+##  Main block
+##
+
+# set -vx
+
+readEnvTree system/env > $FILE
+(echo; echo; echo) >> $FILE
+readEnvTree user:$USER/env >> $FILE
+
+# Execute it
+[ -f $FILE ] && . $FILE
+
+# Remove it
+[ -f $FILE ] && rm -f $FILE
+
+# Clean temporary environment
+unset readEnvTree
+unset FILE
+unset KDB
+
diff --git a/scripts/kdbd b/scripts/kdbd
new file mode 100755 (executable)
index 0000000..d5d4656
--- /dev/null
@@ -0,0 +1,111 @@
+#!/bin/bash
+#
+# kdbd:   Starts the Elektra Key Database Daemon
+#
+# chkconfig: 12345 00 99
+# description:  This is the overall daemon needed to correctly access Elektra
+#               key databases on backends like BerkeleyDB.
+#               The daemon will use the backend pointed
+#               by /lib/elektra/libelektra-ddefault.so.
+#               Though not recomended, an elektrified program can access a
+#               key database without this daemon. But different permissions
+#               calculations will be performed.
+# processname: /sbin/kdbd
+# config: 
+# config: 
+#
+### BEGIN INIT INFO
+# Provides: kdbd
+# Required-Start: 
+# Default-Stop: 0 6
+# Short-Description: Starts the Elektra Key Database access daemon
+# Description:  This is the overall daemon needed to correctly access Elektra
+#               key databases on backends like BerkeleyDB.
+#               The daemon will use the backend pointed
+#               by /lib/elektra/libelektra-ddefault.so.
+#               Though not recomended, an elektrified program can access a
+#               key database without this daemon. But different permissions
+#               calculations will be performed.
+### END INIT INFO
+#
+# $Id$
+#
+#
+
+
+KDBD_PATH=/sbin
+RETVAL=0
+prog=kdbd
+pidfile=/var/run/kdbd/$prog.pid
+
+# Sanity checks.
+#[ -f /etc/nscd.conf ] || exit 0
+[ -x $KDBD_PATH/kdbd ] || exit 0
+
+# Source function library.
+. /etc/init.d/functions
+
+# Source an auxiliary options file if we have one, and pick up NSCD_OPTIONS.
+#[ -r /etc/sysconfig/nscd ] && . /etc/sysconfig/nscd
+
+start () {
+       [ -d /var/run/$prog ] || mkdir /var/run/$prog
+#    [ -d /var/db/nscd ] || mkdir /var/db/nscd
+#    secure=""
+       echo -n $"Starting $prog: "
+       daemon $KDBD_PATH/$prog     # $secure $KDBD_OPTIONS
+#      daemon --check $prog $prog --pidfile=$pidfile
+       RETVAL=$?
+       echo
+       [ $RETVAL -eq 0 ] && touch /var/lock/subsys/kdbd
+       return $RETVAL
+}
+
+stop () {
+       echo -n $"Stopping $prog: "
+       killproc $prog
+       echo
+       RETVAL=$?
+       [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
+       return $RETVAL
+}
+
+restart() {
+       stop
+       start
+}
+
+# See how we were called.
+case "$1" in
+       start)
+       start
+       RETVAL=$?
+       ;;
+       stop)
+       stop
+       RETVAL=$?
+       ;;
+    status)
+       status kdbd
+       RETVAL=$?
+       ;;
+       restart)
+       restart
+       RETVAL=$?
+       ;;
+       try-restart | condrestart)
+       [ -e /var/lock/subsys/$prog ] && restart
+       RETVAL=$?
+       ;;
+       force-reload | reload)
+       echo -n $"Reloading $prog: "
+       killproc /usr/sbin/$prog -HUP
+       RETVAL=$?
+       echo
+       ;;
+       *)
+       echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
+       RETVAL=1
+       ;;
+esac
+exit $RETVAL
diff --git a/scripts/update-backend b/scripts/update-backend
new file mode 100755 (executable)
index 0000000..13f98f1
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+# update-backend
+
+# This Tool helps you to select another
+# backend. Its in a very pre-alpha state,
+# dont use it.
+
+# Author: Markus Raab
+# Licence: BSD
+
+PATH="/bin"
+
+DEBUG=echo
+TMPFILE="/tmp/$$.kdb.xml"
+
+if [ ! "$1" ]
+then
+       echo "Usage: $0 backend"
+       exit 1
+fi
+
+if [ "$UID" != 0 ]
+then
+       echo "Only root can run this script"
+       exit 2
+fi
+
+if [ ! -f "/lib/libelektra-$1.so" ]
+then
+       echo "Choosen backend not available"
+       exit 3
+fi
+
+kdb export > "$TMPFILE"
+
+if [ "$?" != 0 ]
+then
+       echo "Exporting was not possible"
+       exit 4
+fi
+
+$DEBUG rm -f "/lib/libelektra-default.so"
+$DEBUG ln -s "/lib/libelektra-$1.so" "/lib/libelektra-default.so"
+
+kdb import < "$TMPFILE"
+
+if [ "$?" != 0 ]
+then
+       echo "Warning: Import Configuration was not possible!"
+       echo "Check $TMPFILE to fix the problem."
+       exit 5
+fi
+
+$DEBUG rm "$TMPFILE"
+
+echo "Installed new backend $1 successfully!"
+
diff --git a/src/Doxyfile b/src/Doxyfile
new file mode 100644 (file)
index 0000000..f838b89
--- /dev/null
@@ -0,0 +1,1471 @@
+# Doxyfile 1.5.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "Elektra Projekt"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = ../doc/elektra-api
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, 
+# and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = "err=\xrefitem err \"Error\" \"Error\""
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen to replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = NO
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = $(srcdir)/libelektra \
+                         $(srcdir)/libhelper \
+                         $(srcdir)/libelektratools \
+                         $(srcdir)/include \
+                         $(srcdir)/backends/doc
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.C \
+                         *.CC \
+                         *.C++ \
+                         *.II \
+                         *.I++ \
+                         *.H \
+                         *.HH \
+                         *.H++ \
+                         *.CS \
+                         *.PHP \
+                         *.PHP3 \
+                         *.M \
+                         *.MM \
+                         *.PY
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = $(srcdir)/../examples
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = $(srcdir)/../doc/images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature. Other possible values 
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hiererachy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output 
+# directory and reference it in all dot files that doxygen generates. This 
+# font does not include all possible unicode characters however, so when you need 
+# these (or just want a differently looking font) you can specify the font name 
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
+# which can be done by putting it in a standard location or by setting the 
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# By default doxygen will tell dot to use the output directory to look for the 
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
+# different font using DOT_FONTNAME you can set the path where dot 
+# can find it using this tag.
+
+DOT_FONTPATH           = 
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 1000
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is enabled by default, which results in a transparent 
+# background. Warning: Depending on the platform used, enabling this option 
+# may lead to badly anti-aliased labels on the edges of a graph (i.e. they 
+# become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..e23fa0f
--- /dev/null
@@ -0,0 +1,6 @@
+std_subdirs = include libloader libhelper libelektra backends
+
+DIST_SUBDIRS = $(std_subdirs)
+SUBDIRS = $(std_subdirs)
+EXTRA_DIST = Doxyfile
+
diff --git a/src/backends/Makefile.am b/src/backends/Makefile.am
new file mode 100644 (file)
index 0000000..d305c8b
--- /dev/null
@@ -0,0 +1,19 @@
+DIST_SUBDIRS = filesys berkeleydb daemon fstab passwd hosts gconf ini template
+SUBDIRS = @BACKENDS@
+
+EXTRA_DIST = winregistry doc/backend.c
+
+install-exec-hook:
+       cd $(DESTDIR)$(backenddir) && \
+       test -L libelektra-default.so || \
+       $(LN_S) libelektra-@default_backend@.so libelektra-default.so
+       cd $(DESTDIR)$(backenddir) && \
+       test -L libelektra-ddefault.so || \
+       $(LN_S) libelektra-@default_dbackend@.so libelektra-ddefault.so
+
+uninstall-hook:
+       -cd $(DESTDIR)$(backenddir) && \
+       rm -f libelektra-default.so
+       -cd $(DESTDIR)$(backenddir) && \
+       rm -f libelektra-ddefault.so
+
diff --git a/src/backends/berkeleydb/Makefile.am b/src/backends/berkeleydb/Makefile.am
new file mode 100644 (file)
index 0000000..b1ef832
--- /dev/null
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+noinst_LIBRARIES = libelektra-berkeleydb.a
+libelektra_berkeleydb_a_SOURCES = berkeleydb.c ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_berkeleydb_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+backend_LTLIBRARIES = libelektra-berkeleydb.la
+libelektra_berkeleydb_la_SOURCES = berkeleydb.c ../../include/kdb.h ../../include/kdbbackend.h 
+libelektra_berkeleydb_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_berkeleydb_la_LIBADD = ../../libelektra/libelektra.la -ldb
+libelektra_berkeleydb_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
diff --git a/src/backends/berkeleydb/README b/src/backends/berkeleydb/README
new file mode 100644 (file)
index 0000000..6904b80
--- /dev/null
@@ -0,0 +1,3 @@
+This backend is almost ready in terms of functionality.
+kdbRemove(), kdbRename() and some other implementations are missing.
+It needs some fine tunning also.
diff --git a/src/backends/berkeleydb/berkeleydb.c b/src/backends/berkeleydb/berkeleydb.c
new file mode 100644 (file)
index 0000000..d42249f
--- /dev/null
@@ -0,0 +1,1325 @@
+/***************************************************************************
+            berkeleydb.c  -  A Berkeley DB backend for Elektra
+                             -------------------
+    begin                : Mon Jan 24 2005
+    copyright            : (C) 2005 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <db.h>
+
+#include <kdbbackend.h>
+
+#define BACKENDNAME "berkeleydb"
+#define BACKENDVERSION "0.5.5"
+
+#define DB_DIR_USER   ".kdb-berkeleydb"
+#define DB_DIR_SYSTEM "/etc/kdb-berkeleydb"
+
+#define DB_KEYVALUE      "keyvaluepairs"
+#define DB_PARENTINDEX   "parentindex"
+
+#define DB_FILE_KEYVALUE   "keyvalue.db"
+#define DB_FILE_PARENTS    "parents.idx"
+
+
+/**
+ * Our DB layout uses 2 simple tables:
+ * 
+ * - keyValuePairs: table key is key name, and data is a serialization of
+ *   key's metadata, value and comment.
+ * 
+ * - parentIndex: a secondary index, to make folder searches possible, so
+ *   it contains the parent key name as the table key and some DB internal
+ *   data to point to keyValuePais table primary key-data pairs.
+ * 
+ * So if we have the following Elektra keys:
+ * 
+ *     user/sw/app1/key1
+ *     user/sw/app1/key2
+ *     user/sw/app1/dir1/
+ *     user/sw/app1/dir1/key1
+ *     user/sw/app1/dir1/key2
+ * 
+ * The keyValuePairs table will contain:
+ * 
+ *     user/sw/app1/key1      | metadata, value, comment
+ *     user/sw/app1/key2      | metadata, value, comment
+ *     user/sw/app1/dir1      | metadata
+ *     user/sw/app1/dir1/key1 | metadata, value, comment
+ *     user/sw/app1/dir1/key2 | metadata, value, comment
+ *     
+ * And parentIndex table will contain:
+ * 
+ *     user/sw/app1      | (BDB internal pointer to key1 on primary table)
+ *     user/sw/app1      | (BDB internal pointer to key2 on primary table)
+ *     user/sw/app1      | (BDB internal pointer to dir1 on primary table)
+ *     user/sw/app1/dir1 | (BDB internal pointer to dir1/key1 on primary table)
+ *     user/sw/app1/dir1 | (BDB internal pointer to dir1/key2 on primary table)
+ * 
+ * The parentIndex table is written and managed automatically by Berkeley DB
+ * DB->associate() method, with the help of our parentIndexCallback().
+ * 
+ */
+
+
+
+
+/**
+ *  A container for the Berkeley DBs related to the same DBTree.
+ */
+typedef struct {
+       DB *parentIndex;   /* maps folders to the keys they contain */
+       DB *keyValuePairs; /* maps keynames to their values + metainfo */
+} DBInternals;
+
+
+
+
+
+/**
+ *  Each DBTree contains all info needed to access an Elektra
+ *  root tree.
+ *
+ *  Example of root trees:
+ *  system/ *        {isSystem=1,owner=0,...}
+ *  user/ *          {isSystem=0,owner=$USER,...}
+ *  user:luciana/ *  {isSystem=0,owner=luciana,...}
+ *  user:denise/ *   {isSystem=0,owner=denise,...}
+ *  user:tatiana/ *  {isSystem=0,owner=tatiana,...}
+ *
+ */
+typedef struct _DBTree {
+       /* if isSystem==0 and owner==0, this DB is for the current user */
+       int isSystem;
+       char *owner;
+       DBInternals db;
+       struct _DBTree *next;
+} DBTree;
+
+
+/**
+ *  A container for all opened DBTrees
+ */
+typedef struct {
+       size_t size;       /* number of opened databases */
+       DBTree *cursor;
+       DBTree *first;     /* databases */
+} DBContainer;
+
+
+
+
+/**
+ * Serialize a Key struct into DBT structs, one for key name
+ * (including ending NULL), another for the rest.
+ *
+ * Memory will be allocated for DBT.data part, so it is caller
+ * responsability to deallocate that latter.
+ *
+ */
+int keyToBDB(const Key *key, DBT *dbkey, DBT *dbdata) {
+       void *serialized;
+       size_t metaInfoSize;
+       int utf8Conversion=0, utf8CommentConverted=0, utf8ValueConverted = 0;
+       char *convertedName=key->key;
+       size_t sizeName=kdbiStrLen(key->key);
+       char *convertedValue=key->data;
+       size_t sizeValue=key->dataSize;
+       char *convertedComment=key->comment;
+       size_t sizeComment=key->commentSize;
+
+
+       /* First convert all to UTF-8 */
+       if ((utf8Conversion=kdbbNeedsUTF8Conversion())) {
+               if (key->key) {
+                       convertedName=malloc(sizeName);
+                       memcpy(convertedName,key->key,sizeName);
+                       kdbbUTF8Engine(UTF8_TO,&convertedName,&sizeName);
+               } else convertedName=key->key;
+
+               if (dbdata) {
+                       if (!keyIsBinary(key)) {
+                               convertedValue=malloc(sizeValue);
+                               memcpy(convertedValue,key->data,sizeValue);
+                               kdbbUTF8Engine(UTF8_TO,&convertedValue,&sizeValue);
+                               utf8ValueConverted = 1;
+                       } else convertedValue=key->data;
+                
+                       if (key->comment) {
+                               convertedComment=malloc(sizeComment);
+                               memcpy(convertedComment,key->comment,sizeComment);
+                               kdbbUTF8Engine(UTF8_TO,&convertedComment,&sizeComment);
+                               utf8CommentConverted = 1;
+                       } else convertedComment=key->comment;
+               }
+       } 
+       
+       if (dbdata) {
+               memset(dbdata, 0, sizeof(DBT));
+
+               metaInfoSize = KEY_METAINFO_SIZE(key);
+               
+               dbdata->size = metaInfoSize + sizeValue + sizeComment;
+               serialized = malloc(dbdata->size);
+               memset(serialized,0,dbdata->size);
+
+               /* First part: the metainfo */
+               memcpy(serialized,key,metaInfoSize);
+               /* *((Key *)serialized)=*key; */
+
+               /* Second part: the comment */
+               memcpy(serialized+metaInfoSize,convertedComment,sizeComment);
+               /* adjust comment size from UTF-8 conversion */
+               if (key->commentSize!=sizeComment)
+                       memcpy(serialized+metaInfoSize-
+                               sizeof(key->commentSize)-sizeof(key->dataSize),
+                               &sizeComment,sizeof(sizeComment));
+               
+               /* Third part: the value */
+               memcpy(serialized+metaInfoSize+sizeComment,convertedValue,sizeValue);
+               /* adjust value size from UTF-8 conversion */
+               if (key->dataSize!=sizeValue) 
+                       memcpy(serialized+metaInfoSize-sizeof(key->dataSize),
+                               &sizeValue,sizeof(sizeValue));
+       
+               dbdata->data=serialized;
+               
+               if (utf8CommentConverted )
+                       free(convertedComment);
+               if ( utf8ValueConverted )
+                       free(convertedValue);
+       }
+       
+       memset(dbkey, 0, sizeof(DBT));
+       if (utf8Conversion) {
+               dbkey->size=sizeName;
+               dbkey->data=convertedName;
+       } else {
+               dbkey->size=kdbiStrLen(key->key);
+               dbkey->data=malloc(dbkey->size);
+               strcpy(dbkey->data,key->key);
+       }
+       
+       return 0;
+}
+
+
+
+
+/**
+ * The oposite of keyToBDB.
+ * Will take 2 DBTs (one for key name, other for data) and convert them
+ * into a Key structure.
+ * 
+ * WARNING: key->owner must be set outside keyFromBDB(). Someplace more
+ * aware of the context. So everywhere keyFromBDB is called, a call
+ * to keySetOwner() should apper right after it.
+ */
+int keyFromBDB(Key *key, const DBT *dbkey, const DBT *dbdata) {
+       size_t metaInfoSize;
+
+       keyClose(key);
+       
+       metaInfoSize = KEY_METAINFO_SIZE(key);
+       
+       /* Set all metainfo */
+       memcpy(key,        /* destination */
+               dbdata->data,    /* source */
+               metaInfoSize);   /* size */
+       key->dataSize=dbdata->size;
+
+       /* Set comment */
+       if (key->commentSize)
+               keySetComment(key,dbdata->data+metaInfoSize);
+       
+       /* owner must be set outside this function,
+        * someplace more aware of the context */
+       keySetName(key,dbkey->data);
+
+       /* Set value. Key type came from the metaInfo importing above. */
+       keySetRaw(key,dbdata->data+metaInfoSize+key->commentSize,key->dataSize);
+       
+       if (kdbbNeedsUTF8Conversion()) {
+               size_t size=kdbiStrLen(key->key);
+               
+               kdbbUTF8Engine(UTF8_FROM,&key->key,&size);
+               kdbbUTF8Engine(UTF8_FROM,&key->comment,&key->commentSize);
+               if (!keyIsBinary(key))
+                       kdbbUTF8Engine(UTF8_FROM,(char **)&key->data, &key->dataSize);
+       }
+       
+       /* since we just got the key from the storage, it is synced. */
+       key->flags &= ~KEY_FLAG_SYNC;
+
+       return 0;
+}
+
+
+
+
+
+/**
+ * Calculates the secondary index for a key.
+ * In our DB layout, the secondary index is simply the parent of the key.
+ * This method is called everytime DB->get, DB->put etc BDB methods
+ * are called.
+ */
+int parentIndexCallback(DB *db, const DBT *rkey, const DBT *rdata, DBT *pkey) {
+       size_t baseNameSize,parentNameSize;
+       char *parentPrivateCopy=0;
+
+       baseNameSize=keyNameGetBaseNameSize(rkey->data);
+       if (baseNameSize == 0)
+               /* this is a root or empty key */
+               return DB_DONOTINDEX;
+
+       memset(pkey, 0, sizeof(DBT));
+
+       parentNameSize=rkey->size-baseNameSize;
+       parentPrivateCopy=malloc(parentNameSize);
+
+       if (parentPrivateCopy) {
+               memcpy(parentPrivateCopy,rkey->data,parentNameSize-1);
+               parentPrivateCopy[parentNameSize-1]=0;
+       }
+
+       pkey->data=parentPrivateCopy;
+       pkey->size=parentNameSize;
+       pkey->flags=DB_DBT_APPMALLOC;
+
+       return 0;
+}
+
+
+
+
+
+
+
+/**
+ * Closes databases, frees internal memory and destroys the
+ * DBTree data structure. It is the oposite of dbTreeNew().
+ */
+int dbTreeDel(DBTree *dbtree) {
+       if (dbtree->owner) free(dbtree->owner);
+       if (dbtree->db.keyValuePairs)
+               dbtree->db.keyValuePairs->close(dbtree->db.keyValuePairs,0);
+       if (dbtree->db.parentIndex)
+               dbtree->db.parentIndex->close(dbtree->db.parentIndex,0);
+       
+       free(dbtree);
+       
+       return 0;
+}
+
+
+
+
+/**
+ * Given a created, opened and empty DBTree, initialize its root key.
+ * This is usually called by dbTreeNew().
+ */
+int dbTreeInit(KDB *handle,DBTree *newDB) {
+       Key *root=0;
+       int ret;
+       DBT dbkey,data;
+
+       /* TODO: review security bits issues on daemon mode */
+       if (newDB->isSystem) {
+               root=keyNew("system",
+                       KEY_UID,0,
+                       KEY_GID,0,
+                       KEY_END);
+       } else {
+               struct passwd *userOwner;
+               userOwner=getpwnam(newDB->owner);
+               root=keyNew("user",
+                       KEY_UID,   kdbhGetUID(handle),
+                       KEY_GID,   kdbhGetGID(handle),
+                       KEY_DIR,
+                       KEY_END);
+       }
+
+       keySetDir(root);
+
+       root->atime=root->mtime=root->ctime=time(0); /* set current time */
+
+       keyToBDB(root,&dbkey,&data);
+
+       ret = newDB->db.keyValuePairs->put(newDB->db.keyValuePairs,
+               0,&dbkey,&data, 0);
+       if (!ret) printf("db: %s: DB Initialized.\n", (char *)dbkey.data);
+       else {
+               newDB->db.keyValuePairs->err(newDB->db.keyValuePairs, ret, "DB->put");
+               perror("DB->put");
+       }
+
+       keyDel(root);
+       free(dbkey.data); dbkey.data=0;
+       free(data.data); data.data=0;
+
+       newDB->db.keyValuePairs->sync(newDB->db.keyValuePairs,0);
+
+       return KDB_ERR_OK;
+}
+
+
+
+
+
+
+/**
+ * Tries to open a DB for a key.
+ * If it doesn't exist, try to create it.
+ * The returned new DBTree must be included in the static single
+ * DBContainer by the caller.
+ * The returned DBTree must be deleted later with dbTreeDel().
+ * 
+ * The DB location on the filesystem is something like this:
+ * 
+ * if (keyIsUser(forKey))
+ *     ~{keyGetOwner(forKey)}/.kdb-berkeleydb/{dbfiles}
+ * else
+ *     /etc/kdb-berkeleydb/{dbfiles}
+ */
+DBTree *dbTreeNew(KDB *handle,const Key *forKey) {
+       DBTree *newDB;
+       int ret;
+       int newlyCreated; /* True if this is a new database */
+       uid_t uid=0;
+       gid_t gid=0;
+       char dbDir[MAX_PATH_LENGTH];
+       char parentsFile[MAX_PATH_LENGTH];
+       char keyvalueFile[MAX_PATH_LENGTH];
+       struct passwd *user=0;
+
+       struct stat dbDirInfo;
+
+
+       /***********
+        * Calculate path and filenames for the DB files.
+        ***********/
+
+       if (keyIsSystem(forKey)) {
+               /* Prepare to open the 'system/ *' database */
+               strcpy(dbDir,DB_DIR_SYSTEM);
+               uid = 0;
+               gid = 0;
+       } else if (keyIsUser(forKey)) {
+               /* Prepare to open the 'user:????.*' database */
+               /* TODO: user should be calculated from handle */
+               user=getpwnam(forKey->owner);
+               sprintf(dbDir,"%s/%s",user->pw_dir,DB_DIR_USER);
+               uid = user->pw_uid;
+               gid = user->pw_gid;
+       }
+
+       if (stat(dbDir,&dbDirInfo)) {
+               /* Directory does not exist. create it */
+               int ret;
+               
+               fprintf(stderr,"Going to create dir %s\n",dbDir);
+               ret=mkdir(dbDir,DEFFILEMODE | S_IXUSR);
+               if (ret) return 0; /* propagate errno */
+               chown(dbDir,  uid, gid);
+       } else {
+               /* Something exist there. Check it first */
+               if (!S_ISDIR(dbDirInfo.st_mode)) {
+                       /* It is not a directory ! */
+                       errno=EACCES;
+                       return 0;
+               }
+       }
+
+       sprintf(keyvalueFile,"%s/%s",dbDir,DB_FILE_KEYVALUE);
+       sprintf(parentsFile,"%s/%s",dbDir,DB_FILE_PARENTS);
+
+       newDB=malloc(sizeof(DBTree));
+       memset(newDB,0,sizeof(DBTree));
+       newDB->isSystem=keyIsSystem(forKey);
+       newlyCreated=0;
+
+
+       /* We have the files names. Now open/create them */
+
+       /****************
+        * The main database. The one you can find the real key-value pairs
+        *****************/
+       if ((ret = db_create(&newDB->db.keyValuePairs, NULL, 0)) != 0) {
+               fprintf(stderr, "db_create: %s: %s\n", DB_KEYVALUE, db_strerror(ret));
+               free(newDB);
+               errno=KDB_ERR_EBACKEND;
+               return 0;
+       }
+       ret=newDB->db.keyValuePairs->open(newDB->db.keyValuePairs,NULL,keyvalueFile,
+               DB_KEYVALUE, DB_BTREE, DB_CREATE | DB_EXCL | DB_THREAD, 0);
+       if (ret == EEXIST || ret == EACCES) {
+               /* DB already exist. Only open it */
+               ret=newDB->db.keyValuePairs->open(newDB->db.keyValuePairs,NULL, keyvalueFile,
+                       DB_KEYVALUE, DB_BTREE, DB_THREAD, 0);
+               if (ret == EACCES)
+                       ret=newDB->db.keyValuePairs->open(newDB->db.keyValuePairs,NULL,
+                               keyvalueFile, DB_KEYVALUE, DB_BTREE, DB_THREAD | DB_RDONLY, 0);
+       } else newlyCreated=1;
+
+       
+       if (ret) {
+               newDB->db.keyValuePairs->err(newDB->db.keyValuePairs,
+                       ret, "%s", DB_KEYVALUE);
+               dbTreeDel(newDB);
+               errno=KDB_ERR_EBACKEND;
+               return 0;
+       }
+
+
+
+
+       /* TODO: Check newlyCreated also */
+       /****************
+        * The parent index. To make key searches by their parents
+        *****************/
+       ret=db_create(&newDB->db.parentIndex, NULL, 0);
+       if (ret != 0) {
+               fprintf(stderr, "db_create: %s: %s\n", DB_PARENTINDEX, db_strerror(ret));
+               dbTreeDel(newDB);
+               errno=KDB_ERR_EBACKEND;
+               return 0;
+       }
+       
+       ret = newDB->db.parentIndex->set_flags(newDB->db.parentIndex,
+               DB_DUP | DB_DUPSORT);
+       if (ret != 0) fprintf(stderr, "set_flags: %s: %d\n",DB_PARENTINDEX,ret);
+       
+       ret = newDB->db.parentIndex->open(newDB->db.parentIndex,
+               NULL, parentsFile, DB_PARENTINDEX, DB_BTREE, DB_CREATE | DB_EXCL | DB_THREAD, 0);
+       if (ret == EEXIST || ret == EACCES) {
+               /* DB already exist. Only open it */
+               ret=newDB->db.parentIndex->open(newDB->db.parentIndex,NULL, parentsFile,
+                       DB_PARENTINDEX, DB_BTREE, DB_THREAD, 0);
+               if (ret == EACCES)
+                       ret=newDB->db.parentIndex->open(newDB->db.parentIndex,NULL,
+                               parentsFile, DB_PARENTINDEX, DB_BTREE, DB_THREAD | DB_RDONLY, 0);
+       }
+       
+       if (ret) {
+               newDB->db.parentIndex->err(newDB->db.parentIndex, ret, "%s", DB_PARENTINDEX);
+               dbTreeDel(newDB); 
+               errno=KDB_ERR_EBACKEND;
+               return 0;
+       }
+       
+       ret = newDB->db.keyValuePairs->associate(newDB->db.keyValuePairs, NULL,
+               newDB->db.parentIndex, parentIndexCallback, DB_DBT_APPMALLOC);
+       if (ret != 0) {
+               fprintf(stderr, "error: %s: %d\n",DB_PARENTINDEX,ret);
+               dbTreeDel(newDB);
+               errno=KDB_ERR_EBACKEND;
+               return 0;
+       }
+
+
+
+
+       if (!newDB->isSystem) {
+               newDB->owner=malloc(kdbiStrLen(forKey->owner));
+               strcpy(newDB->owner,forKey->owner);
+       }
+
+       /* Set file permissions for the DB files */
+       if (newlyCreated) {
+               if (user) {
+                       chown(keyvalueFile,  user->pw_uid,user->pw_gid);
+                       chown(parentsFile,  user->pw_uid,user->pw_gid);
+               }
+               dbTreeInit(handle,newDB); /* populate */
+       }
+       return newDB;
+}
+
+
+
+
+
+
+
+
+
+/**
+ * Return the DB suitable for the key.
+ * Lookup in the list of opened DBs (DBContainer). If not found, tries to
+ * open it with dbTreeNew().
+ * Key name and user domain will be used to find the correct database.
+ */
+DBTree *getDBForKey(KDB *handle, const Key *key) {
+       DBContainer *dbs=kdbhGetBackendData(handle);
+       DBTree *current,*newDB;
+       char rootName[100];
+       rootName[0]=0; /* just to be sure... */
+
+       if (dbs->cursor) current=dbs->cursor;
+       else current=dbs->cursor=dbs->first;
+       
+       /* We found some DB opened.
+        * Browse it starting from the cursor. */
+       if (current) {
+               /* Look for a DB in our opened DBs */
+               if (keyIsSystem(key))
+                       do {
+                               if (current->isSystem) return dbs->cursor=current;
+                       
+                               current=current->next;
+                               if (!current) current=dbs->first;
+                       } while (current && current!=dbs->cursor);
+               else if (keyIsUser(key)) {
+                       /* If key is a user key, it can't have an empty owner */
+                       if (key->owner == 0) return 0;
+                       do {
+                               if (!current->isSystem && !strcmp(key->owner,current->owner))
+                                       return dbs->cursor=current;
+                               
+                               current=current->next;
+                               if (!current) current=dbs->first;
+                       } while (current && current!=dbs->cursor);
+               }
+       }
+       
+       /* If we reached this point, the DB for our key is not in our container.
+        * Open it and include in the container. */
+
+       newDB=dbTreeNew(handle,key);
+       if (newDB) {
+               /* Put the new DB right after the container's current DB (cursor).
+                * And set the cursor to be the new DB. */
+               if (dbs->cursor) {
+                       newDB->next=dbs->cursor->next;
+                       dbs->cursor->next=newDB;
+                       dbs->cursor=newDB;
+               } else dbs->cursor=dbs->first=newDB;
+               dbs->size++;
+       }
+       
+       /* If some error ocurred inside dbTreeNew(), errno will be propagated */
+       
+       return dbs->cursor;
+}
+
+
+
+
+
+
+
+/*************************************************
+ * Interface Implementation
+ *************************************************/
+
+
+
+
+
+
+
+
+
+/**
+ * Implementation for kdbRemoveKey() method.
+ *
+ * @see kdbRemove() for expected behavior.
+ * @ingroup backend
+ */
+int kdbRemoveKey_bdb(KDB *handle, const Key *key) {
+       DBContainer *dbs;
+       DBTree *dbctx;
+       DBT dbkey,data;
+       int ret;
+       uid_t user=kdbhGetUID(handle);
+       gid_t group=kdbhGetGID(handle);
+       int canWrite=0;
+       Key *cast=0;
+       
+       dbs=kdbhGetBackendData(handle);
+       
+       dbctx=getDBForKey(handle,key);
+       if (!dbctx) return 1; /* propagate errno from getDBForKey() */
+       
+       /* First check if we have write permission to the key */
+       memset(&dbkey,0,sizeof(DBT));
+       memset(&data,0,sizeof(DBT));
+       dbkey.size=dbkey.ulen=kdbiStrLen(key->key);
+       dbkey.data=key->key;
+       data.flags=DB_DBT_REALLOC;
+       
+       ret = dbctx->db.keyValuePairs->get(dbctx->db.keyValuePairs,
+               NULL, &dbkey, &data, 0);
+               
+       if (ret == DB_NOTFOUND) return errno=KDB_ERR_NOTFOUND;
+       
+       if (ret == 0) {
+               cast=(Key *)data.data;
+               
+               /* Check parent permissions to write bellow it. */
+               if (cast->uid == user)
+                       canWrite = cast->mode & S_IWUSR;
+               else if (cast->gid == group)
+                       canWrite = cast->mode & S_IWGRP;
+               else canWrite= cast->mode & S_IWOTH;
+       }
+       
+       free(data.data);
+       
+       if (! canWrite) return errno=KDB_ERR_NOCRED;
+
+       /* Ok, so we can delete the key */
+       
+       ret=dbctx->db.keyValuePairs->del(dbctx->db.keyValuePairs,
+               NULL, &dbkey, 0);
+       
+       switch (ret) {
+               case 0:
+                       return ret; /* success */
+                       break;
+               case EACCES:
+                       return errno=KDB_ERR_NOCRED;
+                       break;
+               default:
+                       dbctx->db.keyValuePairs->err(dbctx->db.keyValuePairs, ret, "DB->del");
+       }
+       
+       return ret;
+}
+
+
+int kdbGetKeyWithOptions(KDB *handle, Key *key, uint32_t options) {
+       DBContainer *dbs;
+       DBTree *dbctx;
+       DBT dbkey,data;
+       int ret;
+       uid_t user=kdbhGetUID(handle);
+       gid_t group=kdbhGetGID(handle);
+       int canRead=0;
+       int isLink=0;
+       Key *buffer = keyNew(0);;
+
+       dbs=kdbhGetBackendData(handle);
+       
+       dbctx=getDBForKey(handle,key);
+       if (!dbctx) return 1; /* propagate errno from getDBForKey() */
+
+       keyInit(buffer);
+       memset(&dbkey,0,sizeof(DBT));
+       memset(&data,0,sizeof(DBT));
+       dbkey.size=dbkey.ulen=kdbiStrLen(key->key);
+       dbkey.data=key->key;
+       data.flags=DB_DBT_REALLOC;
+
+       ret = dbctx->db.keyValuePairs->get(dbctx->db.keyValuePairs,
+               NULL, &dbkey, &data, 0);
+               
+       switch (ret) {
+               case 0: { /* Key found and retrieved. Check permissions */
+                       keyFromBDB(buffer,&dbkey,&data);
+                       if (keyIsUser(buffer)) keySetOwner(buffer,dbctx->owner);
+
+                       dbkey.data=0;
+                       free(data.data); data.data=0;
+                       
+                       /* End of BDB specific code in this method */
+                       
+                       
+                       /* Check permissions. */
+                       if (keyGetUID(buffer) == user)
+                               canRead = keyGetMode(buffer) & S_IRUSR;
+                       else if (keyGetGID(buffer) == group)
+                               canRead = keyGetMode(buffer) & S_IRGRP;
+                       else canRead = keyGetMode(buffer) & S_IROTH;
+
+                       if (!canRead) {
+                               keyClose(buffer);
+                               return errno=KDB_ERR_NOCRED;
+                       }
+                       break;
+               }
+               case DB_NOTFOUND:
+                       return errno=KDB_ERR_NOTFOUND;
+                       break;
+       }
+
+       isLink=keyIsLink(buffer);
+       
+       if (canRead) {
+               /* TODO: check if ok?
+               if (!isLink && (options & KDB_O_STATONLY))
+                       keySetRaw(buffer,0,0);
+               */
+               if (isLink && !(options & KDB_O_NFOLLOWLINK)) {
+                       /* If we have a link and user did not specify KDB_O_NFOLLOWLINK,
+                        * he want to dereference the link */
+                       Key *target = keyNew(0);
+                       
+                       keyInit(target);
+                       keySetName(target,buffer->data);
+
+                       if (kdbGetKeyWithOptions(handle,target, options) ==
+                                       KDB_ERR_NOTFOUND) {
+                               keyDel(target);
+                               keyDel(buffer);
+                               return errno=KDB_ERR_NOTFOUND;
+                       }
+               }
+       }
+       key = keyDup(buffer);
+       keyDel(buffer);
+       
+       return KDB_ERR_OK; /* success */
+}
+
+
+
+
+
+int kdbGetKey_bdb(KDB *handle, Key *key) {
+       return kdbGetKeyWithOptions(handle,key,0);
+}
+
+
+
+int kdbStatKey_bdb(KDB *handle, Key *key) {
+       return kdbGetKeyWithOptions(handle,key,0);
+}
+
+
+
+/**
+ * Implementation for kdbSetKey() method.
+ *
+ * @see kdbSetKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbSetKey_bdb(KDB *handle, Key *key) {
+       DBTree *dbctx;
+       DBT dbkey,data;
+       int ret;
+       uid_t user=kdbhGetUID(handle);
+       gid_t group=kdbhGetGID(handle);
+       int canWrite=0;
+
+       dbctx=getDBForKey(handle,key);
+       if (!dbctx) return 1; /* propagate errno from getDBForKey() */
+
+       /* Check mode permissions.
+          Check if this client can commit this key to the database */
+
+       memset(&dbkey,0,sizeof(DBT));
+       memset(&data,0,sizeof(DBT));
+       dbkey.size=dbkey.ulen=kdbiStrLen(key->key);
+       dbkey.data=key->key;
+       dbkey.flags=data.flags=DB_DBT_REALLOC;
+
+       ret = dbctx->db.keyValuePairs->get(dbctx->db.keyValuePairs,
+               NULL, &dbkey, &data, 0);
+               
+       switch (ret) {
+               case 0: { /* Key found and retrieved. Check permissions */
+                       Key *cast;
+                       
+                       cast=(Key *)data.data;
+                       
+                       /* Check parent permissions to write bellow it. */
+                       if (cast->uid == user)
+                               canWrite = cast->mode & S_IWUSR;
+                       else if (cast->gid == group)
+                               canWrite = cast->mode & S_IWGRP;
+                       else canWrite= cast->mode & S_IWOTH;
+                       
+                       /* cleanup */
+                       dbkey.data=0;
+                       free(data.data); data.data=0;
+
+                       /* keyClose(&buffer); */
+                       break;
+               }
+               case DB_NOTFOUND: {
+                       /* We don't have this key yet.
+                          Check if we have a parent and its permissions. */
+                       Key *parent=0;
+                       size_t parentNameSize;
+                       char *parentName;
+
+                       parentNameSize=keyGetParentNameSize(key);
+                       parentName=malloc(parentNameSize);
+                       keyGetParentName(key,parentName,parentNameSize);
+                       
+                       memset(&dbkey,0,sizeof(DBT));
+                       memset(&data,0,sizeof(DBT));
+                       dbkey.data=parentName;
+                       dbkey.size=parentNameSize;
+                       dbkey.flags=data.flags=DB_DBT_REALLOC;
+
+                       ret = dbctx->db.keyValuePairs->get(dbctx->db.keyValuePairs, NULL,
+                               &dbkey, &data, 0);
+
+                       if (ret == DB_NOTFOUND) {
+                               /* No, we don't have a parent. Create dirs recursivelly */
+                               
+                               parent=keyNew(0);
+                               
+                               /* explicitly set these attributes from the handle cause we
+                                * could be running under a daemon context */
+                               keySetUID(parent,user);
+                               keySetGID(parent,group);
+                               keySetDir(parent);
+                               
+                               /* Next block exist just to not call 
+                                * keySetName(), a very expensive method.
+                                * This is a not-recomended hack. */
+                               parent->key=parentName;
+                               parent->owner=key->owner;
+                               
+                               /* free(parentName); */
+                               
+                               if (kdbSetKey_bdb(handle,parent)) {
+                                       /* If some error happened in this recursive call.
+                                        * Propagate errno.
+                                        */
+                                       
+                                       /* disassociate our hack for deletion */
+                                       parent->owner=0;
+                                       
+                                       /* parentName will be free()d here too */
+                                       keyDel(parent);
+                                       
+                                       return 1;
+                               }
+                               
+                               /* disassociate our hack for latter deletion */
+                               parent->owner=0;
+                               
+                               /* data.data enters and quits this block empty */
+                       } else {
+                               /* Yes, we have a parent already. */
+                               /*parent=keyNew(0);
+                               keyFromBDB(parent,&dbkey,&data);
+                               keySetOwner(parent,dbctx->owner);
+                               
+                               free(data.data);
+                               */
+                               
+                               /* we don't need it anymore */
+                               free(parentName);
+                               
+                               /* we are only interested in some metainfo, so just cast it */
+                               parent=(Key *)data.data;
+                       }
+
+                       /* Check parent permissions to write bellow it. */
+                       if (parent->uid == user)
+                               canWrite = parent->mode & S_IWUSR;
+                       else if (parent->gid == group)
+                               canWrite = parent->mode & S_IWGRP;
+                       else canWrite= parent->mode & S_IWOTH;
+                       
+                       if (data.data) free(data.data);
+                       
+                       if (parent == (Key *)data.data) parent = 0;
+                       else if (parent) keyDel(parent);
+                       
+                       break;
+               } /* case DB_NOTFOUND */
+       } /* switch */
+
+       if (! canWrite) return errno=KDB_ERR_NOCRED;
+
+       key->mtime=key->atime=time(0); /* set current time into key */
+       keyToBDB(key,&dbkey,&data);
+
+       if ((ret = dbctx->db.keyValuePairs->put(dbctx->db.keyValuePairs,
+                       NULL, &dbkey, &data, 0)) != 0) {
+               dbctx->db.keyValuePairs->err(dbctx->db.keyValuePairs, ret, "DB->put");
+               
+               free(dbkey.data); dbkey.data=0;
+               free(data.data); data.data=0;
+
+               errno=KDB_ERR_NOCRED; /* probably this is the error */
+               return 1;
+       }
+
+       free(dbkey.data); dbkey.data=0;
+       free(data.data); data.data=0;
+
+       /* Mark the key as synced */
+       key->flags &= ~KEY_FLAG_SYNC;
+
+       /*
+       dbctx->db.keyValuePairs->sync(dbctx->db.keyValuePairs,0);
+       dbctx->db.parentIndex->sync(dbctx->db.parentIndex,0);
+       */
+       return 0; /* success */
+}
+
+
+
+/**
+ * Implementation for kdbGetKeyChildKeys() method.
+ *
+ * @see kdbGetKeyChildKeys() for expected behavior.
+ * @ingroup backend
+ */
+ssize_t kdbGetKeyChildKeys_bdb(KDB *handle, const Key *parentKey,
+               KeySet *returned, unsigned long options) {
+       DBTree *db=0;
+       DBC *cursor=0;
+       DBT parent,keyName,keyData;
+       Key *currentParent, *retrievedKey;
+       KeySet *folders;
+       uid_t user=kdbhGetUID(handle);
+       gid_t group=kdbhGetGID(handle);
+       mode_t canRead=0; /* wether we have permissions to go ahead */
+       int ret=0;
+       
+       /* Get/create the DB for the parent key */
+       db=getDBForKey(handle,parentKey);
+
+       if (db == 0) {
+               /* Bizarre sitution when a DB (existing or newly creted) can't be
+                  associted with the passed key. This is unacceptable and trated as
+                  as INVALID, because all DBs are calculated from key name.
+               */
+               errno=KDB_ERR_INVALIDKEY;
+               return -1;
+       }
+
+       currentParent=keyNew(0);
+       parentKey = keyDup(currentParent);
+       ret=kdbGetKeyWithOptions(handle,currentParent,0);
+
+       if (ret==KDB_ERR_NOTFOUND) {
+               keyDel(currentParent);
+               errno=KDB_ERR_NOTFOUND;
+               return -1;
+       }
+       
+       if (! keyIsDir(currentParent)) {
+               /* TODO: dereference link keys */
+               keyDel(currentParent);
+               errno=ENOTDIR;
+               return -1;
+       }
+
+       /* Check master parent permissions from DB */
+       if (currentParent->uid == user)
+               canRead = currentParent->mode & (S_IRUSR | S_IXUSR);
+       else if (currentParent->gid == group)
+               canRead = currentParent->mode & (S_IRGRP | S_IXGRP);
+       else canRead = currentParent->mode & (S_IROTH | S_IXOTH);
+       
+       keyDel(currentParent);
+       
+       if (!canRead) return errno=KDB_ERR_NOCRED;
+
+       /* initialize the KeySet that will hold the fetched folders */
+       folders = ksNew(0);
+       
+       /* initialize a cursor to walk through each key under a folder */
+       ret = db->db.parentIndex->cursor(db->db.parentIndex, NULL, &cursor, 0);
+       
+       currentParent=(Key *)parentKey;
+       
+       /*
+        * Each loop pass reads all keys from a single folder, without recursion.
+        * Recursion is provided by multiple passes on this loop
+        */
+       do {
+               memset(&parent,0,sizeof(parent));
+               memset(&keyData,0,sizeof(keyData));
+               memset(&keyName,0,sizeof(keyName));
+               
+               /* Memory will be allocated now for the DBTs.... */
+               keyToBDB((const Key *)currentParent,&parent,0);
+       
+               /* Let BDB allocate memory for next key name and data */
+               keyName.flags=keyData.flags=DB_DBT_REALLOC;
+               
+               /* position the cursor in the first key of "parent" folder
+                  and retrieve it */
+               ret=cursor->c_pget(cursor,
+                               &parent,
+                               &keyName,
+                               &keyData,
+                               DB_SET);
+               
+               if (ret == DB_NOTFOUND) {
+                       /* We are probably in a root key, that
+                        * doesn't have any subentry in the index. */
+                       free(parent.data);
+                       free(keyName.data);
+                       free(keyData.data);
+                       break;
+                       /*return KDB_ERR_NOTFOUND; */
+               }
+               
+               /* Now start retrieving all child keys */
+               do { /* next cursor move is in the ending "while" */
+               
+                       /* Check if is inactive before doing higher level operations */
+                       /*
+                       if (!(options & KDB_O_INACTIVE)) {
+                               char *sep;
+                               
+                               * If we don't want inactive keys, check if its inactive *
+                               * TODO: handle escaping *
+                               sep=strrchr((char *)keyName.data,RG_KEY_DELIM);
+                               if (sep && sep[1] == '.') {
+                                       * This is an inactive key, and we don't want it *
+                                       * Ignore this key, free all, and continue *
+                                       
+                                       free(keyName.data); free(keyData.data);
+                                       memset(&keyName,0,sizeof(keyName));
+                                       memset(&keyData,0,sizeof(keyData));
+                                       keyName.flags=keyData.flags=DB_DBT_REALLOC;
+               
+                                       * fetch next *
+                                       continue;
+                               }
+                       }
+                       */
+               
+                       retrievedKey=keyNew(0);
+                       keyFromBDB(retrievedKey,&keyName,&keyData);
+                       if (keyIsUser(retrievedKey)) keySetOwner(retrievedKey,db->owner);
+               
+                       free(keyName.data); free(keyData.data);
+                       memset(&keyName,0,sizeof(keyName));
+                       memset(&keyData,0,sizeof(keyData));
+                       keyName.flags=keyData.flags=DB_DBT_REALLOC;
+               
+                       /* End of BDB specific code, ready for next c_pget() */
+               
+                       /* check permissions for this key */
+                       canRead=0;
+                       /*
+                       if (options & KDB_O_STATONLY) {
+                               if (!keyIsLink(retrievedKey)) keySetRaw(retrievedKey,0,0);
+                               canRead=1;
+                       } else {
+                       */
+                               /* If caller wants the value, comment, etc... */
+                               canRead=0;
+                               if (retrievedKey->uid == user) {
+                                       canRead = (retrievedKey->mode & S_IRUSR);
+                               } else if (retrievedKey->gid == group) {
+                                       canRead = (retrievedKey->mode & S_IRGRP);
+                               } else canRead = (retrievedKey->mode & S_IROTH);
+                       /*}*/
+               
+                       if (!canRead) {
+                               keyDel(retrievedKey);
+                               continue;
+                       }
+               
+                       
+                       if (keyIsLink(retrievedKey) && !(options & KDB_O_NFOLLOWLINK)) {
+                       /* If we have a link and user did not specify KDB_O_NFOLLOWLINK,
+                        * means he wants to dereference the link */
+                               Key *target = keyNew (0);
+                       
+                               keySetName(target,retrievedKey->data);
+
+                               if (kdbGetKeyWithOptions(handle,target, options) ==
+                                               KDB_ERR_NOTFOUND) {
+                                       /* Invalid link target, so don't include in keyset */
+                               
+                                       keyDel(target);
+                               
+                                       errno=KDB_ERR_NOTFOUND;
+                                       /* fetch next */
+                                       continue;
+                               } else {
+                                       target = keyDup(retrievedKey);
+                                       keyDel (target);
+                               }
+                       }
+               
+                       /*
+                       if (keyIsDir(retrievedKey)) {
+                               if (options & KDB_O_RECURSIVE) {
+                                       ksAppendKey(folders,retrievedKey);
+                               } else if (options & KDB_O_DIR) {
+                                       ksAppendKey(returned,retrievedKey);
+                               } else keyDel(retrievedKey);
+                       } else if (options & KDB_O_DIRONLY) {
+                               * If key isn't a dir, and user only wants dirs... *
+                               keyDel(retrievedKey);
+                               retrievedKey=0;
+                       } else
+                       */
+                       ksAppendKey(returned, retrievedKey);
+               } while (0==(ret=cursor->c_pget(cursor,&parent,&keyName,&keyData,DB_NEXT_DUP)));
+       } while ((currentParent=ksNext(folders)));
+       
+       /* At this point we have all keys we want. Make final adjustments. */
+       
+       /*
+       if (options & KDB_O_DIR)
+       */
+               ksAppend(returned,folders);
+       
+       ksDel(folders);
+
+       /*
+       if ((options & (KDB_O_SORT)) && (returned->size > 1))
+               ksSort(returned);
+       */
+       
+       cursor->c_close(cursor);
+       
+       return returned->size;
+}
+
+
+int kdbOpen_berkeleydb(KDB *handle) {
+       KDBCap *cap = kdbhGetCapability (handle);
+       /* Create only the DB container.
+        * DBs will be allocated on demand
+        */
+       DBContainer *dbs;
+
+       cap->onlyFullGet=1;
+       cap->noStat=1;
+
+       cap->onlyRemoveAll=1;
+
+       cap->onlyFullSet=1;
+       cap->onlyAddKeys=1;
+
+       cap->onlySystem=1;
+       cap->onlyUser=1;
+
+       cap->noOwner=1;
+       cap->noValue=1;
+       cap->noComment=1;
+       cap->noUID=1;
+       cap->noGID=1;
+       cap->noMode=1;
+       cap->noDir=1;
+       cap->noATime=1;
+       cap->noMTime=1;
+       cap->noCTime=1;
+       cap->noRemove=1;
+       cap->noLink=1;
+       cap->noMount=1;
+       cap->noBinary=1;
+       cap->noString=1;
+       cap->noTypes=1;
+       cap->noError=1;
+
+       cap->noLock=1;
+       cap->noThread=1;
+
+       
+       dbs=malloc(sizeof(DBContainer));
+       memset(dbs,0,sizeof(DBContainer));
+       
+       kdbhSetBackendData(handle,dbs);
+       
+       return 0;
+}
+
+int kdbClose_berkeleydb(KDB *handle) {
+
+       /* free all backend resources and shut it down */
+       DBContainer *dbs;
+       
+       dbs=kdbhGetBackendData(handle);
+       
+       if (dbs) {
+               while (dbs->first) {
+                       dbs->cursor=dbs->first;
+                       dbs->first=dbs->cursor->next;
+
+                       dbTreeDel(dbs->cursor);
+               }
+               free(dbs); dbs=0;
+       }
+
+       return 0; /* success */
+}
+
+ssize_t kdbGet_berkeleydb(KDB *handle, KeySet *returned, const Key *parentKey) {
+       ssize_t nr_keys = 0;
+
+       /* get all keys below parentKey and count them with nr_keys */
+       nr_keys = kdbGetKeyChildKeys_bdb(handle, parentKey, returned, 0);
+
+       return nr_keys; /* success */
+}
+
+ssize_t kdbSet_berkeleydb(KDB *handle, KeySet *returned, const Key *parentKey) {
+       ssize_t nr_keys = 0;
+       Key *current=ksCurrent(returned);
+
+       /* set all keys below parentKey and count them with nr_keys */
+
+       if (!current) current=ksNext(returned);
+       while (current) {
+               if (keyNeedRemove(current))
+               {
+                       if (kdbRemoveKey_bdb (handle, current))
+                               return -1;
+                       // TODO: should key be removed?
+               }
+               else if (keyNeedSync(current))
+               {
+                       if (kdbSetKey_bdb (handle,current)) /* check error */
+                               return -1;
+               }
+               current=ksNext(returned);
+       }
+
+       return nr_keys;
+}
+
+KDBEXPORT(berkeleydb)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_berkeleydb,
+               KDB_BE_CLOSE,   &kdbClose_berkeleydb,
+               KDB_BE_GET,     &kdbGet_berkeleydb,
+               KDB_BE_SET,     &kdbSet_berkeleydb,
+               KDB_BE_VERSION,        BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Full Name <email@libelektra.org>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION,
+                       "Add description here",
+               KDB_BE_END);
+}
+
diff --git a/src/backends/daemon/Makefile.am b/src/backends/daemon/Makefile.am
new file mode 100644 (file)
index 0000000..b80bd02
--- /dev/null
@@ -0,0 +1,38 @@
+
+# $Id$
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+noinst_LIBRARIES = libelektra-daemon.a 
+noinst_LTLIBRARIES = libproto.la
+
+libproto_la_SOURCES = message.h message.c protocol.h protocol.c ipc.h ipc.c sig.h sig.c serial.h serial.c serial_int.h serial_int.c serial_string.h serial_string.c serial_key.h serial_key.c serial_keyset.h serial_keyset.c datatype.h
+
+libelektra_daemon_a_SOURCES = daemon.c ../../include/kdb.h ../../include/kdbbackend.h $(libproto_la_SOURCES)
+libelektra_daemon_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+
+backend_LTLIBRARIES = libelektra-daemon.la
+libelektra_daemon_la_SOURCES = daemon.c
+libelektra_daemon_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_daemon_la_LIBADD = ../../libelektra/libelektra.la libproto.la
+libelektra_daemon_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+sbin_PROGRAMS = kdbd
+# kdbd_static
+kdbd_SOURCES = main.c kdbd.h kdbd.c kdb_wrapper.h kdb_wrapper.c thread.h thread.c
+kdbd_CFLAGS = -DDEFAULT_DBACKEND=\"ddefault\"
+kdbd_LDADD = libproto.la ../../libelektra/libelektra.la -lpthread $(LIBICONV)
+
+# kdbd_static_SOURCES = main.c kdbd.g kdbd.c kdb_wrapper.h kdb_wrapper.c thread.h thread.c
+# kdbd_static_CFLAGS = -DELEKTRA_STATIC -DDEFAULT_DBACKEND=\"$(default_dbackend)\"
+# kdbd_static_LDADD = @privatelibs@ libproto.la ../../libelektra/libelektra.a -lpthread
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
+
+# ../../libelektra/libelektra.a:
+#      cd ../../libelektra/ && $(MAKE) libelektra.a
+
+
+
diff --git a/src/backends/daemon/daemon.c b/src/backends/daemon/daemon.c
new file mode 100644 (file)
index 0000000..b914af1
--- /dev/null
@@ -0,0 +1,753 @@
+/***************************************************************************
+            daemon.c  -  Backend that makes RPCs to a kdb daemon
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2005 by Yannick Lecaillez
+    email                : yl@itioweb.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include "kdbbackend.h"
+#include "kdbprivate.h"
+
+
+/* Backend specific includes */
+
+#include "datatype.h"
+#include "protocol.h"
+#include "message.h"
+
+#include "ipc.h"
+#include "sig.h"
+
+
+#define BACKENDNAME "daemon"
+
+#define REPLY_TIMEOUT  5
+
+#ifndef SOCKET_NAME
+#define SOCKET_NAME "/var/run/kdbd/elektra.sock"
+#endif
+
+/**Some systems have even longer pathnames */
+#ifdef PATH_MAX
+#define MAX_PATH_LENGTH PATH_MAX
+/**This value is garanteed on any Posix system */
+#elif __USE_POSIX
+#define MAX_PATH_LENGTH _POSIX_PATH_MAX
+#else 
+#define MAX_PATH_LENGTH 4096
+#endif
+
+typedef struct {
+       int     socketfd;
+} DaemonBackendData;
+
+static Message *callDaemon(int socketfd, Message *request)
+{
+       Message *reply;
+       int ret;
+       
+       assert(request != NULL);
+
+       if ( protocolSendMessage(socketfd, request) == -1 ) {
+               fprintf(stderr, "callDaemon(): Error sending request\n");
+               return NULL;
+       }
+
+       /* Wait for a reply for 5 secondes */
+       reply = protocolReadMessage(socketfd);
+       if ( reply == NULL ) {
+               fprintf(stderr, "callDaemon(): Error reading message\n");
+               messageDel(reply);
+               return NULL;
+       }
+
+       /* Check for Internal error */
+       if ( messageGetProcedure(reply) == INTERNAL_ERROR ) {
+               messageExtractArgs(reply, DATATYPE_INTEGER, &ret);
+               fprintf(stderr, "callDaemon(): An error occured in kdbd: %d.\n", ret);
+               messageDel(reply);
+               return NULL;
+       }
+
+       return reply;
+}
+
+/**
+ * Initialize the backend.
+ * This is the first method kdbOpenBackend() calls after dynamically loading
+ * the backend library.
+ *
+ * This method is responsible of:
+ * - backend's specific configuration gathering
+ * - all backend's internal structs initialization
+ * - initial setup of all I/O details such as opening a file, connecting to a
+ *   database, etc
+ *
+ * @return 0 on success, anything else otherwise.
+ * @see kdbOpenBackend()
+ * @see kdbOpen()
+ * @ingroup backend
+ */
+int kdbOpen_daemon(KDB *handle) {
+       DaemonBackendData       *data;
+       Message *request, *reply;
+       unsigned long umask;
+       char    *real_backend,*tmp;
+       int ret;
+
+       data = (DaemonBackendData *) malloc(sizeof(DaemonBackendData));
+       if ( data == NULL )
+               return 1;
+       memset(data, 0, sizeof(DaemonBackendData));
+
+       sig_ignore(sig_pipe);
+       
+       data->socketfd = ipc_stream();
+       if ( data->socketfd == -1 ) {
+               perror("libelektra-daemon");
+               free(data);
+               return 1;
+       }
+       if ( ipc_connect(data->socketfd, SOCKET_NAME) == -1 ) {
+               perror("libelektra-daemon");
+               close(data->socketfd);
+               free(data);
+               return 1;
+       }
+       ndelay_off(data->socketfd);
+
+       /* Prepare request */
+       umask = kdbhGetUMask(handle);
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_OPEN, 
+                                       DATATYPE_STRING, kdbhGetUserName(handle),
+                                       DATATYPE_ULONG, &umask,
+                                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               fprintf(stderr, "Error building request\n");
+               close(data->socketfd);
+               free(data);
+               messageDel(request);
+               return 1;
+       }
+
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               close(data->socketfd);
+               free(data);
+               return 1;
+       }
+       
+       if ( messageExtractArgs(reply, 
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_STRING, &real_backend,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting args\n");
+               close(data->socketfd);
+               messageDel(reply);
+               free(real_backend);
+               free(data);
+               return 1;
+       }
+       
+       /* Get the backend name being used by the daemon from the reply... */
+       tmp=malloc(strlen(BACKENDNAME) + 1 + kdbiStrLen(real_backend));
+       sprintf(tmp,BACKENDNAME"+%s",real_backend);
+       kdbhSetBackendName(handle, tmp);
+       free(real_backend);
+       
+       messageDel(reply);
+       
+       kdbhSetBackendData(handle, data);
+
+       return ret;
+}
+
+
+
+
+/**
+ * All finalization logic of the backend should go here.
+ * 
+ * Called prior to unloading the backend dynamic module. Should ensure that no
+ * functions or static/global variables from the module will ever be accessed again.
+ * Should free any memory that the backend no longer needs.
+ * After this call, libelektra.so will unload the backend library, so this is
+ * the point to shutdown any affairs with the storage.
+ *
+ * @return 0 on success, anything else otherwise.
+ * @see kdbClose()
+ * @ingroup backend
+ */
+int kdbClose_daemon(KDB *handle)
+{
+       DaemonBackendData *data;
+       Message *request, *reply;
+       int     ret = 0;
+
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 0;
+       
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_CLOSE,
+                               DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbClose_daemon");
+               return 1;
+       }
+       
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               kdbhSetBackendData(handle, NULL);
+               close(data->socketfd); 
+               free(data);
+               return 1;
+       }
+       
+       /* Get reply value */
+       if ( messageExtractArgs(reply, DATATYPE_INTEGER, &ret, DATATYPE_INTEGER, &errno, DATATYPE_LAST) == -1 ) {
+               kdbhSetBackendData(handle, NULL);
+               close(data->socketfd);
+               free(data);
+               messageDel(reply);
+               return -1;
+       } 
+       messageDel(reply);
+
+       kdbhSetBackendData(handle, NULL);
+       close(data->socketfd);
+       free(data);
+       
+       return ret;
+}
+
+/**
+ * Implementation for kdbStatKey() method.
+ *
+ * This method is responsible of:
+ * - make necessary I/O to retrieve @p key->name's metadata
+ * - fill the @p key struct with its metadata
+ *
+ * @see kdbStatKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbStatKey_daemon(KDB *handle, Key *key) 
+{
+       DaemonBackendData *data;
+       Message *request, *reply;
+       int     ret;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL ) 
+               return -1;
+       
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_STATKEY,
+                       DATATYPE_KEY, key,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbStatKey_daemon");
+               return -1;
+       }
+
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               return -1;
+       }
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEY, key,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return 1;
+       }
+       messageDel(reply);
+
+       return ret;
+}
+
+
+/**
+ * Implementation for kdbGetKey() method.
+ *
+ * This method is responsible of:
+ * - make necessary I/O to retrieve all @p key->name's value and metadata
+ * - fill the @p key struct with its value and metadata
+ *
+ * @see kdbGetKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbGetKey_daemon(KDB *handle, Key *key)
+{
+       DaemonBackendData       *data;
+       Message         *request, *reply;
+       int             ret;
+
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL ) 
+               return 1;
+       
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_GETKEY,
+                       DATATYPE_KEY, key,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbGetKey_daemon");
+               return -1;
+       }
+
+       reply = callDaemon(data->socketfd, request);    
+       if ( reply == NULL ) {
+               return -1;
+       }
+
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEY, key,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+
+       return ret;
+}
+
+/**
+ * Implementation for kdbSetKey() method.
+ *
+ * This method is responsible of:
+ * - check the existence of @p key->name on persistent storage
+ * - prepare the backend to receive a new or updated key
+ * - use value and metadata from @p key to store them in the backend storage
+ * - fill the @p key struct with its value and metadata
+ *
+ * @see kdbSetKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbSetKey_daemon(KDB *handle, Key *key)
+{
+       DaemonBackendData *data;
+       Message *request, *reply;
+       int ret;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+      
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_SETKEY,
+                       DATATYPE_KEY, key,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbSetKey_daemon");
+               return -1;
+       }
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               return 1;
+       }
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEY, key,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+       
+       return ret;     
+}
+
+
+
+/**
+ * Implementation for kdbRename() method.
+ *
+ * @see kdbRename() for expected behavior.
+ * @ingroup backend
+ */
+int kdbRename_daemon(KDB *handle, Key *key, const char *newName)
+{
+       DaemonBackendData *data;
+       Message *request, *reply;
+       int ret;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+       
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_RENAME,
+                       DATATYPE_KEY, key,
+                       DATATYPE_STRING, newName,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbRename_daemon");
+               return -1;
+       }
+
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {  
+               return 1;
+       }
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEY, key,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+
+        return ret;    
+}
+
+
+
+
+/**
+ * Implementation for kdbRemoveKey() method.
+ *
+ * @see kdbRemove() for expected behavior.
+ * @ingroup backend
+ */
+int kdbRemoveKey_daemon(KDB *handle, const Key *key)
+{
+       Key     copy;
+       DaemonBackendData *data;
+       Message *request, *reply;
+       int ret;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+       
+       /* Prepare request */
+       keyInit(&copy);
+       if ( keyDup(key, &copy) ) {
+               keyClose(&copy);
+               return -1;
+       }
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_REMOVEKEY,
+                       DATATYPE_KEY, &copy,
+                       DATATYPE_LAST);
+       keyClose(&copy);
+       if ( request == NULL ) {
+               perror("kdbRemoveKey_daemon");
+               return 1;
+       }
+       
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               return -1;
+       }
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+       
+       return ret;
+}
+
+/**
+ * Implementation for kdbGetKeyChildKeys() method.
+ *
+ * @see kdbGetKeyChildKeys() for expected behavior.
+ * @ingroup backend
+ */
+ssize_t kdbGetKeyChildKeys_daemon(KDB *handle, const Key *parentKey, KeySet *returned, unsigned long options) 
+{
+       Key     copy;
+       DaemonBackendData       *data;
+       Message         *request, *reply;
+       int             ret;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+       
+       /* Prepare request */
+       keyInit(&copy);
+       if ( keyDup(parentKey, &copy) ) {
+               keyClose(&copy);
+               return -1;
+       }
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_GETCHILD,
+                       DATATYPE_KEY, &copy,
+                       DATATYPE_ULONG, &options,
+                       DATATYPE_LAST);
+       keyClose(&copy);
+       if ( request == NULL ) {
+               perror("kdbGetKeyChildKeys_daemon");
+               return -1;
+       }
+       
+       
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               return -1;
+       }
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEYSET, returned,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+
+       return ret;
+}
+
+
+/**
+ * Implementation for kdbSetKeys() method.
+ * 
+ * The implementation of this method is optional, and a builtin, probablly 
+ * inefficient implementation can be explicitly used when exporting the
+ * backend with kdbBackendExport(), using kdbSetKeys_default().
+ * 
+ * @see kdbSetKeys() for expected behavior.
+ * @ingroup backend
+ */
+int kdbSetKeys_daemon(KDB *handle, KeySet *ks) 
+{
+       DaemonBackendData       *data;
+       Message         *request, *reply;
+       int             ret;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+       
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_SETKEYS,
+                       DATATYPE_KEYSET, ks,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbSetKeys_daemon");
+               return -1;
+       }
+
+       reply = callDaemon(data->socketfd, request);    
+       if ( reply == NULL ) {
+               return -1;
+       }
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_INTEGER, &ret,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEYSET, ks,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+
+       return ret;
+}
+
+
+/**
+ * The implementation of this method is optional.
+ * The builtin inefficient implementation will use kdbGetKey() for each
+ * key inside @p interests.
+ *
+ * @see kdbMonitorKeys() for expected behavior.
+ * @ingroup backend
+ */
+u_int32_t kdbMonitorKeys_daemon(KDB *handle, KeySet *interests, u_int32_t diffMask,
+               unsigned long iterations, unsigned sleep)
+{
+       DaemonBackendData       *data;
+       Message         *request, *reply;
+       unsigned long   monitorRet;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+       
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_MONITORKEYS,
+                       DATATYPE_KEYSET, interests,
+                       DATATYPE_ULONG, &diffMask,
+                       DATATYPE_ULONG, &iterations,
+                       DATATYPE_ULONG, &sleep,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbMonitorKeys_daemon");
+               return 1;
+       }
+       
+       reply = callDaemon(data->socketfd, request);
+       if ( reply == NULL ) {
+               return -1;
+       }
+
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_ULONG, &monitorRet,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEYSET, interests,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+
+       return monitorRet;
+}
+
+
+
+/**
+ *
+ * The implementation of this method is optional.
+ * The builtin inefficient implementation will use kdbGetKey() for
+ * @p interest.
+ *
+ * @see kdbMonitorKey() for expected behavior.
+ * @ingroup backend
+ */
+u_int32_t kdbMonitorKey_daemon(KDB *handle, Key *interest, u_int32_t diffMask,
+               unsigned long iterations, unsigned sleep) 
+{
+        DaemonBackendData       *data;
+       Message         *request, *reply;
+       unsigned long   monitorRet;
+       
+       data = (DaemonBackendData *) kdbhGetBackendData(handle);
+       if ( data == NULL )
+               return 1;
+       
+       /* Prepare request */
+       request = messageNew(MESSAGE_REQUEST, KDB_BE_MONITORKEY,
+                       DATATYPE_KEY, interest,
+                       DATATYPE_ULONG, &diffMask,
+                       DATATYPE_ULONG, &iterations,
+                       DATATYPE_ULONG, &sleep,
+                       DATATYPE_LAST);
+       if ( request == NULL ) {
+               perror("kdbMonitorKey_daemon");
+               return 1;
+       }
+
+       reply = callDaemon(data->socketfd, request);
+       
+       /* Get result */
+       if ( messageExtractArgs(reply,
+                               DATATYPE_ULONG, &monitorRet,
+                               DATATYPE_INTEGER, &errno,
+                               DATATYPE_KEY, interest,
+                               DATATYPE_LAST) ) {
+               fprintf(stderr, "Error extracting ARGS\n");
+               messageDel(reply);
+               return -1;
+       }
+       messageDel(reply);
+
+       return monitorRet;
+}
+
+
+/**
+ * All KDB methods implemented by the backend can have random names, except
+ * kdbBackendFactory(). This is the single symbol that will be looked up
+ * when loading the backend, and the first method of the backend
+ * implementation that will be called.
+ * 
+ * Its purpose is to "publish" the exported methods for libelektra.so. The
+ * implementation inside the provided skeleton is usually enough: simply
+ * call kdbBackendExport() with all methods that must be exported.
+ * 
+ * @return whatever kdbBackendExport() returns
+ * @see kdbBackendExport() for an example
+ * @see kdbOpenBackend()
+ * @ingroup backend
+ */
+KDBEXPORT(daemon)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,           &kdbOpen_daemon,
+               KDB_BE_CLOSE,          &kdbClose_daemon,
+               KDB_BE_GETKEY,         &kdbGetKey_daemon,
+               KDB_BE_SETKEY,         &kdbSetKey_daemon,
+               KDB_BE_STATKEY,        &kdbStatKey_daemon,
+               KDB_BE_RENAME,         &kdbRename_daemon,
+               KDB_BE_REMOVEKEY,      &kdbRemoveKey_daemon,
+               KDB_BE_GETCHILD,       &kdbGetKeyChildKeys_daemon,
+               KDB_BE_MONITORKEY,     &kdbMonitorKey_daemon,
+               KDB_BE_MONITORKEYS,    &kdbMonitorKeys_daemon,
+               KDB_BE_SETKEYS,        &kdbSetKeys_daemon,
+               KDB_BE_END);
+}
diff --git a/src/backends/daemon/datatype.h b/src/backends/daemon/datatype.h
new file mode 100644 (file)
index 0000000..fbb14e8
--- /dev/null
@@ -0,0 +1,47 @@
+/***************************************************************************
+                datatype.h  -  Defines argument datatypes
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+
+#ifndef DATATYPE_H
+#define DATATYPE_H
+
+/**
+ * Arguments type
+ *
+ * Type accepted in @link message Message@endlink
+ *
+ * @ingroup message
+ * @see messageNew()
+ * @see messageExtractArgs()
+ */
+typedef enum {
+       DATATYPE_UNKNOW = 1,            /*!< Unknown, not initialized */
+       DATATYPE_INTEGER,               /*!< "int" C type */
+       DATATYPE_ULONG,                 /*!< "unsigned long" C type */
+       DATATYPE_STRING,                /*!< "char *" C type */
+       DATATYPE_KEY,                   /*!< libelektra Key */
+       DATATYPE_KEYSET,                /*!< libelektra KeySet */
+
+       DATATYPE_LAST                   /*!< Must be the last */
+} DataType;
+
+
+#endif /* DATATYPE_H */
diff --git a/src/backends/daemon/ipc.c b/src/backends/daemon/ipc.c
new file mode 100644 (file)
index 0000000..731bb4f
--- /dev/null
@@ -0,0 +1,190 @@
+/***************************************************************************
+                   kdb_wrapper.c  -  The server for the daemon backend
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ipc.h"
+
+#define IPCPATH_MAX    100
+
+int ipc_stream(void)
+{
+       int s;
+       
+       s = socket(AF_UNIX,SOCK_STREAM,0);
+       if (s == -1) return -1;
+       if (ndelay_on(s) == -1) { close(s); return -1; }
+       return s;
+}
+
+static int ipc_bindit(int s,const char *p,int del)
+{
+       struct sockaddr_un sa;
+       unsigned int l;
+       
+       l = strlen(p);
+       if (l > IPCPATH_MAX) {
+               errno = EISDIR;
+               return -1;
+       }
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_UNIX;
+       memcpy(sa.sun_path, p, l);
+       if (del) unlink(sa.sun_path);
+       return bind(s,(struct sockaddr *) &sa,sizeof sa);
+}
+
+int ipc_bind(int s,const char *p)
+{
+         return ipc_bindit(s,p,0);
+}
+
+int ipc_bind_reuse(int s,const char *p)
+{
+         return ipc_bindit(s,p,1);
+}
+
+int ipc_accept(int s,char *p,int l,int *trunc)
+{
+       int fd;
+       struct sockaddr_un sa;
+       socklen_t dummy = sizeof sa;
+       
+       memset(&sa, 0, sizeof(sa));
+       fd = accept(s,(struct sockaddr *) &sa,&dummy);
+       if (fd == -1) return -1;
+       
+       memset(sa.sun_path, 0, dummy);
+       
+       *trunc = 1;
+       if (!l) return fd;
+       
+       if (l < (dummy + 1))
+               dummy = l - 1;
+       else
+               *trunc = 0;
+
+       memcpy(p, sa.sun_path, dummy);  
+       p[dummy] = 0;
+       
+       return fd;
+}
+
+int ipc_local(int s,char *p,int l,int *trunc)
+{
+       struct sockaddr_un sa;
+       socklen_t dummy = sizeof sa;
+       
+       memset(&sa, 0, sizeof(sa));
+       if (getsockname(s,(struct sockaddr *) &sa,&dummy) == -1) return -1;
+
+       memset(sa.sun_path, 0, dummy);  
+       
+       *trunc = 1;
+       if (!l) return 0;
+       
+       if (l < (dummy + 1))
+               dummy = l - 1;
+       else
+               *trunc = 0;
+       
+       memcpy(p, sa.sun_path, dummy);
+       p[dummy] = 0;
+       
+       return 0;
+}
+
+int ipc_connect(int s,const char *p)
+{
+       struct sockaddr_un sa;
+       unsigned int l;
+       
+       l = strlen(p);
+       if (l > IPCPATH_MAX) {
+               return -1;
+       }
+       memset(&sa, 0, sizeof(sa));
+       sa.sun_family = AF_UNIX;
+       memcpy(sa.sun_path, p, l);
+       
+       if (connect(s,(struct sockaddr *) &sa,sizeof sa) == -1) return -1;
+       
+       if (ndelay_off(s) == -1) return -1;
+               return 0;
+}
+
+int ipc_listen(int s,int backlog)
+{
+       return listen(s,backlog);
+}
+
+int ndelay_on(int fd)
+{
+         return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+}
+
+int ndelay_off(int fd)
+{
+       return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
+
+int ipc_eid(int s,uid_t *u,gid_t *g, pid_t*p)
+{
+       uid_t dummyu;
+       gid_t dummyg;
+       pid_t dummyp;
+       
+       if (getpeereid(s,&dummyu,&dummyg,&dummyp) == -1) 
+               return -1;
+       
+       *u = dummyu;
+       *g = dummyg;
+       *p = dummyp;
+       
+       return 0;
+}
+
+int getpeereid(int s,uid_t *u,gid_t *g,pid_t *p)
+{
+       struct ucred dummy = {0};
+       socklen_t len = sizeof(dummy);
+       
+       if (getsockopt(s,SOL_SOCKET,SO_PEERCRED,&dummy,&len) == -1)
+               return -1;
+       *u = dummy.uid;
+       *g = dummy.gid;
+       *p = dummy.pid;
+
+       return 0;
+}
diff --git a/src/backends/daemon/ipc.h b/src/backends/daemon/ipc.h
new file mode 100644 (file)
index 0000000..37e23de
--- /dev/null
@@ -0,0 +1,35 @@
+/***************************************************************************
+                   ipc.h  -  Interprocess communication methods
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+int ipc_connect(int s,const char *p);
+int ipc_stream(void);
+int ipc_bind(int s,const char *p);
+int ipc_bind_reuse(int s,const char *p);
+int ipc_accept(int s,char *p,int l,int *trunc);
+int ipc_local(int s,char *p,int l,int *trunc);
+int ipc_listen(int s,int backlog);
+int ipc_eid(int s,uid_t *u,gid_t *g, pid_t *p);
+
+int ndelay_on(int fd);
+int ndelay_off(int fd);
+
+int getpeereid(int s,uid_t *u,gid_t *g,pid_t *p);
+       
diff --git a/src/backends/daemon/kdb_wrapper.c b/src/backends/daemon/kdb_wrapper.c
new file mode 100644 (file)
index 0000000..1a65b8f
--- /dev/null
@@ -0,0 +1,194 @@
+/***************************************************************************
+                   kdb_wrapper.c  -  The server for the daemon backend
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* malloc */
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include "kdbbackend.h"
+
+#include "datatype.h"
+#include "message.h"
+
+
+Message *wrapper_kdbStatKey(KDB *handle, Message *request)
+{
+       Key             key;
+       int             error, ret;
+       Message         *reply;
+       
+       keyInit(&key);
+       if ( messageExtractArgs(request,
+                       DATATYPE_KEY, &key,
+                       DATATYPE_LAST) ) {
+               keyClose(&key);
+               return NULL;
+       }
+       
+       ret = kdbStatKey(handle, &key);
+       error = errno;
+       
+       reply = messageNew(MESSAGE_REPLY, KDB_BE_STATKEY,
+                       DATATYPE_INTEGER, &ret,
+                       DATATYPE_INTEGER, &error,
+                       DATATYPE_KEY, &key,
+                       DATATYPE_LAST);
+       
+       keyClose(&key);
+       
+       return reply;
+}
+
+Message *wrapper_kdbGetKey(KDB *handle, Message *request)
+{
+       Key             key;
+       int             error, ret;
+       Message         *reply;
+
+       keyInit(&key);
+       error = 0;
+       
+       if ( messageExtractArgs(request,
+                               DATATYPE_KEY, &key,
+                               DATATYPE_LAST) ) {
+               keyClose(&key);
+               return NULL;
+       }
+
+       ret = kdbGetKey(handle, &key);
+       error = errno;
+
+       reply = messageNew(MESSAGE_REPLY, KDB_BE_GETKEY,
+                       DATATYPE_INTEGER, &ret,
+                       DATATYPE_INTEGER, &error,
+                       DATATYPE_KEY, &key,
+                       DATATYPE_LAST);
+
+       keyClose(&key);
+       
+       return reply;
+}
+
+Message *wrapper_kdbSetKey(KDB *handle, void *request)
+{
+       Key             key;
+       int             error, ret;
+       Message         *reply;
+       
+       keyInit(&key);
+       error = 0;
+       
+       if ( messageExtractArgs(request,
+                       DATATYPE_KEY, &key,
+                       DATATYPE_LAST) ) {
+               keyClose(&key);
+               return NULL;
+       }
+
+       ret = kdbSetKey(handle, &key);
+       error = errno;
+       
+       reply = messageNew(MESSAGE_REPLY, KDB_BE_SETKEY,
+                       DATATYPE_INTEGER, &ret,
+                       DATATYPE_INTEGER, &error,
+                       DATATYPE_KEY, &key,
+                       DATATYPE_LAST);
+       
+       keyClose(&key);
+       
+       return reply;
+}
+
+Message *wrapper_kdbSetKeys(KDB *handle, void *request)
+{
+       KeySet          *ks;
+       int             error, ret;
+       Message         *reply;
+       
+       if ( (ks = ksNew()) == NULL )
+               return NULL;
+       
+       if ( messageExtractArgs(request, 
+                       DATATYPE_KEYSET, ks,
+                       DATATYPE_LAST) ) {
+               ksDel(ks);
+               return NULL;
+       }
+
+       ret = kdbSetKeys(handle, ks);
+       error = errno;
+       reply = messageNew(MESSAGE_REPLY, KDB_BE_SETKEYS,
+                       DATATYPE_INTEGER, &ret,
+                       DATATYPE_INTEGER, &error,
+                       DATATYPE_KEYSET, ks,
+                       DATATYPE_LAST);
+
+       ksDel(ks);
+
+       return reply;
+}
+
+Message *wrapper_kdbGetChild(KDB *handle, Message *request)
+{
+       Key             parentKey;
+       KeySet          *ks;
+       int             error, ret;
+       unsigned long   options;
+       Message         *reply;
+       
+       keyInit(&parentKey);
+
+       if ( messageExtractArgs(request,
+                       DATATYPE_KEY, &parentKey,
+                       DATATYPE_ULONG, &options,
+                       DATATYPE_LAST) ) {
+               keyClose(&parentKey);
+               return NULL;
+       }
+       
+       ks = ksNew();
+       ret = kdbGetKeyChildKeys(handle, &parentKey, ks, options);
+       error = errno;
+       keyClose(&parentKey);
+       
+       reply = messageNew(MESSAGE_REPLY, KDB_BE_GETCHILD,
+                       DATATYPE_INTEGER, &ret,
+                       DATATYPE_INTEGER, &error,
+                       DATATYPE_KEYSET, ks,
+                       DATATYPE_LAST);
+       ksDel(ks);
+
+       return reply;
+}
diff --git a/src/backends/daemon/kdb_wrapper.h b/src/backends/daemon/kdb_wrapper.h
new file mode 100644 (file)
index 0000000..fc6a7a5
--- /dev/null
@@ -0,0 +1,35 @@
+/***************************************************************************
+                   kdb_wrapper.h  -  The server for the daemon backend
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#include "message.h"
+
+Message *wrapper_kdbOpen(KDB *handle, Message *request, uid_t remoteeuid, gid_t remoteegid);
+Message *wrapper_kdbClose(KDB *handle, Message *request);
+Message *wrapper_kdbStatKey(KDB *handle, Message *request);
+Message *wrapper_kdbGetKey(KDB *handle, Message *request);
+Message *wrapper_kdbSetKey(KDB *handle, Message *request);
+Message *wrapper_kdbSetKeys(KDB *handle, Message *request);
+Message *wrapper_kdbRename(KDB *handle, Message *request);
+Message *wrapper_kdbRemoveKey(KDB *handle, Message *request); 
+Message *wrapper_kdbGetChild(KDB *handle, Message *request);
+Message *wrapper_kdbMonitorKey(KDB *handle, Message *request);
+Message *wrapper_kdbMonitorKeys(KDB *handle, Message *request); 
+
diff --git a/src/backends/daemon/kdbd.c b/src/backends/daemon/kdbd.c
new file mode 100644 (file)
index 0000000..657d49d
--- /dev/null
@@ -0,0 +1,152 @@
+/***************************************************************************
+                   kdbd.c  -  The server for the daemon backend
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "kdbbackend.h"
+
+#include "protocol.h"
+#include "message.h"
+#include "kdb_wrapper.h"
+#include "ipc.h"
+#include "thread.h"
+
+static Message *processRequest(Message *request, KDB *handle, uid_t remoteeuid, gid_t remoteegid);
+
+static Message *processRequest(Message *request, KDB *handle, uid_t remoteeuid, gid_t remoteegid)
+{
+       Message *reply;
+       int     msgType, procedure;
+
+       msgType = messageGetType(request);
+       if ( msgType != MESSAGE_REQUEST ) {
+               fprintf(stderr, "processRequest(): Received a non-request message.\n");
+               return NULL;
+       }
+       
+       procedure = messageGetProcedure(request);
+       switch(procedure) {
+/*             case KDB_BE_OPEN:
+                       reply = wrapper_kdbOpen(handle, request, remoteeuid, remoteegid);
+                       break;
+                       
+               case KDB_BE_CLOSE:
+                       reply = wrapper_kdbClose(handle, request);
+                       break;*/
+                       
+               case KDB_BE_STATKEY:
+                       reply = wrapper_kdbStatKey(handle, request);
+                       break;
+                       
+               case KDB_BE_GETKEY:
+                       reply = wrapper_kdbGetKey(handle, request);
+                       break;
+                       
+               case KDB_BE_SETKEY:
+                       reply = wrapper_kdbSetKey(handle, request);
+                       break;
+                       
+               case KDB_BE_SETKEYS:
+                       reply = wrapper_kdbSetKeys(handle, request);
+                       break;
+                       
+               case KDB_BE_GETCHILD:
+                       reply = wrapper_kdbGetChild(handle, request);
+                       break;
+                       
+               default:
+                       reply = NULL;
+       }
+
+       if ( reply == NULL ) {
+               /* Internat error from wrapper */
+               fprintf(stderr, "Internal error\n");
+               reply = messageNew(MESSAGE_REPLY, INTERNAL_ERROR,
+                                       DATATYPE_INTEGER, &errno,
+                                       DATATYPE_LAST);
+               if ( reply == NULL )
+                       fprintf(stderr, "An internal error caused a fatal error. Client will be confused :-(.\n"); 
+       }
+       
+       return reply;
+}
+
+int kdbd(void *pIntThreadHandle)
+{
+       KDB     *handle;
+       int             threadHandle, socketFd;
+       Message         *request, *reply;
+       uid_t           remoteeuid;
+       gid_t           remoteegid;
+       pid_t           remotepid;
+       int             closed;
+
+       handle = kdbOpen();
+
+       pthread_cleanup_push(threadExit, pIntThreadHandle);
+       
+       threadHandle = *((int *) pIntThreadHandle);
+       
+       if ( (socketFd = threadGetSocket(threadHandle)) == -1 ) {
+               fprintf(stderr, "Can't get socket :-(\n");
+               return 1;
+       } 
+
+       if ( ipc_eid(socketFd, &remoteeuid, &remoteegid, &remotepid) == -1 ) {
+               perror("kdbd");
+               return 1;
+       }
+       fprintf(stderr, "Thread %ld launched to serve PID %d (euid=%d/egid=%d)\n", pthread_self(), remotepid, remoteeuid, remoteegid);
+
+       closed = 0;
+       while ( !closed ) {
+               request = protocolReadMessage(socketFd);
+               if ( request == NULL ) {
+                       if ( (errno == EPIPE) || (errno == EINVAL) ) {
+                               /* Client closed the connection or
+                                * malformed request */
+                               messageDel(request);
+                               return 1;
+                       } else {
+                               /* They are probably some usefull errno
+                                * to check here ...
+                                */
+                               continue;
+                       }
+               }
+               closed = (messageGetProcedure(request) == KDB_BE_CLOSE);
+               
+               reply = processRequest(request, &handle, remoteeuid, remoteegid);
+               messageDel(request);
+
+               protocolSendMessage(socketFd, reply);
+               messageDel(reply);
+       }
+
+       pthread_cleanup_pop(1);
+
+       kdbClose (handle);
+       
+       return 0;
+}
+
diff --git a/src/backends/daemon/kdbd.h b/src/backends/daemon/kdbd.h
new file mode 100644 (file)
index 0000000..d254e4f
--- /dev/null
@@ -0,0 +1,25 @@
+/***************************************************************************
+                   kdbd.h  -  The server for the daemon backend
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2005 by Yannick Lecaillez
+    email                : yl@itioweb.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+
+void *kdbd(void *pIntThreadHandle);
+
diff --git a/src/backends/daemon/main.c b/src/backends/daemon/main.c
new file mode 100644 (file)
index 0000000..8de799d
--- /dev/null
@@ -0,0 +1,159 @@
+/***************************************************************************
+                   kdbd.c  -  The server for the daemon backend
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2005 by Yannick Lecaillez
+    email                : yl@itioweb.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* for exit() */
+#endif
+
+#include <pthread.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "sig.h"
+#include "ipc.h"
+#include "thread.h"
+#include "kdbd.h"
+
+
+#ifndef SOCKET_NAME
+#define SOCKET_NAME "/var/run/kdbd/elektra.sock"
+#endif
+
+
+int numchildren = 0;
+int limit = 20;
+char remotepath[512];
+
+
+void printstatus(void)
+{
+       fprintf(stdout, "kdbd: status: %d/%d\n", numchildren, limit);
+}
+
+int wait_nohang(int *wstat)
+{
+#ifdef HASWAITPID
+       return waitpid(-1,wstat,WNOHANG);
+#else
+       return wait3(wstat,WNOHANG,(struct rusage *) 0);
+#endif
+}
+
+void sigterm()
+{
+       unlink(SOCKET_NAME);
+       unlink("/var/run/kdbd/kdbd.pid");
+       exit(0);
+}
+
+void sigchld()
+{
+       int wstat;
+       int pid;
+       while ((pid = wait_nohang(&wstat)) > 0) {
+               fprintf(stdout, "kdbd: end %d status %d\n", pid, wstat);
+               if (numchildren) --numchildren; printstatus();
+       }
+}
+
+
+int main(int argc, char **argv)
+{
+       mode_t  m;
+       int     t, s;
+       int     trunc;
+       pid_t pid;
+
+       /* Uncomment setuid() call if the demon executable file is +s */
+       /* setuid(0); */
+       
+       if (pid=fork()) {
+               /* The parent. */
+               /* Log child pid and quit. */
+               
+               FILE *pidf;
+               
+               pidf=fopen("/var/run/kdbd/kdbd.pid","w");
+               fprintf(pidf,"%d",pid);
+               fclose(pidf);
+               
+               exit(0);
+       }
+       
+       /* force a superuniversal modern charset: UTF-8 */
+       putenv("LANG=en_US.UTF-8");
+       
+/*     sig_block(sig_child);
+       sig_catch(sig_child,sigchld);
+       sig_catch(sig_term,sigterm);
+       sig_ignore(sig_pipe); */
+
+       s = ipc_stream();
+       if ( s == -1 ) {
+               perror(argv[0]);
+               return 1;
+       }
+       
+       m = umask(0);
+       if ( ipc_bind_reuse(s, SOCKET_NAME) == -1 ) {
+               perror(argv[0]);
+               return 1;
+       }
+       umask(m);
+
+       if ( ipc_local(s, 0, 0, &trunc) == -1 ) {
+               perror(argv[0]);
+               return 1;
+       }
+       if (ipc_listen(s, 20) == -1) {
+               perror(argv[0]);
+               return 1;
+       }
+       ndelay_off(s);
+
+       /* fprintf(stderr,"uid=%d, euid=%d\n",getuid(),geteuid()); */
+       
+       for(;;) {
+               t = ipc_accept(s,remotepath,sizeof(remotepath),&trunc);
+
+               if (t == -1) {
+                       perror("kdbd");
+                       continue;
+               }
+
+               threadCreate(t, kdbd);
+       }
+}
+
diff --git a/src/backends/daemon/message.c b/src/backends/daemon/message.c
new file mode 100644 (file)
index 0000000..7b5a5e0
--- /dev/null
@@ -0,0 +1,299 @@
+/***************************************************************************
+                message.c  -  Class for a protocol messages
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ * Class for messages, to be passed over a protocol line.                  *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "datatype.h"
+#include "serial.h"
+#include "message.h"
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+/**
+ * @defgroup message Message
+ * @brief Encapsulate procedure call for IPC between libelektra-daemon and kdbd
+ *
+ * To use it:
+ * @code
+ * #include "message.h"
+ * @endcode
+ *
+ * Message is a flat struct (i.e: without pointer) which encapsulate a
+ * procedure with arguments. Its the basis class for elektra daemon IPC.
+ * a Message pointer contain the Message struct and the eventual serialized
+ * arguments. Here a in memory schematic representation:
+ * @code
+ * #|MESSAGE STRUCT|ARG0-SERIALIZED|ARGN-SERIALIZED|#
+ * @endcode
+ * 
+ * where # is the boundaries of the memory space allocated for the Message.
+ * Message depends of serializer.
+ *
+ * Refer to @link protocol Protocol@endlink for sending/receiving message.
+ * 
+ */
+
+/**
+ * Message constructor.
+ *
+ * Create a new message with associated arguments.
+ * Message is a serialized procedure and arguments targeted to be
+ * send throught a medium.
+ * Arguments are copied, so you could free these after using messageNew
+ *
+ * @code
+ * Message *msg
+ *
+ * // Create a request containing key and integer as arguments
+ * msg = messageNew(MESSAGE_REQUEST, 1, DATATYPE_KEY, key,
+ *                                      DATATYPE_INTEGER, &myInt,
+ *                                      DATATYPE_LAST);
+ * @endcode
+ *
+ * @param msgType Message type (request, reply, internal error)
+ * @param procedure Procedure ID
+ * 
+ * @see DataType
+ * @see MessageType
+ * @see messageExtractArgs()
+ * @see messageDel()
+ * @return newly allocated Message (must be freed with messageDel()) or NULL if error occured
+ *             
+ * @ingroup message
+ *
+ */
+Message *messageNew(MessageType msgType, int procedure, ...)
+{
+       DataType        type;
+       va_list         va;
+       Message         *msg;
+       int             nbArgs;
+       char            *buf;
+       size_t          size;
+       ssize_t         serializedSize;
+       
+       /* Compute size of Message + serialized args */
+       size = sizeof(Message);
+       
+       va_start(va, procedure);
+       type = va_arg(va, DataType);
+       nbArgs = 0;
+       while ( (type != DATATYPE_LAST) && (nbArgs < MSG_MAX_ARGS) ) {
+               serializedSize = serializeGetSize(type, va_arg(va, void *));
+               if ( serializedSize == -1 ) {
+                       fprintf(stderr, "SerializedGetSize = -1 for args %d of type %d !\n", nbArgs, type);
+                       va_end(va);
+                       return NULL;
+               }
+               size += serializedSize;
+               nbArgs++;
+               
+               type = va_arg(va, DataType);
+       }
+       va_end(va);
+       
+       if ( nbArgs == MSG_MAX_ARGS ) {
+               errno = ERANGE; 
+               return NULL;
+       }
+
+       /* Allocate some memory */
+       msg = (Message *) malloc(size);
+       if ( msg == NULL ) 
+               return NULL;
+               
+       /* Fill message struct */
+       memset(msg, 0, size);
+       msg->type = msgType;
+       msg->procId = procedure;
+       msg->nbArgs = nbArgs;
+       msg->size = size;
+       memset(msg->args, 0, sizeof(msg->args));
+       buf = (char *) msg;
+       buf += sizeof(Message);
+       
+       /* Add serialized args ... */
+       nbArgs = 0;
+       va_start(va, procedure);
+       type = va_arg(va, DataType);
+       while ( type != DATATYPE_LAST ) {
+               serializedSize = serialize(type, va_arg(va, void *), buf);
+               if ( serializedSize == -1 ) {
+                       free(msg);
+                       va_end(va);
+                       return NULL;
+               }
+               msg->args[nbArgs++] = type;
+               buf += serializedSize;
+
+               type = va_arg(va, DataType);
+       }
+       va_end(va);
+
+       return msg;
+}
+
+/**
+ * Get type of a message
+ *
+ * @param msg Message
+ * @return Type of the message
+ *
+ * @see MessageType
+ * @ingroup message
+ */
+MessageType messageGetType(const Message *msg)
+{
+       assert(msg != NULL);
+       
+       return msg->type;
+}
+
+/**
+ * Get procedure of a message
+ *
+ * @param msg Message
+ * @return Type of the message
+ * @ingroup message
+ */
+int messageGetProcedure(const Message *msg)
+{
+       assert(msg != NULL);
+
+       return msg->procId;
+}
+
+/**
+ * Get number of arguments contained in a message
+ *
+ * @param msg Message
+ * @return # of args
+ * @ingroup message
+ */
+int messageGetNbArgs(const Message *msg)
+{
+       assert(msg != NULL);
+
+       return msg->nbArgs;
+}
+
+/**
+ * Extract arguments from a message
+ * This methods extract arguments contained from a message
+ * into the suite of arguments passed to this method
+ *
+ * @code
+ * int ret
+ *  
+ * ret = messageExtractArgs(msg, DATATYPE_KEY, key,
+ *                               DATATYPE_INTEGER, &myInt,
+ *                               DATATYPE_LAST);
+ * @endcode
+ *
+ * @param msg Message
+ * @return 0 if Ok, -1 on error.
+ * 
+ * @see messageNew()
+ * @see DataType
+ * @ingroup message
+ */
+int messageExtractArgs(const Message *msg, ...)
+{
+       DataType type;
+       va_list va;
+       const char *buf;
+       ssize_t serializedSize;
+       int     args;
+
+       assert(msg != NULL);
+       
+       /* Skip message struct */
+       buf = (const char *) msg;
+       buf += sizeof(Message);
+               
+       /* Unserialize args */
+       args = 0;
+       va_start(va, msg);
+       type = va_arg(va, DataType);
+       while ( (type != DATATYPE_LAST) && (args < MSG_MAX_ARGS) ) {
+               if ( msg->args[args] != type ) {
+                       va_end(va);
+                       errno = EBADF;
+                       return -1;
+               }
+               
+               serializedSize = unserialize(type, buf, va_arg(va, void *));
+               if ( serializedSize == -1 ) {
+                       va_end(va);
+                       return -1;
+               }
+               
+               buf += serializedSize;
+               args++;
+
+               type = va_arg(va, DataType);
+       }
+       va_end(va);
+
+       if ( args == MSG_MAX_ARGS ) {
+               errno = ERANGE;
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Delete a message
+ * Free all memory took by this message.
+ * This doesn't free arguments passed when message was created.
+ *
+ * @param msg Message to delete
+ *
+ * @see messageNew()
+ * @ingroup message
+ */
+void messageDel(Message *msg)
+{
+       free(msg);
+}
diff --git a/src/backends/daemon/message.h b/src/backends/daemon/message.h
new file mode 100644 (file)
index 0000000..371f78b
--- /dev/null
@@ -0,0 +1,63 @@
+/***************************************************************************
+                message.h  -  Class for a protocol messages
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ * Class for messages, to be passed over a protocol line.                  *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifndef __MESSAGE__
+#define __MESSAGE__
+
+#include "datatype.h"
+
+#define MSG_MAX_ARGS   8
+
+typedef enum {
+       MESSAGE_REQUEST,
+       MESSAGE_REPLY
+} MessageType;
+
+#define INTERNAL_ERROR  1<<15
+
+typedef struct {
+       MessageType     type;
+       int             procId;
+       int             nbArgs;
+       DataType        args[MSG_MAX_ARGS];
+
+       size_t          size;
+
+       /* In memory, this struct is followed by
+        * the serialized arguments. "size" reflect that. */
+} Message;
+
+Message *messageNew(MessageType msgType, int procedure, ...);
+MessageType messageGetType(const Message *msg);
+int messageGetProcedure(const Message *msg);
+int messageGetNbArgs(const Message *msg);
+int messageExtractArgs(const Message *msg, ...);
+void messageDel(Message *msg);
+
+#endif /* __MESSAGE__ */
diff --git a/src/backends/daemon/protocol.c b/src/backends/daemon/protocol.c
new file mode 100644 (file)
index 0000000..b923d3e
--- /dev/null
@@ -0,0 +1,194 @@
+/***************************************************************************
+                protocol.c  -  Class for a protocol
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : sizon5@gmail.com, avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ * Class for messages, to be passed over a protocol line.                  *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <errno.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <stdarg.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "datatype.h"
+#include "message.h"
+
+#include "protocol.h"
+
+/* Magic: Magic number for protocol header. Elektra written like l33t ;-) */
+#define PROTO_MAGIC     0x0E1E374AL
+#define PROTO_VERSION   1
+
+static int     protocolCheckHeader         (const ProtocolHeader *header);
+
+
+/**
+ * @defgroup protocol Protocol
+ * @brief Protocol provide methods for transport Message throught a medium.
+ * 
+ * Protocol allow to send & receive @link message Message@endlink securely. It does some sanity check
+ * like checking protocol version & a magic number user for verify @link message message@endlink boundaries.
+ *
+ */
+
+/**
+ * Read a message from file/socket fd 
+ * This methods check header, read data and
+ * create a new Message object from data.
+ *
+ * @param fd File Descriptor where data should be readen
+ * @return a newly allocated message (which must be freed
+ * using messageDel()) or NULL if error occured.
+ *
+ * @see protocolCheckHeader()
+ * @see protocolSendMessage()
+ *
+ * @ingroup protocol
+ */
+Message *protocolReadMessage(int fd)
+{
+       Message         *msg;
+       char            *buf;
+       ProtocolHeader  header;
+       size_t          toRead;
+       ssize_t          ret;
+       
+       /* read header */
+       memset(&header, 0, sizeof(header));
+       if ( (ret = read(fd, &header, sizeof(header))) == -1 ) {
+               return NULL;
+       }
+       
+       if ( protocolCheckHeader(&header) ) { 
+               return NULL;
+       }
+
+       /* read message */
+       msg = (Message *) malloc(header.dataLen);
+       if ( msg == NULL ) {
+               return NULL;
+       }
+       
+       buf = (char *) msg;
+       toRead = header.dataLen;
+       while ( toRead > 0 ) {
+               if ( (ret = read(fd, buf, toRead)) == -1 ) {
+                       return NULL;
+               }
+
+               toRead -= ret;
+               buf += ret;
+       }
+
+       return msg;
+}
+
+/**
+ * Write a specified message to a file/socket
+ * This methods build a header, and write message to the descriptor.
+ * 
+ * @param fd File Descriptor where data should be written
+ * @param message Message to send.
+ * @return 0 if OK, -1 otherwise
+ *
+ * @see protocolReadMessage()
+ * @ingroup protocol
+ */
+int protocolSendMessage(int fd, const Message *message)
+{
+       ProtocolHeader header;
+       const char      *buf;
+       size_t  toWrite;
+       ssize_t ret;
+
+       assert(message != NULL);
+       
+       /* Send header */
+       memset(&header, 0, sizeof(header));
+       header.magic    = PROTO_MAGIC;
+       header.version  = PROTO_VERSION;
+       header.dataLen  = message->size;
+       if ( (ret = write(fd, &header, sizeof(header))) == -1 ) {
+               return -1;
+       }
+       
+       /* Send message */
+       toWrite = message->size;
+       buf = (const char *) message;
+       while ( toWrite > 0 ) {
+               if ( (ret = write(fd, buf, message->size)) == -1 ) {
+                       return -1;
+               }
+
+               toWrite -= ret;
+               buf += ret;
+       }
+       
+       return 0;
+}
+
+/**
+ * Check header validity
+ * This methods check a magic number for detect data boundaries.
+ * Check a version number for compatibility between protocol version.
+ *
+ * @param header Header to check
+ * @return 0 if OK, -1 otherwise
+ *
+ * @see protocolReadMessage()
+ */
+static int protocolCheckHeader(const ProtocolHeader *header)
+{
+       assert(header != NULL);
+
+       if ( header->magic != PROTO_MAGIC ) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if ( header->version < PROTO_VERSION ) {
+               errno = EINVAL;
+               return -1;
+       }       
+
+       return 0;
+}
diff --git a/src/backends/daemon/protocol.h b/src/backends/daemon/protocol.h
new file mode 100644 (file)
index 0000000..da15fbd
--- /dev/null
@@ -0,0 +1,43 @@
+/***************************************************************************
+                protocol.h  -  Interface for a protocol
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ * Class that implements a protocol.                                       *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+#include "message.h"
+
+typedef struct {
+       unsigned long  magic;
+       unsigned int   version;
+
+       size_t    dataLen;
+} ProtocolHeader;
+
+int protocolSendMessage(int fd, const Message *msg);
+Message *protocolReadMessage(int fd);
+
+#endif /* PROTOCOL_H */
diff --git a/src/backends/daemon/serial.c b/src/backends/daemon/serial.c
new file mode 100644 (file)
index 0000000..151c5ce
--- /dev/null
@@ -0,0 +1,142 @@
+/***************************************************************************
+                serial_bin.c  -  Low level objects serialization etc
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : sizon5@gmail.com, avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#include "datatype.h"
+
+#include "serial.h"
+#include "serial_int.h"
+#include "serial_string.h"
+#include "serial_key.h"
+#include "serial_keyset.h"
+
+/* *BEWARE* These funcs must be ordered by datatype in a such way
+ * serialize[DATATYPE] return expected results.
+ * Have a look at datatype.h
+ */
+static SerializerFunc serializer[] = {
+       { NULL, NULL, NULL },           /*       [0] -> Uninitialized   */
+       { NULL, NULL, NULL },           /*       [1] -> Unknown         */
+
+       /* Integer */
+       { serialInt_getSize, serialInt_serialize, serialInt_unserialize },
+       
+       /* Unsigned Long */
+       { serialUlong_getSize, serialUlong_serialize, serialUlong_unserialize },
+       
+       /* String */
+       { serialString_getSize, serialString_serialize, serialString_unserialize },
+
+       /* Key */       
+       { serialKey_getSize, serialKey_serialize, serialKey_unserialize },
+       
+       /* KeySet */
+       { serialKeySet_getSize, serialKeySet_serialize, serialKeySet_unserialize }
+};
+
+/**
+ * @defgroup serializer Serializer
+ * @brief Serializer provide methods for serialize/unserialize data of several DataType
+ *
+ * Serializer is used by @link message Message@endlink module for arguments
+ * serialization/unserialization.
+ * 
+ * These functions are wrapper to specific methods depending of the DataType of data
+ * they're working on. Each DataType have its own function set for do the real work.
+ *
+ */
+
+/**
+ * Get size of a serialized arguments
+ *
+ * This function compute the size needed for store
+ * the serialized form of an argument of type "type"
+ * and of value "val"
+ *
+ * @param type Type of the argument
+ * @param val Value of the argument
+ *
+ * @return Size of the serialized or -1 if error
+ *
+ * @see DataType
+ * @ingroup serializer
+ */
+ssize_t serializeGetSize(DataType type, void *val)
+{
+       if ( type <= DATATYPE_UNKNOW || type >= DATATYPE_LAST ) {
+               errno = ERANGE;
+               return -1;
+       }
+
+       return serializer[type].getSizeOfSerialized(val);
+}
+
+
+/**
+ * Serialize argument
+ *
+ * Serialize an argument into a buffer
+ * @param type Type of the argument to serialize
+ * @param pType Pointer to the argument
+ * @param pBuffer Buffer which will receive serialized version
+ *
+ * @return Size of the serialized or -1 if error
+ *
+ * @see unserialize()
+ * @ingroup serializer
+ */
+ssize_t serialize(DataType type, const void *pType, void *pBuffer)
+{
+       if ( type <= DATATYPE_UNKNOW || type >= DATATYPE_LAST ) {
+               errno = ERANGE;
+               return -1;
+       }
+       
+       return serializer[type].serialize(pType, pBuffer);
+}
+
+
+/**
+ * Unserialize argument
+ *
+ * Unserialize data from a buffer to "rebuild" argument
+ *
+ * @param type Type of the argument to unserialize
+ * @param pBuffer Buffer containing serialized version of argument
+ * @param pType Pointer to the unserialized argument
+ * *WARNING*: If argument's type have a flexible size (not like a 
+ * struct containing pointer but like a string) passed pType should
+ * be a **pType. Unserialized will then allocate needed memory for this
+ * type. You'll have to freed this one
+ *
+ * @return Size of the serialized or -1 if error
+ * @see serialize()
+ * @ingroup serializer
+ */
+ssize_t unserialize(DataType type, const void *pBuffer, void *pType) 
+{
+       if ( type <= DATATYPE_UNKNOW || type >= DATATYPE_LAST ) {
+               errno = ERANGE;
+               return -1;
+       }
+       
+       return serializer[type].unserialize(pBuffer, pType);
+}
diff --git a/src/backends/daemon/serial.h b/src/backends/daemon/serial.h
new file mode 100644 (file)
index 0000000..0acd6a8
--- /dev/null
@@ -0,0 +1,46 @@
+/***************************************************************************
+                serial.h  -  Abstraction of serialization
+                             -----------------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+
+#ifndef DAEMON_SERIAL_H
+#define DAEMON_SERIAL_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include "datatype.h"
+
+typedef struct {
+       ssize_t (*getSizeOfSerialized)(const void *data);
+       ssize_t (*serialize)(const void *data, void *into);
+       ssize_t (*unserialize)(const void *data, void *into);
+} SerializerFunc;
+
+ssize_t serializeGetSize(DataType type, void *val);
+ssize_t serialize(DataType type, const void *pType, void *pBuffer);
+ssize_t unserialize(DataType type, const void *pBuffer, void *pType); 
+
+#endif
diff --git a/src/backends/daemon/serial_int.c b/src/backends/daemon/serial_int.c
new file mode 100644 (file)
index 0000000..25aefd9
--- /dev/null
@@ -0,0 +1,105 @@
+/***************************************************************************
+                serial_bin.c  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : sizon5@gmail.com, avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+ssize_t serialInt_getSize(const void *pInt)
+{
+       return sizeof(int);
+}
+
+ssize_t serialInt_serialize(const void *pInt, void *pBuffer)
+{
+       size_t  size;
+
+       assert(pInt != NULL);
+       assert(pBuffer != NULL);
+       
+       size = serialInt_getSize(pInt);
+       memcpy(pBuffer, pInt, size);
+
+       return size;
+}
+
+ssize_t serialInt_unserialize(const void *pBuffer, void *pInt)
+{
+       size_t size;
+
+       assert(pBuffer != NULL);
+       assert(pInt != NULL);
+       
+       size = serialInt_getSize(pInt);
+       memcpy(pInt, pBuffer, size);
+
+       return size;
+       
+}
+
+
+
+ssize_t serialUlong_getSize(const void *pUlong)
+{
+       return sizeof(unsigned long);
+}
+
+ssize_t serialUlong_serialize(const void *pUlong, void *pBuffer)
+{
+       size_t  size;
+
+       assert(pUlong != NULL);
+       assert(pBuffer != NULL);
+       
+       size = serialUlong_getSize(pUlong);
+       memcpy(pBuffer, pUlong, size);
+       
+       return size;
+}
+
+ssize_t serialUlong_unserialize(const void *pBuffer, void *pUlong)
+{
+       size_t size;
+
+       assert(pBuffer != NULL);
+       assert(pUlong != NULL);
+       
+       size = serialUlong_getSize(pUlong);
+       memcpy(pUlong, pBuffer, size);
+       
+       return size;
+}
+
+
diff --git a/src/backends/daemon/serial_int.h b/src/backends/daemon/serial_int.h
new file mode 100644 (file)
index 0000000..3b93cf1
--- /dev/null
@@ -0,0 +1,30 @@
+/***************************************************************************
+                serial_int.c  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+
+ssize_t serialInt_getSize(const void *pInt);
+ssize_t serialInt_serialize(const void *pInt, void *pBuffer);
+ssize_t serialInt_unserialize(const void *pBuffer, void *pInt);
+
+ssize_t serialUlong_getSize(const void *pInt);
+ssize_t serialUlong_serialize(const void *pInt, void *pBuffer);
+ssize_t serialUlong_unserialize(const void *pBuffer, void *pInt);
+
diff --git a/src/backends/daemon/serial_key.c b/src/backends/daemon/serial_key.c
new file mode 100644 (file)
index 0000000..cb21e75
--- /dev/null
@@ -0,0 +1,206 @@
+/***************************************************************************
+                serial_bin.c  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+#include "datatype.h"
+#include "message.h"
+
+#include "serial_string.h"
+#include "serial_key.h"
+
+ssize_t serialKey_getSize(const void *pKey)
+{
+       const Key *key;
+       size_t  size;
+
+       key = (const Key *) pKey;
+               
+       if ( !keyIsInitialized(key) ) 
+               return -1;
+
+       size = sizeof(Key);
+       
+       if ( key->flags & KEY_SWITCH_NAME )
+               size += serialString_getSize(keyName(key));
+        if ( key->flags & KEY_SWITCH_COMMENT )
+               size += serialString_getSize(keyComment(key));
+       if ( key->flags & KEY_SWITCH_OWNER )
+               size += serialString_getSize(keyOwner(key));
+       if ( key->flags & KEY_SWITCH_VALUE ) {
+               if ( keyIsString(key) )
+                       size += serialString_getSize(keyValue(key));
+               else
+                       size += keyGetValueSize(key);
+       }
+
+       return size;
+}
+
+ssize_t serialKey_serialize(const void *pKey, void *pBuffer)
+{
+       const Key *key;
+       size_t  size;
+       char    *buf;
+       int     convert;
+
+       key = (const Key *) pKey;
+       
+       if ( !keyIsInitialized(key) ) {
+               return -1;
+       }
+       buf = (char *) pBuffer;         
+
+       /* Serialize metadata */
+       size = sizeof(Key);
+       memcpy(buf, key, size);
+       buf += size;
+
+       convert = kdbbNeedsUTF8Conversion();
+       
+       /* Serialize key name */
+       if ( key->flags & KEY_SWITCH_NAME ) {
+               size = serialString_serialize(keyName(key), buf);       
+               if ( size == -1 )
+                       return -1;
+               buf += size;
+       }
+
+       /* Serialize key comment */
+       if ( key->flags & KEY_SWITCH_COMMENT ) {
+               size = serialString_serialize(keyComment(key), buf);
+               if ( size == -1 )
+                       return -1;
+               buf += size;
+       }
+
+       /* Serialize key owner */
+       if ( key->flags & KEY_SWITCH_OWNER ) {
+               size = serialString_serialize(keyOwner(key), buf);
+               if ( size == -1 )
+                       return -1;
+               buf += size; 
+       }
+       
+       /* Serialize key value */
+       if ( key->flags & KEY_SWITCH_VALUE ) {
+               if ( keyIsString(key) ) {
+                       size = serialString_serialize(keyValue(key), buf);
+                       if ( size == -1 )
+                               return -1;
+                       buf += size;
+               } else {
+                       size = keyGetValueSize(key);
+                       memcpy(buf, keyValue(key), size);
+                       buf += size;
+               }
+       }
+
+       return (buf - ((char *) pBuffer));
+}
+
+ssize_t serialKey_unserialize(const void *pBuffer, void *pKey) 
+{
+       const   char    *buf;
+               Key     *key, save;
+               size_t  size;
+
+       key = (Key *) pKey;
+       if ( !keyIsInitialized(key) ) 
+               return -1;
+
+       buf = (const char *) pBuffer;
+
+       /* Save current key char pointer 
+        * since these one will be overrided. */
+       memcpy(&save, key, sizeof(Key));
+       
+       /* Unserialize key struct */
+       size = sizeof(Key);
+       memcpy(key, buf, size);
+       buf += size;
+
+       /* Restore original key pointer */
+       key->key = keyName(&save);
+       key->comment = keyComment(&save);
+       key->userDomain = keyOwner(&save);
+       key->data = keyValue(&save);
+
+       /* Unserialize keyname */
+       if ( key->flags & KEY_SWITCH_NAME ) {
+               free(key->key);
+               size = serialString_unserialize(buf, &key->key);
+               if ( size == -1 )
+                       return -1;
+               buf += size;
+       }
+
+       /* Unserialize comment */
+       if ( key->flags & KEY_SWITCH_COMMENT ) {
+               free(key->comment);
+               size = serialString_unserialize(buf, &key->comment);
+               if ( size == -1 )
+                       return -1;
+               key->commentSize = size;
+               buf += size;
+       }
+
+       /* Unserialize userDomain */
+       if ( key->flags & KEY_SWITCH_OWNER ) {
+               free(key->userDomain);
+               size = serialString_unserialize(buf, &key->userDomain);
+               if ( size == -1 )
+                       return -1;
+               buf += size; 
+       }
+
+       /* Unserialize data */
+       if ( key->flags & KEY_SWITCH_VALUE ) {
+               if ( keyIsString(key) ) {
+                       free(key->data);
+                       size = serialString_unserialize(buf, &key->data);
+               } else {
+                       size = keySetRaw(key, buf, key->dataSize);
+               }
+
+               if ( size == -1 )
+                       return -1;
+
+               key->dataSize = size;
+               buf += size;
+       }
+
+       return (buf - ((char *) pBuffer));
+}
diff --git a/src/backends/daemon/serial_key.h b/src/backends/daemon/serial_key.h
new file mode 100644 (file)
index 0000000..955256b
--- /dev/null
@@ -0,0 +1,28 @@
+/***************************************************************************
+                serial_key.h  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#include "kdb.h"
+
+ssize_t serialKey_getSize(const void *pKey);
+ssize_t serialKey_serialize(const void *pKey, void *pBuffer);
+ssize_t serialKey_unserialize(const void *pBuffer, void *pKey);
+       
diff --git a/src/backends/daemon/serial_keyset.c b/src/backends/daemon/serial_keyset.c
new file mode 100644 (file)
index 0000000..62c6dab
--- /dev/null
@@ -0,0 +1,128 @@
+/***************************************************************************
+                serial_keyset.c  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : sizon5@gmail.com, avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+#include "datatype.h"
+#include "message.h"
+
+#include "serial_key.h"
+#include "serial_keyset.h"
+
+ssize_t serialKeySet_getSize(const void *pKeySet) 
+{
+       const KeySet *ks;
+       size_t  size;
+       ssize_t ret;
+       Key     *current;
+       
+       ks = (const KeySet *) pKeySet;
+       size = sizeof(ks->size);
+       current = ks->start;
+       while ( current != NULL ) {
+               ret = serialKey_getSize((const void *) current);
+               if ( ret == -1 )
+                       return -1;              
+               
+               size += ret;
+               current = current->next;
+       }
+       
+       size += sizeof(size_t);
+       
+       return size;
+}
+
+ssize_t serialKeySet_unserialize(const void *pBuffer, void *pKeySet)
+{
+       KeySet  *ks;
+       const char *buf;
+       ssize_t ret;
+       size_t  count, size;
+       Key     *current;
+
+       buf = (const char *) pBuffer;
+       ks = (KeySet *) pKeySet;
+       
+       /* Read size of the keySet */
+       memcpy(&size, buf, sizeof(size));
+       buf += sizeof(size);
+
+       /* Read the key inside the keyset */
+       count = 0;
+       while ( count < size ) {
+               /* Create a new key */
+               current = keyNew(KEY_SWITCH_END);
+               if ( current == NULL )
+                       return -1;
+
+               /* Unserialize key */
+               ret = serialKey_unserialize(buf, (void *) current);
+               if ( ret == -1 )
+                       return -1;
+               buf += ret;
+
+               /* Add the key into the keyset */
+               count = ksAppendKey(ks, current);
+       }
+       
+       return (buf - ((const char *) pBuffer));
+}
+
+ssize_t serialKeySet_serialize(const void *pKeySet, void *pBuffer) 
+{
+       const KeySet *ks;
+       Key *current;
+       char *buf;
+       ssize_t ret;
+       
+       ks = (const KeySet *) pKeySet;
+       buf = pBuffer;
+       
+       memcpy(buf, &ks->size, sizeof(ks->size));
+       buf += sizeof(ks->size);
+
+       /* Store all the keyset */
+       current = ks->start;
+       while ( current != NULL ) {
+               ret = serialKey_serialize((void *) current, (void *) buf);
+               if ( ret == -1 )
+                       return -1;
+               buf += ret;
+
+               current = current->next;
+       }
+
+       return (buf - ((char *) pBuffer));
+}
diff --git a/src/backends/daemon/serial_keyset.h b/src/backends/daemon/serial_keyset.h
new file mode 100644 (file)
index 0000000..c440cf6
--- /dev/null
@@ -0,0 +1,27 @@
+/***************************************************************************
+                serial_keyset.h  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+
+
+ssize_t serialKeySet_getSize(const void *pKeySet);
+ssize_t serialKeySet_unserialize(const void *pBuffer, void *pKeySet);
+ssize_t serialKeySet_serialize(const void *pKeySet, void *pBuffer);
diff --git a/src/backends/daemon/serial_string.c b/src/backends/daemon/serial_string.c
new file mode 100644 (file)
index 0000000..868ea99
--- /dev/null
@@ -0,0 +1,129 @@
+/***************************************************************************
+                serial_string.c  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#include <iconv.h>
+
+#include "kdbbackend.h"
+
+/* Daemon works in UTF-8. So, conversion is done
+ * only when we're called from a non-UTF8 client */
+
+ssize_t serialString_getSize(const void *pChar)
+{
+       size_t size;
+
+       size = kdbiStrLen(pChar);
+       if ( kdbbNeedsUTF8Conversion() ) 
+               size = size * 4;
+
+       return size;    
+}
+
+
+ssize_t serialString_serialize(const void *pChar, void *pBuffer)
+{
+       char    *currentCharset, *writeCursor;
+       const char *readCursor;
+       iconv_t converter;
+       size_t  bufferSize;
+       size_t  size;
+       ssize_t len;
+
+       if ( kdbbNeedsUTF8Conversion() ) {
+               currentCharset=nl_langinfo(CODESET);
+               converter = iconv_open("UTF-8",currentCharset);
+
+               size = kdbiStrLen(pChar);
+               bufferSize = size * 4;
+
+               readCursor = (const char *) pChar;
+               writeCursor = (char *) pBuffer;
+               
+               if ( iconv(converter, (ICONV_CONST char **) &readCursor, &size, &writeCursor, &bufferSize) == (size_t)(-1) ) {
+                       iconv_close(converter);
+                       return -1;
+               }
+               iconv_close(converter);
+               
+               len = writeCursor - ((char *) pBuffer);
+       } else {
+               len = serialString_getSize(pChar);
+               if ( len != -1 ) {
+                       memcpy(pBuffer, pChar, len);
+               }
+       }
+
+       return len;
+}
+
+ssize_t serialString_unserialize(const void *pBuffer, void *ppChar) 
+{
+       char    *currentCharset, *writeCursor;
+       const char      *readCursor;
+       iconv_t converter;
+       char    **dest;
+       size_t  bufferSize;
+       size_t  size;
+       ssize_t len;
+
+       dest = (char **) ppChar;
+       if ( kdbbNeedsUTF8Conversion() ) {
+               currentCharset=nl_langinfo(CODESET);
+               converter = iconv_open(currentCharset, "UTF-8");
+               
+               size = kdbiStrLen(pBuffer);
+               bufferSize = size * 4;
+               *dest = (char *) malloc(bufferSize);
+               readCursor = (const char *) pBuffer;
+               writeCursor = (char *) *dest;
+               
+               if ( iconv(converter, (ICONV_CONST char **) &readCursor, &size, &writeCursor, &bufferSize) == (size_t)(-1) ) {
+                       iconv_close(converter);
+                       return -1;
+               }
+               iconv_close(converter);
+
+               len = writeCursor - ((char *) *dest);   
+       } else {
+               len = kdbiStrLen(pBuffer);
+               if ( len != -1 ) {
+                       *dest = (char *) malloc(len);
+                       memcpy(*dest, pBuffer, len);
+               }
+       }
+
+       return len;
+}
+
diff --git a/src/backends/daemon/serial_string.h b/src/backends/daemon/serial_string.h
new file mode 100644 (file)
index 0000000..cc4344a
--- /dev/null
@@ -0,0 +1,27 @@
+/***************************************************************************
+                serial_string.h  -  Low level objects serialization etc
+                             -------------------
+    begin                : Sun Mar 12 2006
+    copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+
+ssize_t serialString_getSize(const void *pChar);
+ssize_t serialString_serialize(const void *pChar, void *pBuffer);
+ssize_t serialString_unserialize(const void *pBuffer, void *ppChar);
+
diff --git a/src/backends/daemon/sig.c b/src/backends/daemon/sig.c
new file mode 100644 (file)
index 0000000..effe842
--- /dev/null
@@ -0,0 +1,102 @@
+/***************************************************************************
+                   sig.c  - Handle signals
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <signal.h>
+#include "sig.h"
+
+int sig_alarm = SIGALRM;
+int sig_child = SIGCHLD;
+int sig_cont = SIGCONT;
+int sig_hangup = SIGHUP;
+int sig_int = SIGINT;
+int sig_pipe = SIGPIPE;
+int sig_term = SIGTERM;
+
+void (*sig_defaulthandler)() = SIG_DFL;
+void (*sig_ignorehandler)() = SIG_IGN;
+
+void sig_block(int sig)
+{
+/*#ifdef HAVE_SIGPROCMASK*/
+#ifdef HASSIGPROCMASK
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigaddset(&ss,sig);
+       sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
+#else
+       sigblock(1 << (sig - 1));
+#endif
+}
+
+void sig_unblock(int sig)
+{
+/*#ifdef HAVE_SIGPROCMASK*/
+#ifdef HASSIGPROCMASK
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigaddset(&ss,sig);
+       sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
+#else
+       sigsetmask(sigsetmask(~0) & ~(1 << (sig - 1)));
+#endif
+}
+
+void sig_blocknone(void)
+{
+/*#ifdef HAVE_SIGPROCMASK*/
+#ifdef HASSIGPROCMASK
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+#else
+       sigsetmask(0);
+#endif
+}
+
+void sig_catch(int sig,void (*f)())
+{
+/*#ifdef HAVE_SIGACTION*/
+#ifdef HASSIGACTION
+       struct sigaction sa;
+       sa.sa_handler = f;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sigaction(sig,&sa,(struct sigaction *) 0);
+#else
+       signal(sig,f); /* won't work under System V, even nowadays---dorks */
+#endif
+}
+
+void sig_pause(void)
+{
+/*#ifdef HAVE_SIGPROCMASK*/
+#ifdef HASSIGPROCMASK
+       sigset_t ss;
+       sigemptyset(&ss);
+       sigsuspend(&ss);
+#else
+       sigpause(0);
+#endif
+}
+
diff --git a/src/backends/daemon/sig.h b/src/backends/daemon/sig.h
new file mode 100644 (file)
index 0000000..a92d667
--- /dev/null
@@ -0,0 +1,39 @@
+/***************************************************************************
+                   sig.h  - Signal processing
+                             -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+extern int sig_alarm;
+extern int sig_child;
+extern int sig_cont;
+extern int sig_hangup;
+extern int sig_int;
+extern int sig_pipe;
+extern int sig_term;
+
+void sig_block(int sig);
+void sig_unblock(int sig);
+void sig_blocknone(void);
+void sig_catch(int sig,void (*f)());
+void sig_pause(void);
+extern void (*sig_defaulthandler)();
+extern void (*sig_ignorehandler)();
+
+#define sig_ignore(s) (sig_catch((s),sig_ignorehandler))
+#define sig_uncatch(s) (sig_catch((s),sig_defaulthandler))
diff --git a/src/backends/daemon/thread.c b/src/backends/daemon/thread.c
new file mode 100644 (file)
index 0000000..ad96cde
--- /dev/null
@@ -0,0 +1,209 @@
+/***************************************************************************
+                   thread.c  -  Thread Managment
+                           -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <pthread.h>
+
+#include "thread.h"
+
+#define THREADS_PREALLOC       5
+
+typedef struct {
+       pthread_t       thread;
+       
+       int             socketFd;
+       int             handle;
+} ThreadContext;
+
+static ThreadContext **threads = NULL;
+static int threadsArraySize = 0;
+static pthread_mutex_t threadsMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int threadListAdd(ThreadContext *thread);
+static ThreadContext *threadListGet(int threadHandle);
+static int threadListRemove(int threadHandle);
+
+/*
+ * Create a new ThreadContext and launch the thread
+ * Launched thread must call threadExit(handle) at end.
+ */
+int threadCreate(int socketfd, void *(*start_routine)(void *))
+{
+       ThreadContext   *new;
+
+       if ( (new = (ThreadContext *) malloc(sizeof(ThreadContext))) == NULL ) {
+               return -1;
+       }       
+       memset(new, 0, sizeof(ThreadContext));
+       new->socketFd = socketfd;
+
+       if ( (new->handle = threadListAdd(new)) == -1 )
+               return -1;
+       
+       /* Execution options (only one should be used):
+          1. Multithreaded: */
+       if ( pthread_create(&new->thread, NULL, start_routine, (void *) &new->handle) ) {
+               threadListRemove(new->handle);
+               free(new);
+               return -1;
+       }
+
+       /* 2. Single thread, for debugging purposes: */ 
+       /* kdbd(&new->handle); */
+
+       return new->handle;
+}
+
+int threadGetSocket(int threadHandle)
+{
+       ThreadContext *thread;
+
+       if ( (thread = threadListGet(threadHandle)) == NULL )
+               return -1;
+
+       return thread->socketFd;
+}
+
+pthread_t threadGetId(int threadHandle)
+{
+       ThreadContext *thread;
+
+       if ( (thread = threadListGet(threadHandle)) == NULL )
+               return -1;
+
+       return thread->thread;
+}
+
+/*
+ * Remove ThreadContext on thread exit
+ * This functions shoudln't be called by hand !
+ */
+void threadExit(void *pIntThreadHandle)
+{
+       ThreadContext   *thread;
+       int     threadHandle;
+
+       threadHandle = *((int *) pIntThreadHandle);
+       
+       if ( (thread = threadListGet(threadHandle)) == NULL ) {
+               fprintf(stderr, "threadExit(): Thread's handle %d doesn't exist.\n", threadHandle);
+               return;
+       }
+       
+       if ( threadListRemove(threadHandle) == -1 ) {
+               fprintf(stderr, "threadExit(): Unable to remove thread's handle %d.\n", threadHandle);
+               return;
+       }
+
+       close(thread->socketFd);
+       free(thread);
+       fprintf(stderr, "Thread %ld (handle=%d) freed.\n", pthread_self(), threadHandle);
+}
+
+
+/*
+ * Keep a ThreadContext into an array
+ * return an associated handle
+ */
+static int threadListAdd(ThreadContext *thread)
+{
+       ThreadContext **tmp;
+       int threadHandle;
+       int newSize;
+       
+       newSize = 0;
+       if ( threadsArraySize == 0 ) {
+               newSize = THREADS_PREALLOC;
+       } else {
+               /* Search for a free space ... */
+               pthread_mutex_lock(&threadsMutex);
+               for(threadHandle = 0 ; threadHandle < threadsArraySize ; threadHandle++) {
+                       if ( threads[threadHandle] == NULL )
+                               break;
+               }
+               
+               if ( threadHandle == threadsArraySize ) {
+                       /* No free space found, extend array */
+                       newSize = threadsArraySize + THREADS_PREALLOC;
+               }
+               pthread_mutex_unlock(&threadsMutex);
+       }
+       
+       if ( newSize ) {
+               /* No free space found, extend array */
+               pthread_mutex_lock(&threadsMutex);
+               tmp = realloc(threads, sizeof(pthread_t *) * (newSize));
+               if ( tmp == NULL ) {
+                       pthread_mutex_unlock(&threadsMutex);
+                       return -1;
+               }
+               threads = tmp;
+               threadHandle = threadsArraySize;
+               threadsArraySize = newSize;
+               pthread_mutex_unlock(&threadsMutex);
+       }
+       
+       pthread_mutex_lock(&threadsMutex);
+       threads[threadHandle] = thread;
+       pthread_mutex_unlock(&threadsMutex);
+
+       return threadHandle;
+}
+
+
+static int threadListRemove(int threadHandle)
+{
+       if ( threadHandle >= threadsArraySize )
+               return -1;
+
+       pthread_mutex_lock(&threadsMutex);
+       threads[threadHandle] = NULL;
+       pthread_mutex_unlock(&threadsMutex);
+
+       return 0;
+}
+
+static ThreadContext *threadListGet(int threadHandle)
+{
+       ThreadContext *thread;
+       
+       if ( threadHandle >= threadsArraySize )
+               return NULL;
+       
+       pthread_mutex_lock(&threadsMutex);
+       thread = threads[threadHandle];
+       pthread_mutex_unlock(&threadsMutex);
+
+       return thread;
+}
+       
diff --git a/src/backends/daemon/thread.h b/src/backends/daemon/thread.h
new file mode 100644 (file)
index 0000000..e5a7aa9
--- /dev/null
@@ -0,0 +1,24 @@
+/***************************************************************************
+                   thread.h  -  Thread Managment
+                        -------------------
+    copyright            : (C) 2006 by Yannick Lecaillez
+    email                : sizon5@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+int threadCreate(int socketfd, void * (*start_routine)(void *));
+int threadGetSocket(int threadHandle);
+pthread_t threadGetId(int threadHandle);
+void threadExit(void *pIntThreadHandle);
diff --git a/src/backends/doc/backend.c b/src/backends/doc/backend.c
new file mode 100644 (file)
index 0000000..64deafd
--- /dev/null
@@ -0,0 +1,738 @@
+/***************************************************************************
+            backend.c  -  Skeleton of backends to access the Key Database
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <kdbbackend.h>
+
+
+#define BACKENDNAME "backend"
+#define BACKENDVERSION "1.0.0"
+
+
+/**
+ * @defgroup backend KDB Backends :: Elektra framework for pluggable backends
+ * @brief The tactics to create pluggable backends to libelektra.so
+ *
+ * @section intro Introduction
+ *
+ * @since Since version 0.4.9, Elektra can dynamically load different key storage
+ * backends.
+ *
+ * @since Since version 0.7.0 Elektra can have multiple storage backends, called just
+ * backends henceforth, at once for different purposes.
+ *
+ * @par Definition: You refers to the implementation of the function in this specification.
+ * If you read the documentation about kdbGet_backend(), then the caller is kdbGet()
+ * which is the only function which can and will call (invoke) you. The Preconditions
+ * will always be met by the caller, you can count on them. But you (as said before we
+ * speak about the function) need to take care that all Postconditions are met.
+ *
+ * @subsection overview Overview
+ *
+ * The methods of class KDB that are backend dependent are only kdbOpen_backend(),
+ * kdbClose_backend(), kdbGet_backend(), kdbSet_backend() and KDBEXPORT() to export
+ * these methods. A backend must implement each of them. A detailed
+ * specification of these methods and methods needed in that context
+ * follows in this Documentation Module.
+ *
+ * The other KDB methods are higher level. They use the above methods to
+ * do their job, and generally don't have to be reimplemented for a
+ * different backend, but there might be a solution to do so for higher
+ * performance in future. kdbh* methods are for access to the internals
+ * of KDB, which will be passed to all functions.
+ *
+ * @subsection incl Include Files
+ *
+ * The backend implementation must include:
+ * @code
+#include <kdbbackend.h>
+ * @endcode
+ * to have direct access to the structs, which is currently needed to
+ * access the capability structure.
+ *
+ * Don't include kdb.h, it will be automatically included and some macros will
+ * avoid redefining structs where you have more insight from a backend than
+ * you would normally have. Additionally you get the declaration of all functions
+ * described here, except the one you have to implement.
+ *
+ * @subsection dyn Dynamic Mounting
+ *
+ * An elektrified program will use elektra/libelektra-default.so as its default backend.
+ * This backend provides the system/ hierarchy and some base configuration
+ * in system/elektra for elektra itself. Everything below system/ and the other
+ * hierarchies can be stored in any different backend. This is allowed through the
+ * technique mounting. A backend can be mounted to any path except system/
+ * and system/elektra.
+ *
+ * A backends is guaranteed to be loaded whenever calling kdbGet()
+ * or kdbSet() requires the backend, but may already be loaded at kdbOpen(). It
+ * might be loaded explizit by kdbMount() at any time after kdbOpen().
+ * Backends get a chance to initialize by calling kdbOpen_backend() whenever
+ * they are loaded.
+ *
+ * Using kdbUnmount() a backend may closed during runtime.
+ * All backends will be closed when kdbClose() is called.
+ * Backends might be unloaded after some time of inactivity or other reasons.
+ * After loading backends get a chance to cleanup by calling
+ * kdbClose_backend().
+ *
+ * That means it is not guaranteed that the backend live the whole time nor
+ * it will be loaded only one time. A tactic to handle this well is to build
+ * stateless backends referring to kdbGet_backend() and kdbSet_backend().
+ * That means that there is no more information present than in the storage itself.
+ * Be aware that you must not have any global variables in your backend. Read more
+ * about that in kdbOpen_backend().
+ * But to be stateless you also have to consider not to store any other than caching
+ * information into kdbhGetBackendData(). I repeat: it must be possible to restore
+ * everything dynamically stored without exception.
+ *
+ * @subsection lib Library Names
+ *
+ * Elektra source code or development package provides a skeleton and Makefile
+ * to implement a backend. Copy src/backends/template to have a good
+ * starting point. See the CODING document
+ * to know how to integrate the backend in the build system or how to compile
+ * it external.
+ *
+ * A backend is defined by a single name, for example @c BACKENDNAME, that
+ * causes libelektra.so look for its library as @c libelektra-BACKENDNAME.so.
+ *
+ * @par Example of a complete backend:
+ * @code
+//
+// This is my implementation for an Elektra backend storage.
+//
+// To compile it:
+// $ cc -fpic `pkg-config --cflags elektra` -o myback.o -c myback.c
+// $ cc -shared -fpic `pkg-config --libs elektra` -o libelektra-myback.so myback.o
+//
+// To use it:
+// $ preload mount myback system/myback myback /tmp/nofile
+// $ kdb ls system/myback
+// $ kdb set system/myback/key "value"
+// $ kdb get system/myback/key
+//
+
+#include <kdbbackend.h>
+
+#define BACKENDNAME "backend"
+
+
+int kdbOpen_backend(KDB *handle) {...}
+int kdbClose_backend(KDB *handle) {...}
+int kdbGet_backend(KDB handle, KeySet *returned, Key *key) {...}
+int kdbSet_backend(KDB handle, KeySet *returned, Key *key) {...}
+
+KDBEXPORT(backend) {
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,  &kdbOpen_backend,
+               KDB_BE_CLOSE, &kdbClose_backend,
+               KDB_BE_GET,   &kdbGet_backend,
+               KDB_BE_SET,   &kdbSet_backend,
+               KDB_BE_END);
+}
+ * @endcode
+ *
+ * In the example, the *_backend() methods can have other random names,
+ * since you'll correctly pass them later to kdbBackendExport(). It is
+ * recommended to use names according to your backendname to avoid
+ * name clashes. Be aware that every symbol name in the linked application
+ * must be unique.
+ *
+ * Don't copy above example out, use src/backends/template, it does
+ * compile as-is and does some initialization and cleanup already.
+ *
+ * Elektra source code tree includes several backend implementations
+ * https://svn.libelektra.org/svn/elektra/trunk/src/backends/
+ * that can also be used as a reference.
+ *
+ * @section backenddetail Details
+ *
+ * @subsection intro Introduction
+ *
+ * Capabilities may make your live much easier. If it is impossible,
+ * very hard or would impact performance badly you may leave out some
+ * parts described here, but need to declare that you have done so with
+ * capabilites.
+ *
+ * It is allowed to provide additional information, even if you
+ * declared you don't have it. If you declare that you are capable
+ * of doing something, you must provide it without exceptions.
+ *
+ * @subsection owner Owner
+ *
+ * You need to set the owner of keys by keySetOwner(). Owner is the
+ * name to whom a specific key of the user/ hierarchy belongs.
+ * If you declare kdbcGetnoOwner() you need not to set the owner of
+ * the keys. It also means that even if you want to get keys from another
+ * user hierarchy you get yours.
+ *
+ * @subsection value Values
+ *
+ * Values are the central information of keys next to the name
+ * describing what informations it holds. Parse them out of your backend
+ * and put them into the key with keySetString(). The information will
+ * be duplicated, so you might need to free() your string. Don't try
+ * to directly access key->data, things may change there and your
+ * backend might be compiled with a different libc than elektra.
+ * If you support types, you might want to use keySetRaw() to not
+ * change the key type.
+ * If you don't support values for all keys declare kdbcGetnoValue().
+ *
+ * @subsection id IDs
+ *
+ * You need to set uid respective gid for any key not having the uid
+ * and gid of the current process. This will be set by default in
+ * every key. You can do it with keySetUID() and keySetGID().
+ * Declaring kdbcGetnoUID() and kdbcGetnoGID() you need not set uid
+ * and gid.
+ *
+ * @subsection mode Mode
+ *
+ * Mode shows what can be done with the key having or not having
+ * the above uid and gid. Use keySetMode() to set the correct
+ * mode description, read the description in keySetMode()
+ * for the semantics of the 3 octal representation.
+ * Declaring kdbcGetnoMode() means mode will remain default.
+ *
+ * The very related method keySetDir() sets the executable bits
+ * of mode. Even if your backend does not support mode, it
+ * might support directories, meaning that keys have the mode
+ * 0664 or 0775 for directories. Declaring kdbcGetnoDir() means
+ * that the backend is flat, no key will be true for keyIsDir()
+ * and so can't have any subkeys.
+ *
+ * @subsection timing Timing
+ *
+ * Keys should have exact timing information of their
+ * modification and access times. Use keySetATime(), keySetMTime()
+ * and keySetCTime() to store appropriate information.
+ * ATime need to be stored in database, if you stat a key
+ * the backend need to return the time kdbGet() was last used
+ * for the keys.
+ * If you don't support this, declare
+ * kdbcGetnoATime() and simple store time(0) in the atime.
+ * This must be the same for every key for a single
+ * kdbGet_backend().
+ * If you only stat keys with kdbGet(), see below, then the access time
+ * should not be updated.
+ * MTime is the last modification time of value or comment.
+ * If you don't support this, declare
+ * kdbcGetnoMTime() and simple store time(0) in the mtime.
+ * This must be the same for every key for a single
+ * kdbGet_backend().
+ * CTime is the last change time of any metadata or
+ * add/remove of subkeys.
+ * If you don't support this, declare
+ * kdbcGetnoCTime() and simple store time(0) in the ctime.
+ * This must be the same for every key for a single
+ * kdbGet_backend().
+ *
+ * @subsection type Types
+ *
+ * Keys having value and comment can be one of two fundamental
+ * types, string or binary, both called value. While string is
+ * a null terminated utf8 character sequence, binary is any data
+ * of a specific length. Be sure to use keySetString() for string
+ * and keySetBinary() if you want to store binary data.
+ * If you do not support one of these, be sure to declare
+ * kdbcGetnoBinary() or kdbcGetnoString(), if you don't support
+ * both make sure to also declare kdbcGetnoValue().
+ *
+ * Using keySetRaw() does not set the type, be sure to use keySetType()
+ * afterwards. This can be KEY_TYPE_STRING and KEY_TYPE_BINARY
+ * or any other type in #type_t, leading
+ * to same results as explained above, but also any other number
+ * in the range of #type_t. Declare kdbcGetnoTypes() when your
+ * backend does not support arbitrary types.
+ *
+ *
+ */
+
+
+
+
+
+/**
+ * Initialize the backend.
+ * This is the first method kdbOpenBackend() calls after dynamically loading
+ * the backend library.
+ *
+ * This method is responsible of:
+ * - backend's specific configuration gathering
+ * - all backend's internal structs initialization
+ * - initial setup of all I/O details such as opening a file, connecting to a
+ *   database, etc
+ *
+ * If your backend does not support all aspects described in kdbGet_backend()
+ * and kdbSet_backend() you need capabilities to export this information.
+ * Per default you declare to be fully compliant to the specification given
+ * here, to change it get a pointer to KDBCap structure by using
+ * kdbhGetCapability().
+ *
+ * You may also read the configuration you can get with kdbhGetConfig() and transform it
+ * into other structures used by your backend.
+ *
+ * But be aware that you don't have any global variables. If you do your backend will
+ * not be threadsafe. You can use kdbhSetBackendData() and kdbhGetBackendData() to store
+ * and get any information related to your backend.
+ *
+ * The correct substitute for global variables will be:
+ * @code
+struct _GlobalData{ int global; };
+typedef struct _GlobalData GlobalData;
+int kdbOpen_backend(KDB *handle) {
+       PasswdData *data;
+       data=malloc(sizeof(PasswdData));
+       data.global = 20;
+       kdbhSetBackendData(handle,data);
+}
+ * @endcode
+ *
+ * Make sure to free everything in kdbClose_backend().
+ *
+ * @return 0 on success
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @see kdbOpen()
+ * @ingroup backend
+ */
+int kdbOpen_backend(KDB *handle) {
+       return 0;
+}
+
+
+
+
+/**
+ * Finalize the backend.
+ * Called prior to unloading the backend dynamic module. Should ensure that no
+ * functions or static/global variables from the module will ever be accessed again.
+ *
+ * Make sure to free all memory that your backend requested at runtime.
+ *
+ * Specifically make sure to capDel() all capabilites and free your backendData in
+ * kdbhGetBackendData().
+ *
+ * After this call, libelektra.so will unload the backend library, so this is
+ * the point to shutdown any affairs with the storage.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @return 0 on success, anything else otherwise.
+ * @see kdbClose()
+ * @ingroup backend
+ */
+int kdbClose_backend(KDB *handle) {
+       return 0; /* success */
+}
+
+
+
+/**
+ * Retrieve information from a permanent storage to construct
+ * a keyset.
+ *
+ * @section intro Introduction
+ *
+ * This function does everything related to get keys out from a
+ * backend. There is only one function for that purpose to make
+ * implementation and locking much easier.
+ *
+ * The keyset @p returned needs to be filled with information
+ * so that the application using elektra can access it.
+ * See the live cycle of a comment to understand:
+ * @code
+kdbGet_backend(KDB *handle, KeySet *returned, Key *parentKey)
+{
+       // the task of kdbGet_backend is to retrieve the comment out of the permanent storage
+       Key *key = keyDup (parentKey); // generate a new key to hold the information
+       char *comment;
+       loadfromdisc (comment);
+       keySetComment (key, comment, size); // set the information
+       ksAppendKey(returned, key);
+}
+
+// Now return to kdbGet
+int kdbGet(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+       kdbGet_backend (handle, keyset, 0);
+       // postprocess the keyset and return it
+}
+
+// Now return to usercode, waiting for the comment
+void usercode (Key *key)
+{
+       kdbGet (handle, keyset, parentKey, 0);
+       key = ksCurrent (keyset, key); // lookup the key from the keyset
+       keyGetComment (key); // now the usercode retrieves the comment
+}
+
+ * @endcode
+ * Of course not only the comment, but all information of every key in the keyset
+ * @p returned need to be fetched from permanent storage and stored in the key.
+ * So this specification needs to give
+ * an exhaustive list of information present in a key.
+ *
+ * @section conditions Conditions
+ *
+ * @pre The caller kdbGet() will make sure before you are called
+ * that the parentKey:
+ * - is a valid key (means that it is a system or user key).
+ * - is below (see keyIsBelow()) your mountpoint and that your backend is responsible for it.
+ * - has keyNeedStat() set when you should only stat keys (see later).
+ * and that the returned:
+ * - is a valid keyset.
+ * - has @p all keys with the flag KEY_FLAG_SYNC set.
+ * - @p may has keys with the flag  KEY_FLAG_STAT set.
+ * - contains only valid keys direct below (see keyIsDirectBelow()) your parentKey.
+ *   That also means, that the parentKey will not be in that keyset.
+ * - have keyIsStat() set when the value/comment information is not necessary.
+ * - is in a sorted order, see ksSort().
+ * and that the handle:
+ *  - is a valid KDB for your backend.
+ *  - that kdbhGetBackendHandle() contains the same handle for lifetime kdbOpen_backend()
+ *    until kdbClose_backend() was called.
+ *
+ * @pre The caller kdbGet() will make sure that afterwards you were called,
+ * whenever the user requested it with the options, that:
+ * - hidden keys they will be thrown away.
+ * - dirs or only dirs kdbGet() will remove
+ *   the other.
+ * - you will be called again recursively with all subdirectories.
+ * - the keyset will be sorted when needed.
+ * - the keys in returned having KEY_FLAG_SYNC will be sorted out.
+ *
+ * @invariant There are no global variables and kdbhGetBackendData()
+ *  only stores information which can be regenerated any time.
+ *  The handle is the same when it is the same backend.
+ *
+ * @post The keyset @p returned has the @p parentKey and all keys direct
+ * below (keyIsDirectBelow()) with all information from the storage.
+ * Make sure to return all keys, all directories and also all
+ * hidden keys. If some of them are not wished, the caller kdbGet() will
+ * drop these keys, see above.
+ *
+ * @section detail Details
+ *
+ * Now lets look at an example how the typical kdbGet_backend() might be
+ * implemented. To explain we introduce some pseudo functions which do all
+ * the work with the storage (which is of course 90% of the work for a real
+ * backend):
+ * - find_key() gets an key out from the storage and memorize the position.
+ * - next_key() will find the next key and return it (with the name).
+ * - fetch_key() gets out all information of a key from storage
+ *    (details see below example).
+ *    It removes the keyNeedStat() and keyNeedSync() flag afterwards.
+ * - stat_key() gets all meta information (everything but value and comment).
+ *   It removes the key keyNeedSync() flag afterwards.
+ * returns the next key out from the storage.
+ * The typical loop now will be like:
+ * @code
+ssize_t kdbGet_backend(KDB *handle, KeySet *update, const Key *parentKey) {
+       Key * current;
+       KeySet *returned = ksNew(ksGetSize(update)*2, KS_END);
+
+       find_key (parentKey);
+       current = keyDup (parentKey);
+       if (keyNeedStat(parentKey))
+       {
+               current = stat_key(current);
+       } else {
+               current = fetch_key(current);
+       }
+       clear_bit (KEY_FLAG_SYNC, current->flags);
+       ksAppendKey(returned, current);
+
+       while ((current = next_key()) != 0)
+       {
+               // search if key was passed in update by caller
+               Key * tmp = ksLookup (update, current, KDB_O_WITHOWNER|KDB_O_POP);
+               if (tmp) current = tmp; // key was passed, so use it
+               if (keyNeedStat(parentKey) || keyNeedStat(current))
+               {
+                       current = stat_key (current);
+                       set_bit (KEY_FLAG_STAT, current->flags);
+               } else {
+                       current = fetch_key(current);
+               }
+               clear_bit (KEY_FLAG_SYNC, current->flags);
+               ksAppendKey(returned, current);
+               // TODO: delete lookup key
+       }
+
+       if (error_happened())
+       {
+               errno = restore_errno();
+               return -1;
+       }
+
+       ksClear (update); // the rest of update keys is not in storage anymore
+       ksAppend(update, returned); // append the keys
+       ksDel (returned);
+
+       return nr_keys();
+}
+ * @endcode
+ *
+ * @note - returned and update are separated, for details why see ksLookup()
+ * - the bit KEY_FLAG_SYNC is always cleared, see postconditions
+ *
+ * So your mission is simple: Search the @c parentKey and add it and then search
+ * all keys below and add them too, of course with all requested information
+ * (which is only depended on keyNeedStat()).
+ *
+ * @section stat Stat
+ *
+ * Sometimes value and comment are not of interest, but
+ * metadata. To avoid a potential time-consuming kdbGet()
+ * you can keyNeedStat() the @p parentKey. If the backend supports
+ * a less time-consuming method to just get names and
+ * metadata, implement it, otherwise declare kdbcGetnoStat().
+ *
+ * The implementation works as follows: When the @p parentKey has
+ * keyNeedStat() set, all keys need to be stated instead of getting
+ * them. So the keys you ksAppendKey() don't have a value nor a comment
+ * and make sure that KEY_FLAG_SYNC is not set, but keyNeedStat()
+ * must be set for all keys which are only stated.
+ *
+ * The keys in @p returned may already have keyNeedStat() set.
+ * These keys must keep the status keyNeedStat() and you don't need
+ * to get the value and comment. See the example above for code.
+ *
+ * @section updating Updating
+ *
+ * To get all keys out of the storage over and over again can be very inefficient.
+ * You might know a more efficient method to know if the key needs update or not,
+ * e.g. by stating it or by an external time stamp info. In that case you can make
+ * use of @p returned KeySet. There are following possibilities:
+ * - The key is in returned and up to date.
+ *   You just need to remove the KEY_FLAG_SYNC flag.
+ * - The key is in returned but keyNeedStat() is true.
+ *   You just need to stat the key and remove the KEY_FLAG_SYNC flag and
+ *   set the KEY_FLAG_STAT flag.
+ * - The key is in returned, keyNeedStat() is false (for the key and the @p parentKey)
+ *   and you know that the key has changed. You need to fully retrieve the key and
+ *   remove the KEY_FLAG_SYNC flag.
+ * - The key is not in returned, the @p parentKey has keyNeedStat(). You just need
+ *   to stat the key. Make sure that KEY_FLAG_SYNC is not set, but KEY_FLAG_STAT
+ *   needs to be set. Append the key to @p returned.
+ * - The key is not in returned and the @p parentKey keyNeedStat() is false.
+ *   You need to fully retrieve the key out of storage, clear KEY_FLAG_STAT and
+ *   KEY_FLAG_SYNC and ksAppendKey() it to the @p returned keyset.
+ *
+ * @note You must clear the flag KEY_FLAG_SYNC at the very last point where no more
+ * modification on the key will take place, because any modification on the key will
+ * set the KEY_FLAG_SYNC flag again. With that keyNeedSync() will return true and
+ * the caller will sort this key out.
+ *
+ * @section fullget only Full Get
+ *
+ * In some backends it is not useful to get only a part of the configuration, because
+ * getting all keys would take as long as getting some. For this situation,
+ * you can declare onlyFullGet, see kdbcGetonlyFullGet().
+ *
+ * The only valid call for your backend is then that @p parentKey equals the @p mountpoint.
+ * For all other @p parentKey you must, add nothing and just return 0.
+ *
+ * @code
+if (strcmp (keyName(kdbhGetMountpoint(handle)), keyName(parentKey))) return 0;
+ * @endcode
+ *
+ * If the @p parentKey is your mountpoint you will of course fetch all keys, and not only
+ * the keys direct below the @c parentKey.
+ * So @p returned is valid iff:
+ * - every key is below ( keyIsBelow()) the parentKey
+ * - every key has a direct parent (keyIsDirectBelow()) in the keyset
+ *
+ * @note This statement is only valid for backends with kdbcGetonlyFullGet() set.
+ *
+ * @note If any calls you use change errno, make sure to restore the old errno.
+ *
+ * @see kdbGet() for caller.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param returned contains a keyset where the function need to
+ * append the keys got from the storage. There might be also some
+ * keys inside it, see conditions. You may use them to support
+ * efficient updating of keys, see @ref updating.
+ * @param parentKey contains the information below which key
+ * the keys should be gotten.
+ *
+ * @return Return how many keys you added.
+ *
+ * @return -1 on failure, the current key in returned shows the position.
+ * In normal execution cases a positive value will be returned.
+ * But in some cases you are not able to get keys and have to
+ * return -1. If you declare kdbcGetnoError() you are done, but
+ * otherwise you have to set the cause of the error.
+ * (Will be added in 0.7.1)
+ *
+ * @ingroup backend
+ */
+ssize_t kdbGet_backend(KDB *handle, KeySet *returned, const Key *parentKey) {
+       return 0;
+}
+
+
+/**
+ * Store a keyset permanently.
+ *
+ * This function does everything related to set and remove keys in a
+ * backend. There is only one function for that purpose to make
+ * implementation and locking much easier.
+ *
+ * The keyset @p returned was filled in with information from the application
+ * using elektra and the task of this function is to store it in a permanent
+ * way so that a subsequent call of kdbGet_backend() can rebuild the keyset
+ * as it was before. See the live cycle of a comment to understand:
+ * @code
+void usercode (Key *key)
+{
+       keySetComment (key, "mycomment"); // the usercode stores a comment for the key
+       ksAppendKey(keyset, key); // append the key to the keyset
+       kdbSet (handle, keyset, 0, 0);
+}
+
+// so now kdbSet is called
+int kdbSet(KDB *handle, KeySet *keyset, Key *parentKey, options)
+{
+       // find appropriate backend
+       kdbSet_backend (handle, keyset, 0); // the keyset with the key will be passed to this function
+}
+
+// so now kdbSet_backend(), which is the function described here, is called
+kdbSet_backend(KDB *handle, KeySet *keyset, Key *parentKey)
+{
+       // the task of kdbSet_backend is now to store the comment
+       Key *key = ksCurrent (keyset); // get out the key where the user set the comment before
+       char *comment = allocate(size);
+       keyGetComment (key, comment, size);
+       savetodisc (comment);
+}
+ * @endcode
+ * Of course not only the comment, but all information of every key in the keyset
+ * @p returned need to be stored permanetly. So this specification needs to give
+ * an exhaustive list of information present in a key.
+ *
+ * @pre The keyset @p returned holds all keys which must be saved
+ * permanently for this keyset. The keyset is sorted and rewinded.
+ * All keys having children must be true for keyIsDir().
+ *
+ * @pre The @p parentKey is the key which is the ancestor for all other keys in the
+ * keyset. The first key of the keyset @p returned has the same keyname.
+ * The parentKey is below the mountpoint, see kdbhGetMountpoint().
+ *
+ * @pre The caller kdbSet will fulfill following parts:
+ * - If the user does not want hidden keys they will be thrown away.
+ *   All keys in @p returned need to be stored permanently.
+ * - If the user does not want dirs or only dirs kdbGet() will remove
+ *   the other.
+ * - Sorting of the keyset. It is not important in which order the keys
+ *   are appended.
+ * So make sure to set all keys, all directories and also all
+ * hidden keys. If some of them are not wished, the caller kdbSet() will
+ * sort them out.
+ *
+ * @invariant There are no global variables and kdbhGetBackendData()
+ *  only stores information which can be regenerated any time.
+ *  The handle is the same when it is the same backend.
+ *
+ * @post The information of the keyset @p returned is stored permanently.
+ *
+ * When some keys have KEY_FLAG_REMOVE set, that means return true for
+ * keyNeedRemove(), remove the keys instead of getting them. In this case
+ * the sorting order will be the reverse way, first will be the children, then
+ * the parentKey when iterating over the KeySet returned.
+ *
+ * Lock your permanent storage in an exclusive way, no access of a
+ * concurrent kdbSet_backend() or kdbGet_backend() is possible
+ * and these methods block until the function has finished.
+ * Otherwise declare kdbcGetnoLock().
+ *
+ * @see kdbSet() for caller.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param returned contains a keyset with relevant keys
+ * @param parentKey contains the information where to set the keys
+ *
+ * @return When everything works gracefully return the number of keys you set.
+ * The cursor position and the keys remaining in the keyset are not important.
+ *
+ * @return Return 0 on success with no changed key in database
+ *
+ * @return Return -1 on failure.
+ *
+ * @note If any calls you use change errno, make sure to restore the old errno.
+ *
+ * @err In normal execution cases a positive value will be returned.
+ * But in some cases you are not able to set keys and have to
+ * return -1. If you declare kdbcGetnoError() you are done, but
+ * otherwise you have to set the cause of the error.
+ * (Will be added with 0.7.1)
+ *
+ * You also have to make sure that ksGetCursor()
+ * shows to the position where the error appeared.
+ *
+ * @ingroup backend
+ */
+ssize_t kdbSet_backend(KDB *handle, KeySet *returned, const Key *parentKey) {
+       return 0;
+}
+
+/**
+ * All KDB methods implemented by the backend can have random names, except
+ * kdbBackendFactory(). This is the single symbol that will be looked up
+ * when loading the backend, and the first method of the backend
+ * implementation that will be called.
+ *
+ * Its purpose is to publish the exported methods for libelektra.so. The
+ * implementation inside the provided skeleton is usually enough: simply
+ * call kdbBackendExport() with all methods that must be exported.
+ *
+ * The first paramter is the name of the backend.
+ * Then every backend must have:
+ * @c KDB_BE_OPEN,
+ * @c KDB_BE_CLOSE,
+ * @c KDB_BE_GET and
+ * @c KDB_BE_SET
+ *
+ * You might also give following information by char *:
+ * @c KDB_BE_VERSION,
+ * @c KDB_BE_AUTHOR,
+ * @c KDB_BE_LICENCE and
+ * @c KDB_BE_DESCRIPTION
+ *
+ * You must use static "char arrays" in a read only segment.
+ * Don't allocate storage, it won't be freed.
+ *
+ * With capability you can get that information on
+ * runtime from any backend with kdbGetCapability().
+ *
+ * The last parameter must be @c KDB_BE_END.
+ *
+ * @return kdbBackendExport() with the above described parameters.
+ * @see kdbBackendExport() for an example
+ * @see kdbOpenBackend()
+ * @ingroup backend
+ */
+KDBEXPORT(backend)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_backend,
+               KDB_BE_CLOSE,   &kdbClose_backend,
+               KDB_BE_GET,     &kdbGet_backend,
+               KDB_BE_SET,     &kdbSet_backend,
+               KDB_BE_END);
+}
+
diff --git a/src/backends/filesys/Makefile.am b/src/backends/filesys/Makefile.am
new file mode 100644 (file)
index 0000000..de35979
--- /dev/null
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+noinst_LIBRARIES = libelektra-filesys.a
+libelektra_filesys_a_SOURCES = filesys.c filesys.h ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_filesys_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+
+backend_LTLIBRARIES = libelektra-filesys.la
+libelektra_filesys_la_SOURCES = filesys.c filesys.h ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_filesys_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_filesys_la_LIBADD = ../../libelektra/libelektra.la
+libelektra_filesys_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
diff --git a/src/backends/filesys/filesys.c b/src/backends/filesys/filesys.c
new file mode 100755 (executable)
index 0000000..713405b
--- /dev/null
@@ -0,0 +1,982 @@
+/***************************************************************************
+            filesys.c  -  A filesystem backend implementation for Elektra
+                             -------------------
+    begin                : Mon Dec 25 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/***************************************************************************
+ *                                                                         *
+ *   This is the implementation of a filesystem backend for the            *
+ *   Elektra. Each Key is a file in the filesystem.                        *
+ *   It is as secure as filesystem security. It is as reliable             *
+ *   as filesystem. It uses only standards C calls, which makes it         *
+ *   usable by very low level or early boot stage software, like           *
+ *   /sbin/init.                                                           *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+#include "filesys.h"
+
+int kdbOpen_filesys(KDB *handle)
+{
+       KDBCap * cap = kdbhGetCapability(handle);
+
+       cap->noMode=1;
+       cap->noMount=1;
+       cap->noError=1;
+
+       kdbhSetCapability (handle,cap);
+#if DEBUG && VERBOSE
+       fprintf (stderr, "you open filesys " BACKENDVERSION "\n");
+#endif
+
+       return 0;
+}
+
+
+
+
+int kdbClose_filesys(KDB *handle)
+{
+       return 0;
+}
+
+
+int IN_SBOX;
+
+#define ELEKTRA_MOUNT_PATH "/opt/var/kdb/db"
+#define ELEKTRA_MOUNT_PATH_CHECK \
+do{\
+       if(!IN_SBOX) \
+      IN_SBOX = access("/opt/var/kdb/kdb_first_boot", F_OK) + 2; \
+   if(2==IN_SBOX) return 0;\
+}while(0)
+#define DB_START_TRANS_XATTR_NAME "full_db_transaction_start"
+#define DB_STOP_TRANS_XATTR_NAME "full_db_transaction_stop"
+#define DB_RB_TRANS_XATTR_NAME "full_db_transaction_rb"
+#define DB_TRANS_ONLY_CHECK "full_db_transaction_check"
+
+static void acquire_transaction_delay(int ms)
+{
+    struct timeval timeout;
+    timeout.tv_sec = ms / 1000 ;
+    timeout.tv_usec = 1000 * ( ms % 1000 );
+
+    select(0, 0, 0, 0, &timeout);
+}
+
+static int sqlite_begin_transaction()
+{
+#ifdef SQLFS_BACKEND
+       pid_t value;
+       ELEKTRA_MOUNT_PATH_CHECK;
+       value = getpid();
+transaction_retry:
+       if(setxattr(ELEKTRA_MOUNT_PATH, DB_START_TRANS_XATTR_NAME, &value, sizeof(value), 0) == -1)
+       {
+               acquire_transaction_delay(500);
+               goto transaction_retry;
+       }       
+#endif
+
+       return 0;
+}
+
+static int sqlite_commit_transaction()
+{
+#ifdef SQLFS_BACKEND
+       pid_t value;
+       ELEKTRA_MOUNT_PATH_CHECK;
+       value = getpid();
+       setxattr(ELEKTRA_MOUNT_PATH, DB_STOP_TRANS_XATTR_NAME, &value, sizeof(value), 0);
+#endif
+       return 0;
+}
+
+static int sqlite_rollback_transaction()
+{
+#ifdef SQLFS_BACKEND
+       pid_t value;
+       ELEKTRA_MOUNT_PATH_CHECK;
+       value = getpid();
+       setxattr(ELEKTRA_MOUNT_PATH, DB_RB_TRANS_XATTR_NAME, &value, sizeof(value), 0);
+#endif
+       return 0;
+}
+
+static int sqlite_check_transaction()
+{
+#ifdef SQLFS_BACKEND
+       pid_t value;
+       ELEKTRA_MOUNT_PATH_CHECK;
+       value = getpid();
+       setxattr(ELEKTRA_MOUNT_PATH, DB_TRANS_ONLY_CHECK, &value, sizeof(value), 0);
+#endif
+       return 0;
+}
+
+
+ssize_t kdbGet_filesys(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       DIR *parentDir;
+       char buffer[MAX_KEY_LENGTH];
+       struct dirent *entry;
+       Key *keyEntry;
+       Key *found;
+       KeySet *appended = ksNew (ksGetSize(returned)*2, KS_END);
+       int errnosave = errno;
+
+       char *parent_key_value;
+
+#ifdef SQLFS_BACKEND   
+       if(keyGetNamespace(parentKey)== KEY_NS_USER)
+               sqlite_check_transaction(parentKey);
+#endif
+       /*
+               - Convert parent key name into a real filename
+               - Check if it is a directory. Open it
+               - Browse, read and include in the KeySet
+       */
+       if (kdbbGetFullFilename(handle,parentKey,buffer,sizeof(buffer)) == -1)
+       {
+               errno = errnosave;
+               return -1;
+       }
+
+#ifdef TUNNING_ELEKTRA_0_7
+   if(ksGetSize(returned))
+   {
+      ksRewind(returned);
+      while((found = ksPop(returned)))
+      {
+                       keyEntry = found;
+         keyGetBaseName(keyEntry, buffer, MAX_KEY_LENGTH);
+                       if (kdbbGetFullKeyName (handle, buffer, parentKey, keyEntry) == -1) 
+                       {
+                               errno = errnosave;
+                               return -1;
+                       }
+               
+                       if (keyNeedStat(parentKey)) set_bit (keyEntry->flags, KEY_FLAG_STAT);
+                       if (kdbGetKey_filesys(handle,keyEntry) != -1)
+                       {
+                               keyflag_t semiflag;
+                               /* Remove the SYNC flag */
+                               semiflag= KEY_FLAG_SYNC;
+                               semiflag=~semiflag;
+                               keyEntry->flags &= semiflag;
+                               ksAppendKey(appended,keyEntry);
+                       }
+      }
+      
+      ksClear (returned); /* delete all other keys */
+      ksAppend (returned, appended);
+      ksDel (appended);
+      
+      errno = errnosave;
+
+       return returned->size;
+
+   }
+
+       if(keyGetValueSize(parentKey) != 1)
+       {
+               parent_key_value = keyValue(parentKey);
+               if(strcmp(parent_key_value,"*")==0)
+               {
+                       fprintf(stderr,"NOT IMPLEMENTED - FOR ALL\n");
+               }
+               else
+               {
+                       sprintf(buffer,"%s/%s",buffer,parent_key_value);
+
+                       keyEntry = keyNew(0);
+                       if (kdbbGetFullKeyName (handle, parent_key_value, parentKey, keyEntry) == -1) 
+                       {
+                               errno = errnosave;
+                               keyDel (keyEntry);
+                               keyDel (appended);
+                               return -1; 
+                       }
+               
+                       if (keyNeedStat(parentKey)) set_bit (keyEntry->flags, KEY_FLAG_STAT);
+                       if (kdbGetKey_filesys(handle,keyEntry) != -1)
+                       {
+                               keyflag_t semiflag;
+                               /* Remove the SYNC flag */
+                               semiflag= KEY_FLAG_SYNC;
+                               semiflag=~semiflag;
+                               keyEntry->flags &= semiflag;
+                               ksAppendKey(appended,keyEntry);
+                       }
+
+                       ksClear (returned); /* delete all other keys */
+                       ksAppend (returned, appended);
+                       ksDel (appended);
+
+                       errno = errnosave;
+
+                       keyDel (keyEntry);
+               }
+       }
+       else
+#endif
+       {
+       parentDir=opendir(buffer);
+
+       /* Check if Key is not a directory or doesn't exist.
+        * Propagate errno */
+       if (!parentDir) {
+               errno=KDB_ERR_NODIR;
+               ksDel (appended);
+               errno = errnosave;
+               return -1;
+       }
+
+       /* add the parentKey itself
+       Return value of kdbGetKey_filesys is irrelevant, because we already know its a directory
+       keyEntry=keyDup(parentKey);
+       kdbGetKey_filesys(handle,keyEntry);
+       ksAppendKey(appended,keyEntry);
+*/
+       while ((entry=readdir(parentDir))) {
+
+               /* Ignore '.', '..' and file containing information about directory itself */
+               if (!strcmp(entry->d_name,".") ||
+                               !strcmp(entry->d_name,"..") ||
+                               !strcmp(entry->d_name,DIR_FILENAME))
+                       continue;
+
+               keyEntry = keyNew(0);
+               if (kdbbGetFullKeyName (handle, entry->d_name, parentKey, keyEntry) == -1)
+               {
+                       closedir (parentDir);
+                       errno = errnosave;
+                       keyDel (keyEntry);
+                       keyDel (appended);
+                       return -1;
+               }
+
+               found = ksLookup (returned, keyEntry, KDB_O_WITHOWNER|KDB_O_POP);
+               if (found)
+               {
+                       keyDel (keyEntry);
+                       keyEntry = found; /* use found instead */
+               }
+               if (keyNeedStat(parentKey)) set_bit (keyEntry->flags, KEY_FLAG_STAT);
+               if (kdbGetKey_filesys(handle,keyEntry) != -1)
+               {
+                       keyflag_t semiflag;
+                       /* Remove the SYNC flag */
+                       semiflag= KEY_FLAG_SYNC;
+                       semiflag=~semiflag;
+                       keyEntry->flags &= semiflag;
+                       ksAppendKey(appended,keyEntry);
+               }
+               else keyDel (keyEntry);
+       } /* while(readdir) */
+
+       closedir(parentDir);
+
+       ksClear (returned); /* delete all other keys */
+       ksAppend (returned, appended);
+       ksDel (appended);
+
+       errno = errnosave;
+       }
+       return returned->size;
+}
+
+
+
+/**
+ * A probably inefficient implementation for kdbSet()
+ * method, but in case of filesys it is also the most effective one, because every
+ * key is independed.
+ *
+ * @see kdbSet(), kdbSet_backend()
+ * @return 0 on success
+ * @return -1 on failure and @c errno is propagated
+ */
+ssize_t kdbSet_filesys(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int errnosave = errno;
+       int db_trans = 0;
+
+       Key *current=ksCurrent(returned);
+
+       if (!current) current=ksNext(returned);
+
+#ifdef SQLFS_BACKEND
+       if (keyGetNamespace(current) == KEY_NS_USER) 
+               db_trans = 1;
+       if(db_trans) sqlite_begin_transaction();
+#endif
+
+       while (current) {
+               if (keyNeedRemove(current))
+               {
+                       if (kdbRemoveKey_filesys (handle, current))
+                       {
+                               errno = errnosave;
+
+#ifdef SQLFS_BACKEND
+                               if(db_trans) sqlite_rollback_transaction();
+#endif
+                               return -1;
+                       }
+               }
+               else if (keyNeedSync(current))
+               {
+                       if (kdbSetKey_filesys(handle,current))
+                       {
+                               errno = errnosave;
+
+#ifdef SQLFS_BACKEND
+                               if(db_trans) sqlite_rollback_transaction();
+#endif
+                               return -1;
+                       }
+               }
+               current=ksNext(returned);
+       }
+
+#ifdef SQLFS_BACKEND
+       if(db_trans) sqlite_commit_transaction();
+#endif
+
+       errno = errnosave;
+       return 0;
+}
+
+
+/*************************************
+ *           Helpers                 *
+ *************************************/
+
+
+
+int kdbGetKey_filesys(KDB *handle, Key *key) {
+       char keyFilename[MAX_KEY_LENGTH];
+       struct stat keyFilenameInfo;
+       int fd;
+       ssize_t pos;
+       keyflag_t semiflag;
+       FILE *input;
+
+
+       pos=kdbbGetFullFilename(handle,key,keyFilename,sizeof(keyFilename));
+       if (pos == -1) return -1; /* something is wrong */
+
+       stat(keyFilename,&keyFilenameInfo);
+       keyFromStat(key,&keyFilenameInfo);
+
+       if (keyNeedStat (key))
+       {
+               /* Remove the SYNC flag */
+               semiflag= KEY_FLAG_SYNC;
+               semiflag=~semiflag;
+               key->flags &= semiflag;
+               return 0;
+       }
+
+       if ((fd=open(keyFilename,O_RDONLY))==-1)
+       {
+               if (errno==ENOTDIR) errno=KDB_ERR_NOTFOUND;
+               return -1;
+       }
+
+       if (keyIsDir(key)) {
+               close(fd);
+               strcat(keyFilename,"/");
+               strcat(keyFilename,DIR_FILENAME);
+               if ((fd=open(keyFilename,O_RDONLY))!=-1)
+               {
+                       if(NULL == (input=fdopen(fd,"r"))) {
+                               close(fd);
+                               return -1;
+                       }
+                       kdbbReadLock (input);
+                       if (keyFileUnserialize(key,input))
+                       {
+                               kdbbUnlock (input);
+                               fclose(input);
+                               return -1;
+                       }
+                       kdbbUnlock (input);
+                       fclose(input);
+               }
+       } else {
+               if(NULL == (input=fdopen(fd,"r"))) {
+                       close(fd);
+                       return -1;
+               }
+               kdbbReadLock (input);
+               if (keyFileUnserialize(key,input)) {
+                       kdbbUnlock (input);
+                       fclose(input);
+                       return -1;
+               }
+               kdbbUnlock (input);
+               fclose(input);
+       }
+
+       /* Remove the SYNC flag */
+       semiflag=KEY_FLAG_SYNC;
+       semiflag=~semiflag;
+       key->flags &= semiflag;
+
+       return 0;
+}
+
+
+int keyToFile(KDB *handle, Key *key, char *keyFilename) {
+       int fd=0;
+       FILE *output=0;
+       int errnosave;
+
+#if 1
+        /* For security(DAC enable), this code is modified by sangjung.woo@samsung.com */
+       /* Open key file with its full file name */
+        mode_t temp;
+        temp = umask(0000);
+       fd=open(keyFilename,O_CREAT | O_RDWR | O_TRUNC, key->mode);
+        umask(temp);
+#endif
+       if (fd==-1) return -1;
+       if (!(output=fdopen(fd,"w+"))) return -1;
+
+       kdbbWriteLock (output);
+
+       /* Set permissions, might fail without problems */
+       errnosave = errno;
+#ifndef TUNNING_ELEKTRA_0_7
+       fchown(fd,key->uid,key->gid);
+       fchmod(fd,key->mode);
+#endif
+       errno = errnosave;
+/*
+   fstat(fd, &sb);
+   if(sb.st_uid != getuid())
+      fchown(fd,key->uid,key->gid);
+   if(sb.st_mode & 0003)
+      fchmod(fd,key->mode);
+*/
+
+       /* Write file content */
+       if (keyFileSerialize(key,output)) {
+               kdbbUnlock (output);
+               fclose(output);
+               return -1;
+       }
+
+       kdbbUnlock (output);
+       fclose(output);
+
+       return 0;
+}
+
+int kdbSetKey_filesys(KDB *handle, Key *key) {
+       char keyFilename[MAX_KEY_LENGTH];
+       char genericBuffer[MAX_PATH_LENGTH];
+       char *cursor=0, *last=0;
+       ssize_t pos=0;
+       keyflag_t semiflag=0;
+       struct stat stated;
+       int rc=0;
+       int exists=0;
+       int errnosave;
+
+       pos=kdbbGetFullFilename(handle,key,keyFilename,sizeof(keyFilename));
+       if (pos == -1) return -1; /* Something is wrong. Propagate errno. */
+
+       exists = ! stat(keyFilename,&stated);
+
+       if (! exists && errno!=ENOENT && errno!=ENOTDIR)
+               return -1; /* propagate errno */
+
+       if (! exists) {
+               /* Entry does not exist in the filesystem. */
+               /* It is our job to create it. */
+               /* Now this block will take care to check or create
+                  entire file path to key */
+
+               /* check if parent dir already exists */
+               last=strrchr(keyFilename,(int)'/');
+               strncpy(genericBuffer,keyFilename,last-keyFilename);
+               genericBuffer[last-keyFilename]=0;
+
+               /* first test existence of immediate parent */
+               if (stat(genericBuffer,&stated) || !S_ISDIR(stated.st_mode)) {
+                       /* create all path recursively until before our basename */
+                       mode_t parentMode =  KEY_DEF_MODE | KEY_DEF_DIR;
+
+                       last   =  strrchr(keyFilename,'/');
+                       cursor =   strchr(keyFilename,'/'); cursor++; /* skip first occurence */
+                       if (!last || !cursor) { /* bizarre key name */
+                               errno=KDB_ERR_INVALIDKEY;
+                               return -1;
+                       }
+
+                       /* The deep dir maker loop */
+                       for (cursor=strchr(cursor,'/');
+                                                       cursor && (cursor <= last);
+                                                       cursor=strchr(cursor,'/')) {
+
+                               strncpy(genericBuffer,keyFilename,cursor-keyFilename);
+                               genericBuffer[cursor-keyFilename]=0;
+
+#ifdef HAVE_WIN32
+                               if (mkdir(genericBuffer)<0) {
+#else
+                               if (mkdir(genericBuffer,parentMode)<0) {
+#endif
+               if (errno==EEXIST) {
+                                               /* There is something there already. Check. */
+                                               stat(genericBuffer,&stated);
+
+                                               if (!S_ISDIR(stated.st_mode)) {
+                                                       /* Trying to create a dir on something that is not. */
+                                                       /* Convert into a dir in a very low level way. */
+                                                       char tempName[MAX_PATH_LENGTH];
+                                                       char finalName[MAX_PATH_LENGTH];
+
+                                                       sprintf(tempName,"%s.%d",genericBuffer,rand());
+
+                                                       if (rename(genericBuffer,tempName)<0) return -1;
+
+                                                       /* Now tries to create the dir again */
+#ifdef HAVE_WIN32
+                                                       if (mkdir(genericBuffer)<0) {
+#else
+                                                       if (mkdir(genericBuffer,parentMode)<0) {
+#endif
+                                                               /* Rollback on failure */
+                                                               rename(tempName,genericBuffer);
+                                                               return -1;
+                                                       }
+
+                                                       sprintf(finalName,"%s/%s",genericBuffer,DIR_FILENAME);
+
+                                                       if (rename(tempName,finalName)<0) return -1;
+                                               } /* Regular key converted into a dir key. */
+                                       } else return -1; /* propagate errno */
+                               }
+
+#ifdef HAVE_WIN32
+                               /* Since mkdir on win32 can't set a mode for us we need to do it manually */
+                               if(chmod(genericBuffer, parentMode) < 0)
+                                       return -1;
+#endif
+
+                               cursor++;
+                       } /* END OF: dir maker loop */
+               } /* END OF: parent is not there */
+       } /* END OF: !exists (or, preparation for key creation) */
+
+       if (keyIsDir(key)) {
+               if ( exists && !S_ISDIR(stated.st_mode) ) {
+                       /* New key is dir, but file there is not */
+                       /* Remove the file */
+                       rc=unlink(keyFilename);
+                       if (rc && errno!=ENOENT) return -1;
+               }
+
+#ifdef HAVE_WIN32
+               if (mkdir(keyFilename)<0 && errno!=EEXIST)
+                       return -1; /* propagate errno */
+               /* Since mkdir on win32 can't set a mode for us we need to do it manually */
+               if (chmod(keyFilename, key->mode)<0) return -1;
+#else
+               if (mkdir(keyFilename,key->mode)<0 && errno!=EEXIST)
+                       return -1;
+#endif
+
+               /* Dir permissions... */
+               errnosave = errno;
+               chown(keyFilename,key->uid,key->gid);
+               chmod(keyFilename,key->mode);
+               errno = errnosave;
+
+               /* Save Value, Comment and Type... */
+               strcat(keyFilename,"/"); /* Append a filename for key data and comment */
+               strcat(keyFilename,DIR_FILENAME);
+               rc=keyToFile(handle,key,keyFilename);
+       } else { /* key is not dir */
+               if ( exists && S_ISDIR(stated.st_mode) ) {
+                       /* but inode there is a dir */
+                       DIR *dir=0;
+                       int hasChild=0;
+                       struct dirent *entry=0;
+
+                       /* Check if it has child keys */
+                       dir=opendir(keyFilename);
+                       if (!dir) return -1;
+                       while ( !hasChild && (entry=readdir(dir)) ) {
+                               /* Ignore '.' and '..' directory entries */
+                               if (!strcmp(entry->d_name,".") ||
+                                                                !strcmp(entry->d_name,"..") ||
+                                                                !strcmp(entry->d_name,DIR_FILENAME))
+                                       continue;
+                               else hasChild=1;
+                       }
+                       closedir(dir);
+
+                       if (hasChild) {
+                               /* Dir contains files, so can't un-dir it */
+                               errno=KDB_ERR_NOTEMPTY;
+                               return -1;
+                       }
+
+                       /* We'll have to transform it to a non-dir key, so... */
+                       /* Remove the directory file if any */
+                       sprintf(genericBuffer,"%s/%s",keyFilename,DIR_FILENAME);
+                       rc=unlink(genericBuffer);
+                       if (rc && errno!=ENOENT) return -1;
+
+                       /* Remove the dir */
+                       rc=rmdir(keyFilename);
+                       if (rc) return -1;
+               }
+
+               /* Its a plain key, so simply write to disk */
+               rc=keyToFile(handle,key,keyFilename);
+       } /* END OF: key is not dir */
+
+       if (rc == 0) {
+               /* Remove the SYNC flag */
+               semiflag=KEY_FLAG_SYNC;
+               semiflag=~semiflag;
+               key->flags &= semiflag;
+       }
+
+       return rc;
+}
+
+
+int kdbRemoveKey_filesys(KDB *handle, Key *key) {
+       char fileName[MAX_PATH_LENGTH];
+       ssize_t rc;
+       struct stat stated;
+       keyflag_t semiflag;
+
+       rc=kdbbGetFullFilename(handle,key,fileName,sizeof(fileName));
+       if (rc == -1) return -1;
+
+       if (stat(fileName,&stated)) return -1;
+
+       if ( S_ISDIR(stated.st_mode) ) {
+               /* inode there is a dir */
+               DIR *dir=0;
+               int hasChild=0;
+               int hasDataEntry=0;
+               char dataFilename[MAX_PATH_LENGTH];
+               struct dirent *entry=0;
+
+               /* Check if it has child keys */
+               dir=opendir(fileName);
+               if (!dir) return -1;
+               while ( !hasChild && (entry=readdir(dir)) ) {
+                       /* Ignore DIR_FILENAME, '.' and '..' directory entries */
+                       if (!strcmp(entry->d_name,".") ||
+                                                       !strcmp(entry->d_name,"..") ||
+                                                       (hasDataEntry=!strcmp(entry->d_name,DIR_FILENAME)))
+                               continue;
+                       else hasChild=1;
+               }
+               closedir(dir);
+
+               if (hasChild) {
+                       /* Dir contains files, so can't un-dir it */
+                       errno=ENOTEMPTY;
+                       return -1;
+               }
+
+               if (hasDataEntry) {
+                       /* We'll have to transform it to a non-dir key, so... */
+                       /* Remove the directory file if any */
+                       sprintf(dataFilename,"%s/%s",fileName,DIR_FILENAME);
+                       rc=remove(dataFilename);
+                       if (rc && errno!=ENOENT) return -1;
+               }
+       }
+
+       /* Remove the SYNC flag */
+       semiflag=KEY_FLAG_SYNC;
+       semiflag=~semiflag;
+       key->flags &= semiflag;
+
+       return remove(fileName);
+}
+
+
+/**
+ * Makes a key object from its serialized form, coming from a file.
+ *
+ * @param key the pre-initialized key that will contain our data.
+ * @param input the opened file from which we want to read.
+ * @return 0 on success.
+ * @ingroup internals
+ */
+int keyFileUnserialize(Key *key,FILE *input) {
+       char generalBuffer[BUFFER_SIZE] = {0,};
+       size_t currentBufferSize;
+
+       char version[10] = {0,};
+       unsigned int nversion=0;
+       char type[5] = {0,};
+       char *data=0;
+       size_t dataSize=0;
+       char *comment=0;
+       size_t commentSize=0;
+
+       int readComment=1;
+       int eof=0;
+
+       /* The serialized format is
+          -------------------------
+          RG001\n
+          type\n
+          comment (with newlines)\n
+          <DATA>\n
+          The data encoded as text
+          -------------------------
+       */
+
+       if (!fgets(version, sizeof(version), input)) return -1;
+       if (strncmp(version,"RG",2)) {
+               /* Doesn't look like a key file */
+               errno=KDB_ERR_INVALIDKEY;
+               return -1;
+       }
+
+       nversion=atoi(version+2);
+       if (!nversion || nversion != RG_KEY_FORMAT_VERSION) {
+               errno=KDB_ERR_INVALIDKEY;
+               return -1;
+       }
+
+       if (!fgets(type,    sizeof(type),    input)) return -1;
+
+       while (readComment) {
+               if (fgets(generalBuffer,sizeof(generalBuffer),input)) {
+                       if (memcmp(generalBuffer,"<DATA>\n\0",8)) {
+                               /* This is not the beginning of the data part so it is part of comment */
+                               currentBufferSize=kdbiStrLen(generalBuffer);
+                               if (!comment) {
+                                       comment=(char *)malloc(commentSize=currentBufferSize);
+                                       strcpy(comment,generalBuffer);
+                               } else {
+                                       char *buffer=0;
+
+                                       --commentSize; /* remove awareness of previous \0 */
+                                       buffer=malloc(commentSize+currentBufferSize);
+                                       strcpy(buffer,comment);
+                                       strcat(buffer,generalBuffer);
+                                       comment=realloc(comment,commentSize+=currentBufferSize);
+                                       assert(comment!=NULL);
+                                       strcpy(comment,buffer);
+                                       free(buffer);
+                               }
+                       } else readComment=0;
+               } else {
+                       readComment=0;
+                       eof=1;
+               }
+       }
+
+       /* Remove last \n */
+       if (commentSize > 1 && (*(comment+commentSize-2) == '\n')) {
+               *(comment+commentSize-2)=0;
+               --commentSize;
+       }
+
+       if (comment && kdbbUTF8Engine(UTF8_FROM,&comment,&commentSize)) {
+               free(comment);
+               return -1;
+       }
+
+       /* Now read the data section */
+       if (!eof) {
+               while (fgets(generalBuffer,sizeof(generalBuffer),input)) {
+                       currentBufferSize=strlen(generalBuffer);
+                       if (!data) {
+                               data=(char *)malloc(dataSize=(currentBufferSize+1));
+                               strcpy(data,generalBuffer);
+                       } else {
+                               char *buffer=0;
+
+                               buffer=malloc(dataSize+currentBufferSize);
+                               strcpy(buffer,data);
+                               strcat(buffer,generalBuffer);
+                               data=realloc(data,dataSize+=currentBufferSize);
+                               assert(data!=NULL);
+                               strcpy(data,buffer);
+                               free(buffer);
+                       }
+               }
+       }
+
+       /* Put in the Key object */
+       keySetComment(key,comment);
+       if (comment) free(comment);
+       keySetType(key,atoi(type));
+       if (!dataSize) {
+               keySetRaw(key,0,0);
+               return 0;
+       }
+
+       if (keyIsString (key))
+       {
+               if (kdbbUTF8Engine(UTF8_FROM,&data,&dataSize)) {
+                       free(data);
+                       return -1;
+               }
+               keySetRaw(key,data,dataSize);
+       } else if (keyIsBinary(key))
+       {
+               /* Binary decoded data. */
+               char *decoded=0;
+               size_t decodedSize;
+
+               /* raw data is maximum half the size of text-decoded data */
+               decodedSize=dataSize/2;
+
+               decoded=malloc(decodedSize);
+               if (!(decodedSize=kdbbDecode(data,decoded))) return -1;
+               keySetRaw(key,decoded,decodedSize);
+               free(decoded);
+       }
+
+       free(data);
+
+       return 0;
+}
+
+
+
+/**
+ * Writes the serialized form of the given key onto a file.
+ *
+ * This is the counterpart of keyFileUnserialize().
+ * @param key the key we want to serialize.
+ * @param output the opened file to be written.
+ * @return 0 on success.
+ * @see keyFileUnserialize()
+ * @ingroup internals
+ */
+int keyFileSerialize(Key *key, FILE *output) {
+       /* The serialized format is
+          -------------------------
+          RG001\n
+          type\n
+          comment (with newlines)\n
+          <DATA>\n
+          The data encoded as text
+          -------------------------
+       */
+
+       fprintf(output,"RG%03d\n",RG_KEY_FORMAT_VERSION);
+       fprintf(output,"%d\n", keyGetType (key));
+       if (key->comment) {
+               if (kdbbNeedsUTF8Conversion()) {
+                       size_t convertedCommentSize=key->commentSize;
+                       char *convertedComment=malloc(convertedCommentSize);
+
+                       memcpy(convertedComment,key->comment,key->commentSize);
+                       if (kdbbUTF8Engine(UTF8_TO,&convertedComment,&convertedCommentSize)) {
+                               free(convertedComment);
+                               return -1;
+                       }
+                       fprintf(output,"%s\n",convertedComment);
+                       free(convertedComment);
+               } else fprintf(output,"%s\n",key->comment);
+       }
+
+       fputs("<DATA>\n",output);
+#ifndef TUNNING_ELEKTRA_0_7
+       fflush(output);
+#endif
+       if (key->dataSize) {
+               /* There is some data to write */
+               if (keyIsString (key)) {
+                       /* String or similar type of value */
+                       if (kdbbNeedsUTF8Conversion()) {
+                               size_t convertedDataSize=key->dataSize;
+                               char *convertedData=malloc(convertedDataSize);
+
+                               memcpy(convertedData,key->data,key->dataSize);
+                               if (kdbbUTF8Engine(UTF8_TO,&convertedData,&convertedDataSize)) {
+                                       free(convertedData);
+                                       return -1;
+                               }
+                               fprintf(output,"%s",convertedData);
+                               free(convertedData);
+                       } else fputs(key->data,output);
+               } else if (keyIsBinary (key)) {
+                       /* Binary values */
+                       char *encoded=malloc(3*key->dataSize+1);
+                       size_t encodedSize;
+
+                       encodedSize=kdbbEncode(key->data,key->dataSize,encoded);
+                       fwrite(encoded,encodedSize,1,output);
+                       free(encoded);
+               } // else unhandeled: has data but neighter string nor binary, so drop the data
+       }
+       return 0;
+}
+
+
+/**
+ * Stats a key file.
+ * Will not open the key file, but only stat it, not changing its last
+ * access time.
+ * The resulting key will have all info, but comment, value and value type.
+ *
+ * @param stat the stat structure to get metadata from
+ * @param key object to be filled with info from stat structure
+ * @return 0 on success, -1 otherwise
+ * @ingroup internals
+ */
+int keyFromStat(Key *key,struct stat *stat) {
+       keySetUID(key,stat->st_uid);
+       keySetGID(key,stat->st_gid);
+
+       // remove directory bit
+       keySetMode(key,stat->st_mode & 0777);
+
+       key->atime=stat->st_atime;
+       key->mtime=stat->st_mtime;
+       key->ctime=stat->st_ctime;
+       return 0;
+}
+
+
+
+KDBEXPORT(filesys)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_filesys,
+               KDB_BE_CLOSE,   &kdbClose_filesys,
+               KDB_BE_GET,     &kdbGet_filesys,
+               KDB_BE_SET,     &kdbSet_filesys,
+               KDB_BE_VERSION, BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Avi Alkalay <avi@unix.sh>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION,
+                       "Every key is represented by a single file",
+               KDB_BE_END);
+}
diff --git a/src/backends/filesys/filesys.h b/src/backends/filesys/filesys.h
new file mode 100644 (file)
index 0000000..525c99a
--- /dev/null
@@ -0,0 +1,144 @@
+/***************************************************************************
+            filesys.h  -  A filesystem backend implementation for Elektra
+                             -------------------
+    begin                : Mon Dec 25 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/***************************************************************************
+ *                                                                         *
+ *   This is the implementation of a filesystem backend for the            *
+ *   Elektra. Each Key is a file in the filesystem.                        *
+ *   It is as secure as filesystem security. It is as reliable             *
+ *   as filesystem. It uses only standards C calls, which makes it         *
+ *   usable by very low level or early boot stage software, like           *
+ *   /sbin/init.                                                           *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <kdbbackend.h>
+
+#define BACKENDNAME "filesys"
+#define BACKENDVERSION "0.2.1"
+
+
+/* When FORMAT_VERSION changes, FORMAT must change too. */
+#define RG_KEY_FORMAT_VERSION   2
+#define RG_KEY_FORMAT           "RG00" RG_KEY_FORMAT_VERSION
+
+
+char *DIR_FILENAME="%%dirdata";
+
+
+int kdbbWriteLock (FILE *f);
+int kdbbReadLock (FILE *f);
+int kdbbUnlock (FILE *f);
+
+/* These are some helpers we'll define bellow */
+int keyFileUnserialize(Key *key,FILE *input);
+size_t kdbGetFullFilename(const Key *forKey,char *returned,size_t maxSize);
+int keyFileSerialize(Key *key, FILE *output);
+int keyFromStat(Key *key,struct stat *stat);
+
+
+
+int kdbGetKey_filesys(KDB *handle, Key *key);
+int kdbRemoveKey_filesys(KDB *handle, Key *key);
+int kdbSetKey_filesys(KDB *handle, Key *key);
+
+
+
+/*****************
+ * Error codes
+ *****************/
+
+/*
+ * Error codes.
+ *
+ * Methods return -1 or NULL on failure. To indicate what the problem
+ * was @c errno is propagated to one of the values below.
+ *
+ * The idea is to keep compatibility to POSIX @c errno system, so each library
+ * error code maps to some POSIX E* error. Some mappings really make no sense,
+ * so to detect errors use the following error names, and to detect
+ * system's, use the naming conventions documented in @c errno man page.
+ *
+ * A very robust program should check the return value and @c errno
+ * after each API call.
+ *
+ * @ingroup kdb
+ * @see kdbhGetError(), kdbhSetError()
+ * @see kdbStrError()
+ */
+enum KDBErr {
+       KDB_ERR_OK=0,                   /*!< No error */
+
+       /* Errors related to invalid/uninitialized objects */
+       KDB_ERR_NULLKEY=EINVAL,         /*!< Null Key object */
+
+       /* Errors related to bad key names or keys not found */
+       KDB_ERR_NOTFOUND=ENOENT,        /*!< Key was not found */
+       KDB_ERR_INVALIDKEY=ESRCH,       /*!< Key name not valid */
+
+       /* Errors related to empty internal key properties */
+       KDB_ERR_NOKEY=EFAULT,           /*!< Key has no name */
+       KDB_ERR_NODATA=ENODEV,          /*!< Key has no data */
+       KDB_ERR_NODESC=ENOMSG,          /*!< Key has no comment */
+       KDB_ERR_NOOWNER=EDOM,           /*!< Key has no user domain set */
+       KDB_ERR_NOGROUP=ECHILD,         /*!< Key has no group */
+       KDB_ERR_NOTIME=ENOTTY,          /*!< Key has no access time set */
+       KDB_ERR_OVERFLOW=EOVERFLOW,     /*!< Key has too large value to convert it to type */
+
+       /* Errors related to permissions*/
+       KDB_ERR_NOCRED=EACCES,          /*!< No credentials to access resource */
+       KDB_ERR_NOTEMPTY=ENOTEMPTY,     /*!< Tried to delete directory key with children */
+
+       /* Errors related to no memory, failed internal operations */
+       KDB_ERR_TRUNC=ERANGE,           /*!< Buffer was too small */
+       KDB_ERR_TOOLONG=ENAMETOOLONG,   /*!< String is too big for buffer */
+       KDB_ERR_NOMEM=ENOMEM,           /*!< Out of memory */
+       KDB_ERR_TYPEMISMATCH=EBADF,     /*!< Failed to convert key data due to data type */
+       KDB_ERR_CONVERT=EILSEQ,         /*!< Could not utf8 convert data or name */
+
+       /* Errors related to backend access or opening */
+       KDB_ERR_NOSYS=ENOSYS,           /*!< Backend method not implemented */
+       KDB_ERR_EBACKEND=EIO,           /*!< Error opening backend */
+       KDB_ERR_BACKEND=EAGAIN,         /*!< Error in the backend in reading or writing the data */
+       KDB_ERR_CONFIG=ENXIO,           /*!< Essential configuration for mountpoint missing */
+
+       /* Errors related to backend storage */
+       KDB_ERR_NODIR=ENOTDIR,          /*!< The file where key should be is not a directory key */
+       KDB_ERR_NOSPACE=ENOSPC,         /*!< No space left on device */
+       KDB_ERR_NOLOCK=ENOLCK,          /*!< Could not acquire lock */
+       KDB_ERR_NOTSUP=ENOTSUP,         /*!< Operation not supported could not set or delete key */
+       KDB_ERR_PERM=EPERM,             /*!< Could not access data because of permission problem */
+       KDB_ERR_PIPE=EPIPE,             /*!< Broken Pipe */
+       KDB_ERR_EROFS=EROFS,            /*!< Read only filesystem */
+       KDB_ERR_EXDEV=EXDEV             /*!< Improper link */
+};
diff --git a/src/backends/fstab/Makefile.am b/src/backends/fstab/Makefile.am
new file mode 100644 (file)
index 0000000..b744823
--- /dev/null
@@ -0,0 +1,20 @@
+
+# $Id$
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+noinst_LIBRARIES = libelektra-fstab.a
+libelektra_fstab_a_SOURCES = fstab.c ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_fstab_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+backend_LTLIBRARIES = libelektra-fstab.la
+libelektra_fstab_la_SOURCES = fstab.c ../../include/kdb.h ../../include/kdbbackend.h 
+libelektra_fstab_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_fstab_la_LIBADD = ../../libelektra/libelektra.la
+libelektra_fstab_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
diff --git a/src/backends/fstab/README b/src/backends/fstab/README
new file mode 100644 (file)
index 0000000..0c4b603
--- /dev/null
@@ -0,0 +1,53 @@
+See http://www.libelektra.org/Fstab for current version
+
+Fstab is a implementation of a parser and generator of the /etc/fstab file.
+It can be used as a [[Backends|Backend]] for Elektra.
+
+== Storage ==
+
+The elektra representation will be:
+ pseudoname/device
+ pseudoname/mpoint
+ pseudoname/type
+ pseudoname/options
+ pseudoname/dumpfreq
+ pseudoname/passno
+
+for every column in the /etc/fstab file.
+
+The pseudoname can be any name for setting keys,
+the will be generated when getting keys, so don't
+expect the same name.
+
+the directory / will be called
+ rootfs
+
+all swap devices will be called
+ swapXX
+with a number from 00 on for XX
+
+else the mountpoint without / char will be used.
+
+
+== Capabilities ==
+
+setmntent is used and restrict the capabilities in many ways.
+At one point you can't use any comments, they will be generated
+automatically.
+
+At the other point there is the issue with the pseudonames,
+you can't rely on the pseudoname you have set.
+
+The biggest issue is that you can't change or delete existing
+entries. All entries you set will be appended to the other filesystems.
+
+So if you get the filesystems and change the type of the filesystem
+of the rootfs and set it again the resulting fstab will be like:
+ /dev/sda6       /               ext3>----   >----defaults,errors=remount-ro 0 1
+ /dev/sda6       /               jfs>----   >----defaults,errors=remount-ro 0 1
+
+which will be not like you desired!
+
+== Portability ==
+
+setmntent is used, so it is only conforming to BSD 4.3 and linux.
diff --git a/src/backends/fstab/fstab.c b/src/backends/fstab/fstab.c
new file mode 100644 (file)
index 0000000..8c1f69a
--- /dev/null
@@ -0,0 +1,330 @@
+/***************************************************************************
+            fstab.c  -  Access the /etc/fstab file
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2004 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This is a backend that takes /etc/fstab file as its backend storage.  *
+ *   The kdbGet() method will parse /etc/fstab and generate a              *
+ *   valid key tree. The kdbSet() method will take a KeySet with valid     *
+ *   filesystem keys and print an equivalent regular fstab in stdout.      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <fstab.h>
+
+int kdbOpen_fstab(KDB *handle)
+{
+       KDBCap * cap = kdbhGetCapability(handle);
+       const void *f;
+       KeySet *ks;
+       Key *k;
+
+       cap->onlyFullGet=1;
+       cap->noStat=1;
+
+       cap->onlyRemoveAll=1;
+       cap->onlyAddKeys=1;
+       cap->onlyFullSet=1;
+
+       cap->noComment=1;
+       cap->noUID=1;
+       cap->noGID=1;
+       cap->noMode=1;
+       cap->noATime=1;
+       cap->noMTime=1;
+       cap->noCTime=1;
+       cap->noRemove=1;
+       cap->noMount=1;
+       cap->noBinary=1;
+       cap->noTypes=1;
+       cap->noError=1;
+
+       cap->noLock = 1;
+       cap->noThread = 1;
+
+       ks = kdbhGetConfig (handle);
+       ksRewind (ks);
+       while ((k = ksNext (ks)) != 0)
+       {
+               f = keyName (k);
+               if (f) f = strrchr (f, '/');
+               if (f && strcmp (f, "/path") == 0) {
+                       void *data=malloc(keyGetValueSize(k));
+                       keyGetString (k, data, keyGetValueSize(k));
+                       kdbhSetBackendData (handle, data);
+               }
+       }
+       if (!kdbhGetBackendData (handle)) kdbhSetBackendData (handle, kdbiStrDup (FSTAB_PATH));
+       /* backend initialization logic */
+#if DEBUG && VERBOSE
+       printf ("open fstab backend with %s\n", kdbhGetBackendData (handle));
+#endif
+       return 0;
+}
+
+
+
+
+int kdbClose_fstab(KDB *handle)
+{
+       /* free all backend resources and shutdown */
+       free(kdbhGetBackendData(handle));
+#if DEBUG && VERBOSE
+       printf ("close fstab backend\n");
+#endif
+       return 0; /* success */
+}
+
+#define MAX_NUMBER_SIZE 10
+
+ssize_t kdbGet_fstab(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int errnosave = errno;
+       ssize_t nr_keys = 0;
+#ifndef __APPLE__
+       Key *key;
+       Key *dir;
+       FILE *fstab=0;
+       struct mntent *fstabEntry;
+       char fsname[MAX_PATH_LENGTH];
+       char buffer[MAX_NUMBER_SIZE];
+       const char *mountpointname = keyName(kdbhGetMountpoint(handle));
+       const char *parentname = keyName(parentKey);
+
+#if DEBUG && VERBOSE
+       printf ("get fstab %s, point: %s\n", keyName(parentKey), mountpointname);
+#endif
+
+       if (strcmp (mountpointname, parentname)) return 0;
+
+       ksClear (returned);
+       key = keyDup (parentKey);
+       keySetDir(key);
+       ksAppendKey(returned, key);
+       nr_keys ++;
+       key->flags &= ~KEY_FLAG_SYNC;
+
+       fstab=setmntent(kdbhGetBackendData(handle), "r");
+       if (fstab == 0)
+       {
+               /* propagate errno */
+               errno = errnosave;
+               return -1;
+       }
+       
+       while ((fstabEntry=getmntent(fstab)))
+       {
+               unsigned int swapIndex=0;
+               nr_keys += 7;
+
+               /* Some logic to define the filesystem name when it is not
+                * so obvious */
+               if (!strcmp(fstabEntry->mnt_type,"swap")) {
+                       sprintf(fsname,"swap%02d",swapIndex);
+                       swapIndex++;
+               } else if (!strcmp(fstabEntry->mnt_dir,"none")) {
+                       strcpy(fsname,fstabEntry->mnt_type);
+               } else if (!strcmp(fstabEntry->mnt_dir,"/")) {
+                       strcpy(fsname,"rootfs");
+               } else {
+                       /* fsname will be the mount point without '/' char */
+                       char *slash=0;
+                       char *curr=fstabEntry->mnt_dir;
+                       fsname[0]=0;
+                       
+                       while((slash=strchr(curr,PATH_SEPARATOR))) {
+                               if (slash==curr) {
+                                       curr++;
+                                       continue;
+                               }
+                               
+                               strncat(fsname,curr,slash-curr);
+                               curr=slash+1;
+                       }
+                       strcat(fsname,curr);
+               }
+               
+               /* Include only the filesystem pseudo-names */
+               dir = keyDup (parentKey);
+               keyAddBaseName(dir, fsname);
+               keySetString(dir,"");
+               keySetComment(dir,"");
+               keySetMode(dir, 0664); /* TODO stat */
+               keySetComment (dir, "Filesystem pseudo-name");
+               ksAppendKey(returned,dir);
+
+               key = keyDup (dir);
+               keyAddBaseName(key, "device");
+               keySetString (key, fstabEntry->mnt_fsname);
+               keySetComment (key, "Device or Label");
+               ksAppendKey(returned, key);
+               key->flags &= ~KEY_FLAG_SYNC;
+
+               key = keyDup (dir);
+               keyAddBaseName(key, "mpoint");
+               keySetString (key, fstabEntry->mnt_dir);
+               keySetComment (key, "Mount point");
+               ksAppendKey(returned, key);
+               key->flags &= ~KEY_FLAG_SYNC;
+
+               key = keyDup (dir);
+               keyAddBaseName(key, "type");
+               keySetString (key, fstabEntry->mnt_type);
+               keySetComment (key, "Filesystem type.");
+               ksAppendKey(returned, key);
+               key->flags &= ~KEY_FLAG_SYNC;
+
+               key = keyDup (dir);
+               keyAddBaseName(key, "options");
+               keySetString (key, fstabEntry->mnt_opts);
+               keySetComment (key, "Filesystem specific options");
+               ksAppendKey(returned, key);
+               key->flags &= ~KEY_FLAG_SYNC;
+
+               key = keyDup (dir);
+               keyAddBaseName(key, "dumpfreq");
+               snprintf(buffer, MAX_NUMBER_SIZE, "%d",fstabEntry->mnt_freq);
+               keySetString (key, buffer);
+               keySetComment (key, "Dump frequency in days");
+               ksAppendKey(returned, key);
+               key->flags &= ~KEY_FLAG_SYNC;
+
+               key = keyDup (dir);
+               keyAddBaseName(key, "passno");
+               snprintf(buffer, MAX_NUMBER_SIZE, "%d",fstabEntry->mnt_passno);
+               keySetString (key, buffer);
+               keySetComment (key, "Pass number on parallel fsck");
+               ksAppendKey(returned, key);
+               key->flags &= ~KEY_FLAG_SYNC;
+
+               keySetDir (dir);
+               dir->flags &= ~KEY_FLAG_SYNC;
+       }
+       
+       endmntent(fstab);
+       
+#endif
+       errno = errnosave;
+       return nr_keys;
+}
+
+
+ssize_t kdbSet_fstab(KDB *handle, KeySet *ks, const Key *parentKey)
+{
+       int ret = 1;
+       int errnosave = errno;
+#ifndef __APPLE__
+       FILE *fstab=0;
+       Key *key=0;
+       char *basename = 0;
+       const void *rootname = 0;
+       struct mntent fstabEntry;
+       const char *mountpointname = keyName(kdbhGetMountpoint(handle));
+       const char *parentname = keyName(parentKey);
+
+#if DEBUG && VERBOSE
+       printf ("set fstab %s, point: %s\n", keyName(parentKey), mountpointname);
+#endif
+
+       if (strcmp (mountpointname, parentname)) return 0;
+
+       ksRewind (ks);
+       if ((key = ksNext (ks)) != 0 && keyNeedRemove (key))
+       {
+               unlink(kdbhGetBackendData(handle));
+               return ret;
+       } /*skip parent key*/
+
+       fstab=setmntent(kdbhGetBackendData(handle), "w");
+       memset(&fstabEntry,0,sizeof(struct mntent));
+
+       while ((key = ksNext (ks)) != 0)
+       {
+               ret ++;
+               basename=strrchr(keyName(key), '/')+1;
+#if DEBUG && VERBOSE
+               printf ("key: %s %s\n", keyName(key), basename);
+#endif
+               if (!strcmp (basename, "device"))
+               {
+                       fstabEntry.mnt_fsname=(char *)keyValue(key);
+               } else if (!strcmp (basename, "mpoint")) {
+                       fstabEntry.mnt_dir=(char *)keyValue(key);
+               } else if (!strcmp (basename, "type")) {
+                       fstabEntry.mnt_type=(char *)keyValue(key);
+               } else if (!strcmp (basename, "options")) {
+                       fstabEntry.mnt_opts=(char *)keyValue(key);
+               } else if (!strcmp (basename, "dumpfreq")) {
+                       fstabEntry.mnt_freq=atoi((char *)keyValue(key));
+               } else if (!strcmp (basename, "passno")) {
+                       fstabEntry.mnt_passno=atoi((char *)keyValue(key));
+               } else { // new rootname
+                       if (!rootname)
+                       {
+                               rootname = keyValue(key);
+                       } else {
+                               rootname = keyValue(key);
+#if DEBUG && VERBOSE
+                               fprintf(stdout, "first: %s   %s   %s   %s   %d %d\n",
+                                       fstabEntry.mnt_fsname,
+                                       fstabEntry.mnt_dir,
+                                       fstabEntry.mnt_type,
+                                       fstabEntry.mnt_opts,
+                                       fstabEntry.mnt_freq,
+                                       fstabEntry.mnt_passno);
+#endif
+                               addmntent(fstab, &fstabEntry);
+                               memset(&fstabEntry,0,sizeof(struct mntent));
+                       }
+               }
+       }
+
+       if (rootname)
+       {
+#if DEBUG && VERBOSE
+               fprintf(stdout, "last: %s   %s   %s   %s   %d %d\n",
+                       fstabEntry.mnt_fsname,
+                       fstabEntry.mnt_dir,
+                       fstabEntry.mnt_type,
+                       fstabEntry.mnt_opts,
+                       fstabEntry.mnt_freq,
+                       fstabEntry.mnt_passno);
+#endif
+               addmntent(fstab, &fstabEntry);
+       }
+       
+       endmntent(fstab);
+#endif
+       errno = errnosave;
+       return ret;
+}
+
+
+KDBEXPORT(fstab) {
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,           &kdbOpen_fstab,
+               KDB_BE_CLOSE,          &kdbClose_fstab,
+               KDB_BE_GET,            &kdbGet_fstab,
+               KDB_BE_SET,            &kdbSet_fstab,
+               KDB_BE_VERSION,        BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Markus Raab <elektra@markus-raab.org>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION,
+                       "Reads and writes /etc/fstab content",
+               KDB_BE_END);
+                       
+}
+
+
diff --git a/src/backends/gconf/Makefile.am b/src/backends/gconf/Makefile.am
new file mode 100644 (file)
index 0000000..b3a9f8b
--- /dev/null
@@ -0,0 +1,23 @@
+
+# $Id$
+
+#
+# Compile libelektra-gconf only if GConf was found
+#
+
+#if HAVE_GCONF
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+noinst_LIBRARIES = libelektra-gconf.a
+libelektra_gconf_a_SOURCES = gconf.c
+libelektra_gconf_a_CFLAGS = $(gconf_CFLAGS) -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+hlvlbackend_LTLIBRARIES = libelektra-gconf.la
+libelektra_gconf_la_SOURCES = gconf.c 
+libelektra_gconf_la_CFLAGS = $(gconf_CFLAGS) $(COPTFLAGS) $(CDBGFLAGS)
+libelektra_gconf_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module 
+libelektra_gconf_la_LIBADD = $(gconf_LIBS) ../../libelektra/libelektra.la
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
+#endif
diff --git a/src/backends/gconf/README b/src/backends/gconf/README
new file mode 100644 (file)
index 0000000..e6c5b3d
--- /dev/null
@@ -0,0 +1,2 @@
+It will make GConf calls to access the conceptual key database. It also lets you access GConf keys through the Elektra API.
+It is ready to be used but, thanks to GConf, it is extremelly slow.
diff --git a/src/backends/hosts/Makefile.am b/src/backends/hosts/Makefile.am
new file mode 100644 (file)
index 0000000..1ac09e6
--- /dev/null
@@ -0,0 +1,22 @@
+#elekhostsdir = $(develdocdir)/backend-hosts/
+
+#EXTRA_DIST = hosts.c README
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+noinst_LIBRARIES = libelektra-hosts.a
+libelektra_hosts_a_SOURCES = hosts.c hosts.h ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_hosts_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+
+backend_LTLIBRARIES = libelektra-hosts.la
+libelektra_hosts_la_SOURCES = hosts.c hosts.h ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_hosts_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_hosts_la_LIBADD = ../../libelektra/libelektra.la
+libelektra_hosts_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
diff --git a/src/backends/hosts/hosts.c b/src/backends/hosts/hosts.c
new file mode 100644 (file)
index 0000000..9435ec8
--- /dev/null
@@ -0,0 +1,377 @@
+/***************************************************************************
+            hosts.c  -  Access the /etc/hosts file
+                             -------------------
+    begin                : Nov 2007
+    copyright            : (C) 2007 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/***************************************************************************
+ *                                                                         *
+ *   This is the skeleton of the methods you'll have to implement in order *
+ *   to provide libelektra.so a valid backend.                             *
+ *   Simple fill the empty _hosts functions with your code and you are   *
+ *   ready to go.                                                          *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include "hosts.h"
+
+
+int kdbOpen_hosts(KDB *handle)
+{
+       KDBCap * cap = kdbhGetCapability(handle);
+       const void *f;
+       KeySet *ks;
+       Key *k;
+
+       cap->onlyFullGet=1;
+       cap->onlyRemoveAll=1;
+       cap->onlyAddKeys=1;
+       cap->onlyFullSet=1;
+       cap->onlySystem=1;
+       cap->onlyUser=1;
+
+       cap->noOwner=1;
+       cap->noValue=1;
+       cap->noComment=1;
+       cap->noUID=1;
+       cap->noGID=1;
+       cap->noMode=1;
+       cap->noDir=1;
+       cap->noATime=1;
+       cap->noMTime=1;
+       cap->noCTime=1;
+       cap->noRemove=1;
+       cap->noStat=1;
+       cap->noMount=1;
+       cap->noBinary=1;
+       cap->noString=1;
+       cap->noTypes=1;
+       cap->noError=1;
+
+       ks = kdbhGetConfig (handle);
+       ksRewind (ks);
+       while ((k = ksNext (ks)) != 0)
+       {
+               f = keyName (k);
+               if (f) f = strrchr (f, '/');
+               if (f && strcmp (f, "/path") == 0) {
+                       void *data=kdbiMalloc(keyGetValueSize(k));
+                       memcpy(data,keyValue(k),keyGetValueSize(k));
+                       kdbhSetBackendData (handle, data);
+               }
+       }
+       if (!kdbhGetBackendData (handle)) kdbhSetBackendData (handle, kdbiStrDup (HOSTS_PATH));
+
+       return 0;
+}
+
+int kdbClose_hosts(KDB *handle)
+{
+       kdbiFree(kdbhGetBackendData(handle));
+       return 0; /* success */
+}
+
+/** Appends a comment to key found in line.
+ *
+ * Comments are marked with number signs, also called pound sign (#).
+ *
+ * White spaces are space and tab.
+ * Lines can not start with blank or tab.
+ * Blank lines are allowed and preserved in oomments
+ * Trailing white spaces are allowed.
+ * Only one host entry per line is allowed.
+ *
+ *
+ *
+ * @param line Will be closed with \0 where the comment begins
+ * @return 0 when the caller should go on
+ * @return 1 when the caller should continue (with next iteration)
+ */
+static int append_comment (char *comment, char *line)
+{
+       size_t i;
+       size_t s = kdbiStrLen(line);
+       size_t c = kdbiStrLen(comment);
+       char *endline;
+
+       if (line[0] == '\n')
+       {
+               strncat (comment, "\n", HOSTS_BUFFER_SIZE-c-1);
+               return 1; /* Empty line, so go on.. */
+       }
+
+       if (line[0] == '#')
+       {
+               strncat (comment, line, HOSTS_BUFFER_SIZE-c-2);
+               return 1; /* Complete line is comment, so go on.. */
+       }
+
+
+       for (i=0; i<s; i++)
+       {
+               if (line [i] == '#') /* comment found */
+               {
+                       /* Remove the endline, might be not there
+                        * if size limit of fgets hit or at end of file */
+                       endline = strrchr (line, '\n');
+                       if (endline) *endline = '\0';
+
+                       /* Copy the comment */
+                       strncat (comment, line+i+1, HOSTS_BUFFER_SIZE-c-s-2);
+                       line[i] = '\0';
+                       return 0; /* There should be a key here */
+               }
+       }
+       return 0; /* No # found */
+}
+
+/**Finds a token (which might be ip, canonical name or an alias).
+ *
+ * Token will be a pointer to the null terminated string containing
+ * the token.
+ *
+ * @return the size that next time the caller have to jump (line+s)
+ * in order to find the next token.
+ * @return 0 if no more token is available
+ */
+static size_t find_token (char **token, char *line)
+{
+       size_t i = 0;
+
+       /* Step 1, skip whitespaces */
+       while (line[i] == ' ' || line[i] == '\t') i++;
+       if (line[i] == '\0' || line[i] == '\n') return 0; /* We found the end of the line */
+
+       /* Step 2, parse the token */
+       *token = &line[i];
+       while (line[i] != ' ' && line[i] != '\t' && line[i] != '\0' && line[i] != '\n') i++;
+       if (line[i] == '\0' || line[i] == '\n')
+       {
+               line[i] = '\0'; /* Terminate the token. */
+               return i; /* find_token will quit next time in Step 1 */
+       }
+
+       /* Step 3, terminate the token */
+       line[i] = '\0';
+       return i+1; /* let find_token continue next time one byte after termination */
+}
+
+ssize_t kdbGet_hosts(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int errnosave = errno;
+       ssize_t nr_keys = 0, nr_alias;
+       FILE * fp;
+       char readbuffer [HOSTS_BUFFER_SIZE];
+       char *fieldbuffer;
+       size_t readsize;
+       char aliasname[] = "alias00";
+       char *fret;
+       int   sret;
+       Key *key, *alias, *tmp;
+       char comment [HOSTS_BUFFER_SIZE] = "";
+       KeySet *append = 0;
+
+       if (strcmp (keyName(kdbhGetMountpoint(handle)), keyName(parentKey))) return 0;
+
+       fp = fopen (kdbhGetBackendData(handle), "r");
+
+       if (fp == 0)
+       {
+               /*kdbhSetError (handle, KDB_ERR_NODIR);*/
+               errno = errnosave;
+               return -1;
+       }
+
+       kdbbReadLock (fp);
+
+       ksClear (returned);
+       append = ksNew(ksGetSize(returned)*2, KS_END);
+
+       key = keyDup (parentKey);
+       keySetDir (key);
+       ksAppendKey(append, key);
+       clear_bit (key->flags, KEY_FLAG_SYNC);
+       nr_keys ++;
+
+       while (1)
+       {
+               fret = fgets (readbuffer, HOSTS_BUFFER_SIZE, fp);
+               if (fret == 0) 
+               {
+                       /* success */
+                       kdbbUnlock(fp);
+                       fclose (fp);
+
+                       ksClear (returned);
+                       ksAppend (returned, append);
+                       ksDel (append);
+                       errno = errnosave;
+                       return nr_keys;
+               }
+
+               if (append_comment(comment, readbuffer)) continue;
+
+               sret = find_token (&fieldbuffer, readbuffer);
+               if (sret == 0) continue;
+
+               key = ksLookupByName(returned, fieldbuffer, KDB_O_POP);
+               if (!key) key = keyDup (parentKey);
+               keySetMode(key, 0664);
+               keySetString (key, fieldbuffer);
+               keySetComment (key, comment);
+               *comment = '\0'; /* Start with a new comment */
+
+               readsize = sret;
+               sret = find_token (&fieldbuffer, readbuffer+readsize);
+
+               keyAddBaseName (key, fieldbuffer);
+               ksAppendKey(append, key);
+               clear_bit (key->flags, KEY_FLAG_SYNC);
+
+               nr_alias = 0;
+               while (1) /*Read in aliases*/
+               {
+                       readsize += sret;
+                       sret = find_token (&fieldbuffer, readbuffer+readsize);
+                       if (sret == 0) break;
+
+                       tmp = keyDup (key);
+                       aliasname[5] = nr_alias / 10 + '0';
+                       aliasname[6] = nr_alias % 10 + '0';
+                       keyAddBaseName (tmp, aliasname);
+                       alias = ksLookup(returned, tmp, KDB_O_POP);
+                       if (!alias) alias = tmp;
+                       else keyDel (tmp);
+                       keySetMode(alias, 0664);
+                       keySetString (alias, fieldbuffer);
+                       keySetComment (alias, "");
+                       ksAppendKey(append, alias);
+                       clear_bit (alias->flags, KEY_FLAG_SYNC);
+                       nr_alias ++;
+                       if (nr_alias == 1)
+                       {
+                               keySetDir (key);
+                               clear_bit (key->flags, KEY_FLAG_SYNC);
+                       }
+               }
+               nr_keys += nr_alias + 1;
+       }
+#if DEBUG
+       printf ("error at line: %s\n", readbuffer);
+#endif
+       ksDel (append);
+       kdbbUnlock (fp);
+       fclose (fp);
+       errno = errnosave;
+       return -1;
+}
+
+ssize_t kdbSet_hosts(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int errnosave = errno;
+       ssize_t nr_keys = 0, nr_alias = 0;
+       FILE *fp;
+       Key *key, *alias=0;
+       char * lastline;
+
+       if (strcmp (keyName(kdbhGetMountpoint(handle)), keyName(parentKey))) return 0;
+
+       fp = fopen (kdbhGetBackendData(handle), "w");
+
+       if (fp == 0)
+       {
+               /*kdbhSetError (handle, KDB_ERR_NODIR);*/
+               errno = errnosave;
+               return -1;
+       }
+
+       kdbbWriteLock (fp);
+
+       ksRewind (returned);
+       key = ksNext (returned); /* skip parentKey */
+
+       nr_keys ++;
+
+       while (1)
+       {
+               if (!alias) key = ksNext (returned);
+               else key = alias;
+               if (!key) break;
+
+               lastline = strrchr (keyComment(key), '\n');
+               if (lastline)
+               {
+                       *lastline = '\0';
+                       fprintf (fp, "%s\n", keyComment(key));
+                       *lastline = '\n'; /* preserve comment */
+               }
+
+               fprintf (fp, "%s\t%s", (char*)keyValue(key), (char*)keyBaseName (key));
+
+               nr_alias = 0;
+               if (keyIsDir (key))
+               {
+                       while ((alias = ksNext (returned)) != 0)
+                       {
+                               if (keyIsDir(alias)) break;
+                               if (strncmp (keyName(key), keyName(alias), strlen(keyName(key)))) break;;
+                               if (strlen(keyName(key)) + strlen (keyBaseName (alias)) + 1 != strlen (keyName(alias))) goto error;
+                               if (strncmp (keyBaseName (alias), "alias", 5)) goto error;
+                               fprintf (fp, "\t%s", (char*)keyValue (alias));
+                               nr_alias++;
+                       }
+               } else alias = 0;
+
+               if (lastline)
+               {
+                       if (*(lastline+1) != '\0') fprintf (fp, " #%s", lastline+1);
+               } else {
+                       if (*keyComment(key) != '\0') fprintf (fp, " #%s", keyComment(key));
+               }
+
+               fprintf (fp, "\n");
+               nr_keys += nr_alias + 1;
+       }
+
+       kdbbUnlock (fp);
+       fclose (fp);
+       errno = errnosave;
+       return nr_keys;
+
+error:
+       kdbbUnlock (fp);
+       fclose (fp);
+       /* Make the file empty */
+       fp = fopen ("/tmp/hosts", "w");
+       fclose (fp);
+       errno = errnosave;
+       return -1;
+}
+
+KDBEXPORT(hosts)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_hosts,
+               KDB_BE_CLOSE,   &kdbClose_hosts,
+               KDB_BE_GET,     &kdbGet_hosts,
+               KDB_BE_SET,     &kdbSet_hosts,
+               KDB_BE_VERSION, BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Markus Raab <elektra@markus-raab.org>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION,
+                       "Reads and writes /etc/hosts content",
+               KDB_BE_END);
+}
+
diff --git a/src/backends/hosts/hosts.h b/src/backends/hosts/hosts.h
new file mode 100644 (file)
index 0000000..63e80e3
--- /dev/null
@@ -0,0 +1,25 @@
+#include <kdbbackend.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define BACKENDNAME "hosts"
+#define BACKENDVERSION "0.0.2"
+
+#define HOSTS_PATH "/etc/passwd"
+/* Use a buffer so large that it can hold my /etc/hosts :-)
+ * TODO: make it dynamic */
+#define HOSTS_BUFFER_SIZE 16384
+/*Test size for small buffer
+#define HOSTS_BUFFER_SIZE 16 */
+
+
+int kdbbWriteLock (FILE *f);
+int kdbbReadLock (FILE *f);
+int kdbbUnlock (FILE *f);
+
+int kdbOpen_hosts(KDB *handle);
+int kdbClose_hosts(KDB *handle);
+ssize_t kdbGet_hosts(KDB *handle, KeySet *ks, const Key *parentKey);
+ssize_t kdbSet_hosts(KDB *handle, KeySet *ks, const Key *parentKey);
+KDBEXPORT(hosts);
diff --git a/src/backends/ini/Makefile.am b/src/backends/ini/Makefile.am
new file mode 100644 (file)
index 0000000..1838bcb
--- /dev/null
@@ -0,0 +1,19 @@
+# $Id$
+
+#testdir = $(develdocdir)/src/backends/ini/
+AM_CPPFLAGS = -I$(top_srcdir)/src/include -DDEBUG -D_POSIX_SOURCE -D_BSD_SOURCE
+
+ini_sources = ini.c helpers.c parser.c ini.h ../../include/kdb.h ../../include/kdbbackend.h
+noinst_LIBRARIES = libelektra-ini.a
+libelektra_ini_a_SOURCES = $(ini_sources)
+libelektra_ini_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+backend_LTLIBRARIES = libelektra-ini.la
+libelektra_ini_la_SOURCES = $(ini_sources)
+libelektra_ini_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_ini_la_LIBADD = ../../libelektra/libelektra.la
+libelektra_ini_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
+
diff --git a/src/backends/ini/helpers.c b/src/backends/ini/helpers.c
new file mode 100644 (file)
index 0000000..8c654ab
--- /dev/null
@@ -0,0 +1,441 @@
+/***************************************************************************
+            helpers.c  -  System depended helper functions (POSIX)
+                             -------------------
+    begin                : 01.03.2005
+    updated              : 06.10.2005
+    copyright            : (C) 2005 by Markus Raab
+    email                : debian@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <ini.h>
+
+#define _POSIX_SOURCE 1
+
+/**POSIX include files*/
+#include <unistd.h>
+#include <pwd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+/*For flock*/
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+/**
+ * Opens a file filename.
+ * The mode might be 'r' or 'w'
+ *  (will be mapped to O_RDONLY or O_RDWR.)
+ *
+ * It handles the failures very safty.
+ * Don't use any other open inside the
+ * backend.
+ * 
+ * @see close_file
+ *
+ * You have to close it with close_file
+ * because there is also a file locking
+ * done.
+ * 
+ * @return -1 on failure, 0 otherwise
+ * @ingroup ini
+ * */
+int open_file (KDB *handle, char * filename, char mode)
+{
+       char buffer [2] = "\0\0";
+       int ret = 0;
+       int posix_mode;
+
+       if (mode == 'w')
+       {
+               buffer[0] = 'r';
+               buffer[1] = '+';
+               posix_mode = O_RDWR;
+       }
+       else if (mode == 'r')
+       {
+               buffer[0] = 'r';
+               posix_mode = O_RDONLY;
+       } else {
+               fprintf (stderr, "Mode not useful\n");
+               return -1;
+       }
+
+       FILEDES = open (filename, posix_mode);
+       if (FILEDES == -1) {
+#if DEBUG
+               fprintf (stderr, "Will create new file\n");
+#endif
+               FILEDES = open (filename, posix_mode | O_CREAT);
+               if (FILEDES == -1)
+               {
+                       fprintf (stderr, "Unable to open file\n");
+                       perror ("Reason: ");
+                       return -1;
+               }
+               ret = fchmod (FILEDES, 0644);   /* TODO: permissions!*/
+               if (ret == -1)
+               {
+                       fprintf (stderr, "Unable to chmod file\n");
+                       perror ("Reason: ");
+               }
+       }
+       
+       if (flock (FILEDES, LOCK_EX) == -1) {
+               fprintf (stderr, "Unable to lock file\n");
+               perror ("Reason: ");
+               ret = -1;
+       }
+
+       FILEPTR = fdopen (FILEDES,buffer);
+       if (FILEPTR == NULL) {
+               fprintf (stderr, "fdopen() failed\n");
+               perror ("Reason: ");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+/**
+ * Close previous with open_file() opened file
+ * @return 0 on success, -1 on failure
+ */
+int close_file (KDB *handle)
+{
+       int ret = 0;
+       
+       if (flock (FILEDES, LOCK_UN) == -1) {
+               perror ("Unable to unlock file");
+               ret = -1;
+       }
+
+       ret = fclose (FILEPTR);
+       if (ret != 0) {
+               perror ("Could not close file");
+               ret = -2;
+       }
+
+       return ret;
+}
+
+/**
+ * stat_file stats the filename filename and write
+ * the information in the key.
+ *
+ * So its possible to stat the real filename, without
+ * changing the keyname (which is normally another
+ * name then the filename).
+ *
+ * @param filename will be stated
+ * @param key will get the information about filename
+ *
+ * @return 0 on success, -1 otherwise
+ * 
+ * @ingroup ini
+ */
+int stat_file (Key * key, char * filename)
+{
+       struct stat buf;
+       stat (filename, &buf);
+
+       keySetMode(key,buf.st_mode);
+       keySetUID(key,buf.st_uid);
+       keySetGID(key,buf.st_gid);
+       if (S_ISDIR (buf.st_mode)) keySetDir(key);
+       if (S_ISREG (buf.st_mode)) keySetType(key, KEY_TYPE_FILE);
+       key->atime=buf.st_atime;
+       key->mtime=buf.st_mtime;
+       key->ctime=buf.st_ctime;
+
+       return 0;
+}
+
+
+/**
+ * Enlarges file on place where with space bytes. The new
+ * place will contain the previous text. The text before
+ * where will not be touched.
+ * 
+ * @param where: holds the place where a new space is needed
+ * @param space: holds the size of the new needed space
+ * 
+ * @return 0 on success, -1 else
+ * @ingroup ini
+ */
+int enlarge_file (KDB *handle, long where, long space)
+{
+       char buffer [BUFFER_RDWR_SIZE+1];
+       size_t sread;
+       long diff = 0;
+       int err;
+       int finished = 0;
+       long pos;
+
+       fseek (FILEPTR,0,SEEK_END); /* begin at end*/
+       pos = ftell (FILEPTR);
+       do {
+               pos -= BUFFER_RDWR_SIZE;
+               if (pos < where) {
+                       diff = where - pos;
+                       pos = where;
+                       finished = 1;
+               }
+               fseek (FILEPTR, pos, SEEK_SET);
+               sread = fread (buffer,1,BUFFER_RDWR_SIZE-diff,FILEPTR); /* read last peace*/
+               buffer[sread] = 0;      /* mark end (not necessary)*/
+
+               fseek (FILEPTR,pos+space,SEEK_SET);     /* jump to writepos*/
+
+#if DEBUG
+               printf ("buffer: %s, sread: %d\n", buffer, (int)sread);
+#endif
+               fwrite (buffer,1,sread,FILEPTR);
+               err = ferror (FILEPTR);
+               if (err != 0)
+               {
+                       fprintf (stderr, "Error in stream\n");
+                       return -1;
+               }
+       } while (! finished);
+
+       return 0;
+}
+
+/**
+ * Shrinks file on place where with space bytes.
+ * The old text (length space after where) will 
+ * be lost! The text before where will not be touched.
+ *
+ * @param where: The File will be shrinked here
+ * @param space: The size how much the file will be shrinked
+ * 
+ * @return 0 on success, -1 on error
+ * @ingroup ini
+ */
+int shrink_file (KDB *handle, long where, long space)
+{
+       char buffer [BUFFER_RDWR_SIZE+1];
+       size_t sread;
+       int err;
+       long pos;
+
+       fseek (FILEPTR,where, SEEK_SET);
+       pos = ftell (FILEPTR);
+       
+       do {
+               fseek (FILEPTR,pos+space,SEEK_SET); /* jump to readposition*/
+               sread = fread (buffer,1,BUFFER_RDWR_SIZE,FILEPTR);      /* read a peace*/
+               buffer[sread] = 0;      /* mark end (not necessary)*/
+
+               fseek (FILEPTR,pos,SEEK_SET);   /* jump to writepos*/
+#if DEBUG
+               printf ("buffer: %s, sread: %d\n", buffer, (int)sread);
+#endif
+               fwrite (buffer,1,sread,FILEPTR);
+               err = ferror (FILEPTR);
+               if (err != 0)
+               {
+                       fprintf (stderr, "Error in stream\n");
+                       return -1;
+               }
+               pos += sread;
+       } while (sread == BUFFER_RDWR_SIZE);
+
+       ftruncate (FILEDES,lseek(FILEDES,0,SEEK_CUR));
+
+       return 0;
+}
+
+/**
+ * Get the basename for the Key forKey.
+ *
+ * This might be:
+ * /etc/kdb
+ * /home/markus/.kdb
+ * 
+ * There are 2 possibilites. It may be KEY_NS_SYSTEM
+ * or KEY_NS_USER. When the key of a user is asked
+ * for, then environment will be asked what USER is
+ * logged on.
+ *
+ * @see file_name
+ *
+ * @ingroup ini
+ */
+size_t base_name (const Key * forKey, char * basename)
+{
+       size_t length;
+
+       switch (keyGetNamespace(forKey)) {
+               case KEY_NS_SYSTEM: {
+                       /* Prepare to use the 'system/ *' database */
+                       strncpy(basename,KDB_DB_SYSTEM,MAX_PATH_LENGTH);
+                       length=strlen(basename);
+                       break;
+               }
+               /* If we lack a usable concept of users we simply let the default handle it
+                * and hence disable the entire user/ hiarchy. */
+               case KEY_NS_USER: {
+                       /*
+                        * TODO: Change to use elektra internal config.
+                        *       As fallback use getpwnam (libc bug makes valgrind cry)
+                        *       And as last solution use environment $HOME?
+                        *
+                        * Prepare to use the 'user:????/ *' database */
+                       if (getenv("HOME"))
+                       {
+                               char * home = getenv("HOME");
+                               length=snprintf(basename,MAX_PATH_LENGTH,"%s/%s",home,KDB_DB_USER);
+                               break;
+                       }
+
+#ifdef HAVE_PWD_H
+                       else if (forKey->owner)
+                               user=getpwnam(forKey->owner);
+                       else if ( getenv("USER") )
+                               user=getpwnam(getenv("USER"));
+
+                       if (!user) return 0; /* propagate errno */
+                       length=snprintf(basename,MAX_PATH_LENGTH,"%s/%s",user->pw_dir,KDB_DB_USER);
+                       break;
+#else
+                       /*errno=KDB_ERR_INVALIDKEY;*/
+                       return 0;
+#endif
+               }
+
+               default: {
+                       /*errno=KDB_ERR_INVALIDKEY;*/
+                       return 0;
+               }
+       }
+       return length;
+}
+
+/**
+ * Returns the filename from the Key forKey
+ *
+ * The name returned is normally not correct, because
+ * it may have subdirs and it has the keyname in it.
+ *
+ * @see IniSearchFileName
+ * will cut of the end until it has found a file.
+ * 
+ * @param filename: MAX_PATH_LENGTH size char*
+ * @param keyname: MAX_PATH_LENGTH size char*
+ * 
+ * @ingroup ini
+ */
+size_t file_name (const Key * forKey, char * filename)
+{
+       size_t length;
+       size_t namesize;
+       char * name;
+
+       length = base_name (forKey, filename);
+
+        filename[length]='/'; length++; /* now the Keyname follows*/
+
+       namesize = keyGetNameSize (forKey);
+       if (namesize == 0) return 0;
+       name = (char*) malloc (namesize);
+
+       keyGetName (forKey, name, namesize);
+
+       if (length > MAX_PATH_LENGTH) return -1;        /* too long*/
+
+       strncpy (filename + length, name, namesize);
+       length += namesize;
+       free (name);
+
+       return length;
+}
+
+/**Opens a directory and returns a pointer to a
+ * platform depended unique struct which indentifies
+ * the opened directory.
+ * With the pointer you can get the filenames with
+ * repeated calling from read_dir.
+ * @see read_dir
+ * @return pointer to struct (DIR), or NULL if failed*/
+void * open_dir (char * pathname)
+{
+       return (void *) opendir (pathname);
+}
+
+/**Creates directories recursively up to the filename
+ * @return 0 on success, -1 on failure*/
+int create_dir (char * keyFileName)
+{
+       char * end;
+       char * fil;
+
+#if DEBUG
+       fprintf (stderr, "will create_dir() for %s\n", keyFileName);
+#endif
+       end = strrchr(keyFileName, '/'); /* key abschneiden*/
+       *end = 0;
+       fil = strrchr(keyFileName, '/'); /* das hier wird file sein*/
+       end = keyFileName +1;   /* fange suche nach dir an*/
+       while (1) {
+               end = strchr(end+1, '/');
+               if (end == NULL) break;
+               * end = '\0';
+#if DEBUG
+               fprintf (stderr, "Create Folder %s\n", keyFileName);
+#endif
+               if (mkdir (keyFileName, 0777) == -1)
+               {
+                       if (errno == EEXIST)
+                       {
+#if DEBUG
+                               /**TODO Check if it is dir. Make more robust with stat*/
+                               fprintf (stderr, "Directory already exists\n");
+#endif
+                       } else {
+                               fprintf (stderr, "Could not create Folder %s\n", keyFileName);
+                               perror ("Reason: ");
+                               return -1;
+                       }
+               }
+               * end = '/';                    
+       }
+       return 0;
+}
+
+
+/**Reads the next filename out of dir.
+ * precondition:
+ * filename size is MAX_PATH_LENGTH and guaranted to be allocated.
+ * @return -1 if no more files, 0 on success, -1 on failure*/
+int read_dir (void * dir, char * filename)
+{
+       struct dirent * n;
+       n= readdir (dir);
+       strncpy (filename, n->d_name, MAX_PATH_LENGTH);
+       if (n == NULL) return -1; /*No more files*/
+       return 0;
+}
+
+/**Closes previously opened dir
+ * Use pointer from open_dir
+ * @return 0 on success, -1 on failure*/
+int close_dir (void * dir)
+{
+       return close_dir ((DIR *) dir);
+}
+
diff --git a/src/backends/ini/ini.c b/src/backends/ini/ini.c
new file mode 100644 (file)
index 0000000..f039a96
--- /dev/null
@@ -0,0 +1,498 @@
+/***************************************************************************
+            ini.c  -  Backend for ini-style like files
+                             -------------------
+    begin                : 01.03.2005
+    updated              : 06.10.2005
+    copyright            : (C) 2005 by Markus Raab
+    email                : debian@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <ini.h>
+
+
+int kdbOpen_ini(KDB *handle)
+{
+       KDBCap *cap = kdbhGetCapability (handle);
+
+       cap->onlyFullGet=1;
+       cap->noStat=1;
+
+       cap->onlyRemoveAll=1;
+
+       cap->onlyFullSet=1;
+       cap->onlyAddKeys=1;
+
+       cap->onlySystem=1;
+       cap->onlyUser=1;
+
+       cap->noOwner=1;
+       cap->noValue=1;
+       cap->noComment=1;
+       cap->noUID=1;
+       cap->noGID=1;
+       cap->noMode=1;
+       cap->noDir=1;
+       cap->noATime=1;
+       cap->noMTime=1;
+       cap->noCTime=1;
+       cap->noRemove=1;
+       cap->noMount=1;
+       cap->noBinary=1;
+       cap->noString=1;
+       cap->noTypes=1;
+       cap->noError=1;
+
+       cap->noLock=1;
+       cap->noThread=1;
+
+       kdbhSetBackendData (handle, malloc (sizeof (backendData)));
+
+       /* backend initialization logic */
+
+       return 0;
+}
+
+int kdbClose_ini(KDB *handle)
+{
+       free (kdbhGetBackendData (handle));
+
+       /* free all backend resources and shut it down */
+
+       return 0; /* success */
+}
+
+ssize_t kdbGet_ini(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int rc = 0;
+       char t [MAX_PATH_LENGTH];
+       Key * write = keyDup (parentKey);
+       
+#if DEBUG
+       file_name(write, t);
+       fprintf (stderr, "file_name: %s\n",     t);
+       base_name(write, t);
+       fprintf (stderr, "base_name: %s\n",     t);
+#endif
+
+       /**Immediately call IniChooseFile (will work recursively)*/
+       rc = IniChooseFile (handle, write, returned, 0);
+       keyDel (write);
+
+       return rc;
+}
+
+ssize_t kdbSet_ini(KDB *handle, KeySet *ks, const Key *parentKey)
+{
+       return IniSetKeys (handle, ks);
+}
+
+
+/**
+ * Returns a filename from the Key which can be opened for forKey
+ *
+ * The name returned should be correct, because
+ * it removes subdirs and the keyname.
+ *
+ * It even tests with kdbStatKey if the File exists.
+ * So be prepaired that stat information is filled
+ * within the key.
+ *
+ * @see IniSearchFileName
+ * @param filename must be allocated MAX_PATH_LENGTH space.
+ * Previous Content of Filename will be destroyed afterwards.
+ * 
+ * @ingroup ini
+ */
+size_t IniSearchFileName (KDB *handle, Key * forKey, char * filename)
+{
+       size_t length;
+       uint8_t info = 0;
+       char * end;
+
+       length = file_name (forKey, filename);
+       
+       do {
+#if DEBUG
+               fprintf (stderr, "Search %s\n", filename);
+#endif
+               end = strrchr (filename, '/');
+               if (end == NULL) {
+#if DEBUG
+                       fprintf (stderr, "Could not find any file\n");
+#endif
+                       return -1;
+               }
+               *end= '\0';
+               stat_file (forKey, filename);
+               info = keyGetType (forKey);
+       } while (!(info & KEY_TYPE_FILE));
+
+       return length;
+}
+
+
+/**
+ * Get out all the keys of a file
+ * 
+ * @param keyFileName: Name of the file
+ * @param keyRoot: Name of the root of files
+ *  The root will be added before the keyName
+ * 
+ * @ingroup ini
+ */
+ssize_t IniGetKeys (KDB *handle, char * keyFileName, char * keyRoot, KeySet * returned)
+{
+       Key * key;
+       int pos;
+
+       if (open_file (handle, keyFileName, O_RDONLY) == -1)
+       {
+#if DEBUG
+               fprintf (stderr, "Could not open file %s\n", keyFileName);
+#endif
+               /*errno = KDB_ERR_NOTFOUND;*/
+               return -1;
+       }
+       
+       key = keyNew(0);
+
+#if DEBUG
+       fprintf (stderr, "Call read_key(%s)\n", keyRoot);
+#endif
+       while ((pos=read_key (handle, key, keyRoot)) == 0)
+       {
+#if DEBUG
+               fprintf (stderr, "Append key\n");
+#endif
+               ksAppendKey(returned,key);
+
+               key = keyNew(0);
+       }
+       
+       keyDel (key); /* delete the not used key left*/
+
+       close_file(handle);
+       
+       return 0; /* success */
+}
+
+/**
+ * Reads the contents of the whole file which Key
+ * points to.
+ *
+ * The Keys will be added in the Keyset.
+ *
+ * @ingroup ini
+ */
+int IniReadFile (KDB *handle, Key * key, KeySet * returned, unsigned long options)
+{
+       char filename [MAX_PATH_LENGTH];
+       char * keyname;
+       size_t keyLength;
+
+#if DEBUG
+       fprintf (stderr, "IniReadfile\n");
+#endif
+       
+       file_name(key, filename);
+
+       keyLength = keyGetNameSize (key);
+       keyname = malloc (keyLength);
+       keyGetName (key, keyname, keyGetNameSize (key));
+
+#if DEBUG
+       fprintf (stderr, "Call IniGetKeys(filename: %s, keyRoot: %s,returned)\n", 
+               filename, keyname);
+#endif
+       IniGetKeys (handle, filename, keyname, returned);
+       
+       if (keyLength >0) free (keyname);
+
+       return 0;
+}
+
+/**
+ * This mapper chooses between the different
+ * styles of files to start the correct function.
+ *
+ * For files it starts IniReadFile
+ * For directorys it starts IniReadDir
+ *
+ * @ingroup ini
+ * */
+int IniChooseFile(KDB *handle, Key * key, KeySet * returned, unsigned long options)
+{
+       char filename [MAX_PATH_LENGTH];
+       char * keyname;
+       size_t keylength;
+       
+       file_name(key, filename);
+       stat_file (key, filename);
+
+#if DEBUG
+       fprintf (stderr, "IniChooseFile, pathName: %s\n", filename);
+#endif
+       
+       if (keyGetType (key) == KEY_TYPE_DIR)
+       {
+               if (filename[strlen(filename)-1] != '/')
+               {
+                       keylength = keyGetNameSize(key);
+                       keyname = malloc (keylength + 2);
+                       keyGetName (key, keyname, keylength);
+                       keyname[keylength-1] = '/';
+                       keyname[keylength] = '\0';
+                       keySetName (key, keyname);
+                       free (keyname);
+               }
+                       
+               return IniReadDir (handle, key, returned, options);
+       }
+
+       if (keyGetType (key) == KEY_TYPE_FILE)
+       {
+               return IniReadFile (handle, key, returned, options);
+       }
+
+#if DEBUG
+       fprintf (stderr, "Not a directory or file!");
+#endif
+       return -1;
+}
+
+
+/**
+ * Reads all Keys of a directory.
+ *
+ * For every key the mapper IniChooseFile
+ * will be called.
+ * 
+ * @ingroup ini
+ * */
+int IniReadDir(KDB *handle, Key * key, KeySet * returned, unsigned long options)
+{
+       char pathName [MAX_PATH_LENGTH];
+       char keyname [MAX_PATH_LENGTH];
+       char keypath [MAX_PATH_LENGTH];
+       char filename [MAX_PATH_LENGTH];
+       void * dir;
+       int ret;
+
+#if DEBUG
+       fprintf (stderr, "IniReadDir\n");
+#endif
+       file_name (key, pathName);
+       dir = open_dir (pathName);
+       if (dir == NULL) {
+               fprintf (stderr, "Could not open directory %s\n", pathName);
+               return -1;
+       }
+       
+       keyGetName (key, keypath, MAX_PATH_LENGTH);
+       
+       while (read_dir (dir,filename) == 0)
+       {
+               if (    strcmp(filename, ".")  == 0 || 
+                       strcmp(filename, "..") == 0)
+                       continue;
+               
+               if (filename[0] == '.' && !(options & KDB_O_INACTIVE))
+                       continue;
+
+#if DEBUG
+               fprintf (stderr, "Next entry filename: %s\n", filename);
+#endif
+               strncpy(keyname, keypath, MAX_PATH_LENGTH);
+               strcat(keyname, filename);
+               keySetName (key, keyname);
+
+#if DEBUG
+               fprintf (stderr, "New keyname: %s\n", keyname);
+#endif
+               ret = IniChooseFile (handle, key, returned, options);
+       }
+
+       if (close_dir (dir))
+       {
+               fprintf (stderr, "Could not close directory\n");
+               return -1;
+       }
+
+       return ret;
+}
+
+
+
+/**Walks through a file and lookups if the first key of a keyset is in the
+ * given Keyset. When found it overwrites the key. It also checks if any
+ * of the other keys can be written in that file under that context. If
+ * not, the keys will remain in the Keyset.
+ *
+ * So, the KeySet will be empty after complete successful writing. At
+ * least one key is guranteed to be written when it was successful.
+ * 
+ * The whole idea behind that concept is, that open/close and searching
+ * within a file is complete minimized. The disadvantage is, that a
+ * fast ksLookupKey is required, because every readed key will be compared
+ * with all keys in keyset (its not implemented in elektra right now,
+ * so it will consume linear more time, but only in memory, not on
+ * harddisc).
+ *
+ * When not found it writes out the keys before the sectionend.
+ * This can be:
+ * 
+ * setting keys new keys will introduce new folders and files
+ * as needed.
+ *
+ * At least one key will be written!
+ * 
+ * @return >=0 on success
+ * @return -1 on failure (Keyset may be changed then!)
+ * @return #nr when #nr keys could not written to file
+ *
+ * */
+int IniSetKeys (KDB *handle, KeySet * origKeys)
+{
+       char keyFileName [MAX_PATH_LENGTH];
+       
+       int pos;
+       int keySize;
+       char * keyFullName = NULL;
+       char * keyRoot = NULL;
+       char * end;
+
+       Key * origKey;  /*First key which will introduce opening file*/
+       Key * setKey;   /*Used for getting the keys which may be set*/
+       Key * key = keyNew(0);
+
+       long oldpos;
+       
+#if DEBUG
+       fprintf (stderr, "IniSetKeys() entered\n");
+#endif
+
+       ksRewind (origKeys);
+       origKey = ksNext (origKeys); /* Open file for this key*/
+       key = keyDup (origKey); /* for searching*/
+       
+       pos = IniSearchFileName(handle, key, keyFileName);
+
+#if DEBUG
+       fprintf (stderr, "after SearchFileName ...\n");
+#endif
+
+       if (pos == -1) /* no such file exists*/
+       {
+               file_name(key,keyFileName);
+               create_dir(keyFileName);        
+       }
+       
+       keySize = keyGetNameSize (key);
+       keyFullName = malloc (keySize+1);
+       if (keyFullName == NULL) goto memerror;
+       keyGetName(key, keyFullName, keySize);
+       
+       end = strrchr (keyFullName, '/');       /* dirname*/
+       if (end) *end = 0;
+       keyRoot = malloc (strlen (keyFullName));
+       if (keyRoot == NULL) goto memerror;
+       strcpy (keyRoot, keyFullName);
+       if (end) *end = '/';    /*revert keyname*/
+
+#if DEBUG
+       fprintf (stderr, "keyRoot: %s, keyName: %s\n", keyRoot, keyFullName);
+       fprintf (stderr, "Set Key [%d] in File: %s\n",keySize, keyFileName);
+#endif
+
+       if (open_file (handle, keyFileName, O_RDWR) == -1)
+       {
+#if DEBUG
+               fprintf (stderr, "Could not open file %s\n", keyFileName);
+#endif
+               ksAppendKey(origKeys, origKey);
+               /*errno = KDB_ERR_NOTFOUND;*/
+               goto fileerror;
+       }
+       
+       while ((pos=read_key (handle, key, keyRoot)) == 0)
+       {
+               if ((setKey = ksLookupByName (origKeys, key->key, 0)) != NULL) 
+               {       /* right Key found*/
+#if DEBUG
+                       fprintf (stderr, "Key found\n");
+                       fprintf(stderr, "Name: (%s), Value: (%s), Comment: (%s)\n",
+                               keyName (setKey), (char *) keyValue(setKey),
+                               (char *) keyComment (setKey));
+#endif
+                       
+                       write_key(handle, setKey, oldpos);
+
+                       if ((keyCompare (key, origKey) & KEY_NAME) == 0)
+                               pos = 1; /*Start Key found, good!*/
+               }
+               oldpos = ftell (FILEPTR);
+       }
+       if (pos != 1) { /* key not found, add to the end*/
+#if DEBUG
+               fprintf (stderr, "Key not found!\n");
+#endif
+               fseek (FILEPTR, 0, SEEK_END);
+               oldpos = ftell(FILEPTR);
+               /*TODO: write key here if not found
+                * write_key(setKey, oldpos);*/
+               pos = 0;
+       } else if (pos == 1) { /* key found, everything went ok!*/
+               pos = 0;
+       }
+       
+       close_file (handle);
+       
+       free (keyFullName);
+       free (keyRoot);
+       
+       keyDel (key);
+
+#if DEBUG
+       fprintf (stderr, "leaving IniSetKeys()\n");
+#endif
+       
+       return pos; /* success */
+
+memerror:
+       /*errno=KDB_ERR_NOMEM;*/
+       close_file (handle);
+#if DEBUG
+       fprintf (stderr, "Memory Error\n");
+#endif
+fileerror:
+       
+       free (keyFullName);
+       free (keyRoot);
+       
+       keyDel (key);
+       
+       return -1;
+}
+
+KDBEXPORT(ini)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_ini,
+               KDB_BE_CLOSE,   &kdbClose_ini,
+               KDB_BE_GET,     &kdbGet_ini,
+               KDB_BE_SET,     &kdbSet_ini,
+               KDB_BE_VERSION,        BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Markus Raab <elektra@libelektra.org>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION, "Key/Value Pairs are stored in files in following scheme: key1=value1;comment",
+               KDB_BE_END);
+}
+
diff --git a/src/backends/ini/ini.h b/src/backends/ini/ini.h
new file mode 100644 (file)
index 0000000..f2cac4c
--- /dev/null
@@ -0,0 +1,117 @@
+/***************************************************************************
+            ini.h  -  Backend for ini-style like files
+                             -------------------
+    begin                : 01.03.2005
+    updated              : 06.10.2005
+    copyright            : (C) 2005 by Markus Raab
+    email                : debian@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/***************************************************************************
+ *
+ *   This is a ini-style backend.
+ *   Key/Value Pairs are stored in files in following scheme:
+ *   
+ *   key1=value1;comment
+ *
+ * TODO:
+ *   allow subkeys setting/getting
+ *   setting existing keys again
+ *   setting errno properly (update doc, how it should be)
+ *   KDB_ERR_NOMEM bei malloc errors
+ *
+ ***************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <kdbbackend.h>
+
+#define BACKENDNAME "ini"
+#define BACKENDVERSION "0.1.2"
+
+struct _backendData
+{
+       int fd;
+       FILE *fc;
+};
+
+typedef struct _backendData backendData;
+
+#define FILEDES ((((backendData*)kdbhGetBackendData (handle))->fd))
+#define FILEPTR ((((backendData*)kdbhGetBackendData (handle))->fc))
+
+int IniReadDir(KDB *handle, Key * key, KeySet * returned, unsigned long options);
+int IniChooseFile(KDB *handle, Key * key, KeySet * returned, unsigned long options);
+int IniReadFile (KDB *handle, Key * key, KeySet * returned, unsigned long options);
+int IniSetKeys (KDB *handle, KeySet * origKeys);
+
+/**Helper functions*/
+int open_file (KDB *handle, char * filename, char mode);
+int close_file (KDB *handle);
+int enlarge_file (KDB *handle, long where, long space);
+int shrink_file (KDB *handle, long where, long space);
+int read_key (KDB *handle, Key * key, char * root);
+int write_key (KDB *handle, Key * key, long oldpos);
+int remove_key (KDB *handle, Key * key, long oldpos);
+
+int kdbiRealloc (void ** buffer, size_t size);
+
+int stat_file (Key * forKey, char * filename);
+size_t base_name (const Key * forKey, char * basename);
+size_t file_name (const Key * forKey, char * basename);
+
+void * open_dir (char * pathname);
+int create_dir (char * filename);
+int read_dir (void * dir, char * filename);
+int close_dir (void * dir);
+
+/**Parsing functions*/
+int parse_buffer (char * c);
+int convert_engine (char * c);
+int convert_strlen (char * p, int size);
+int convert_stream (char * buffer, int size, FILE * stream);
+int make_key (Key * key, char * root, char * buffer_key, char * buffer_value, char * buffer_comment);
+
+/**Some systems have even longer pathnames */
+#ifdef PATH_MAX
+#define MAX_PATH_LENGTH PATH_MAX
+/**This value is garanteed on any Posix system */
+#elif _POSIX_PATH_MAX
+#define MAX_PATH_LENGTH _POSIX_PATH_MAX
+#else 
+/**Fallback: This value should be useful*/
+#define MAX_PATH_LENGTH 4096
+#endif
+
+
+/**This buffer size is fastest for reading and writing
+ * in files*/
+#define BUFFER_RDWR_SIZE 8024
+
+/**Buffer for holding strings*/
+#ifndef BUFFER_SIZE
+#define BUFFER_SIZE 4048
+#endif
+
+/**Some more key types needed for ini
+ * FILE ... is a real file on the system
+ * DIR ... is a real directory
+ * SUBDIR ... is a subdirectoy within a file*/
+#define KEY_TYPE_FILE 4
+#define KEY_TYPE_DIR 8
+#define KEY_TYPE_SUBDIR 16
+
+#define O_RDONLY 'r'
+#define O_RDWR 'w'
+
diff --git a/src/backends/ini/parser.c b/src/backends/ini/parser.c
new file mode 100644 (file)
index 0000000..c3b51dc
--- /dev/null
@@ -0,0 +1,523 @@
+/***************************************************************************
+            parser.c  -  Parsing Functions for Configuration
+                             -------------------
+    begin                : 01.03.2005
+    updated              : 06.10.2005
+    copyright            : (C) 2005 by Markus Raab
+    email                : debian@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <ini.h>
+
+
+/**States of parsing the key*/
+#define STATE_BEG 0
+#define STATE_KEY 1
+#define STATE_VALUE 2
+#define STATE_COMMENT 4
+#define STATE_END 8
+#define STATE_SEC_BEG 16
+#define STATE_SEC_END 32
+#define STATE_EMPTY 64
+#define STATE_CHAR 128
+
+int state;
+
+#define CHAR_OK 0
+#define CHAR_ADD 1
+#define CHAR_SEC 2
+#define CHAR_NEWLINE 3
+#define CHAR_NULL 4
+#define CHAR_ERR 5
+
+/**parses a char
+ * Logic of states is here. If you want a user defined
+ * configuration format, this is your function.
+ *
+ * The pointer points to an array of chars. It must be
+ * null terminated, because the function might try to
+ * read the next char, but it will only change the char
+ * pointed to.
+ *
+ * The change will take place, when a mulitbyte sequence
+ * should be decoded.
+ * @see convert_engine
+ * for more information about that.
+ * 
+ * @return CHAR_ERR on error
+ * @return CHAR_OK if char was correctly handeled
+ * @return CHAR_ADD if parsing function could not handle the char
+ * @return CHAR_SEC if section was read in gracefully
+ *  
+ * @param: pointer to char, char may be converted if needed
+ *  
+ *  always check if success, otherwise the char belongs
+ *  to key/name/comment*/
+int parse_buffer (char * p)
+{
+       char c = *p;
+#if DEBUG && VERBOSE 
+       fprintf (stderr, "Will parse %c with state %d\n", c, state);
+#endif
+       if (c == '\0')
+       { /**End found, report that*/
+               return CHAR_NULL;
+       } else if (c == '\n')
+       { /**Newline (end of key) found, report that*/
+               return CHAR_NEWLINE;
+       } else if (state & STATE_CHAR)
+       { /*Convert multichar*/
+               if (c == '0') *p = '\0';
+               else if (c=='n') *p = '\n';
+               else if (c=='b') *p = '\\';
+               else if (c=='w') *p = ' ';
+               else if (c=='e') *p = '=';
+               else if (c=='s') *p = ';';
+               else if (c=='r') *p = '#';
+               state &= ~STATE_CHAR; /**Remove char bit*/
+               return CHAR_ADD;
+       }
+       else if (state == STATE_BEG) /**Check first char*/
+       {
+               if (c == '[') {
+                       state = STATE_SEC_BEG;
+                       return CHAR_OK;
+               } else if (c == '#') {
+                       state = STATE_EMPTY;
+                       return CHAR_ERR;
+               } else { /**char belongs to key*/
+                       state = STATE_KEY;
+                       return CHAR_ADD;
+               }
+       } 
+       else if (state == STATE_KEY && c == '=')
+       {       /* value follows*/
+               state = STATE_VALUE;
+               return CHAR_OK;
+       }
+       else if (state == STATE_VALUE && c == ';')
+       {       /* comment follows*/
+               state = STATE_COMMENT;
+               return CHAR_OK;
+       }
+       else if (state == STATE_SEC_BEG && c == ']')
+       {       /* section correctly ended*/
+               state = STATE_SEC_END;
+               return CHAR_OK;
+       } else if (c == '\\')
+       { /**Mulitchar sequence appeared*/
+               state |= STATE_CHAR;
+               return CHAR_OK;
+       }
+       else if (state == STATE_KEY) 
+       { /**Stay in that state, and add char*/
+               return CHAR_ADD;
+       }
+       else if (state == STATE_VALUE)
+       { /**Stay in that state, and add char*/
+               return CHAR_ADD;
+       }
+       else if (state == STATE_COMMENT)
+       { /**Stay in that state, and add char*/
+               return CHAR_ADD;
+       }
+       return CHAR_ERR; /* Should not be reached*/
+}
+
+/**Will decode a char into a char sequence safe for use
+ * in configuration files.
+ *
+ * <0NULL> -> \0 ... 0 byte
+ * <enter> -> \n ... newline
+ *    \    -> \b ... backslash
+ * <space> -> \w ... whitespace
+ *    =    -> \e ... equals
+ *    ;    -> \s ... semikolon
+ *    #    -> \r ... rhombus
+ *
+ * It will use the next char in the array. The size of the
+ * array needs to be 2. It is not needed to be NULL terminated.
+ *
+ * If it is not a multichar, the second byte is guaranteed to
+ * be NULL ('\0'). Otherwise see above.
+ * 
+ * @return 0 on success
+ */
+int convert_engine (char * p)
+{
+       char c = *p;
+       char * n = p+1;
+       *n = '\0';
+       if (c == '\0') *n = '0';
+       else if (c=='\n') *n = 'n';
+       else if (c=='\\') *n = 'b';
+       else if (c==' ') *n = 'w';
+       else if (c=='=') *n = 'e';
+       else if (c==';') *n = 's';
+       else if (c=='#') *n = 'r';
+       else return 0;
+       *p = '\\';
+#if DEBUG && VERBOSE
+       fprintf (stderr, "Handle %c to %c%c\n", c, *p, *n);
+#endif
+       return 0;
+}
+
+/**Returns the Stringlen for the char, multichars are handeld
+ * correct.
+ *
+ * @param size: You may find it funny to give a strlen function
+ * a length. But thus null Char are allowed, this is needed.
+ * In fact, the function will only iterate above the size
+ * and will calculate how much more it will need after converting*/
+int convert_strlen (char * p, int size)
+{
+       int i;
+       char c[2];
+       int s;
+       for (i=0,s=0; i<size; i++,s++)
+       {
+               c[0] = p[i];
+               convert_engine(c);
+               if (c[1] != '\0') s++;
+       }
+       return s;
+}
+
+/**Outputs a buffer to a stream. It converts the multichar
+ * sequences as
+ * @see convert_engine
+ * does.
+ * Thus null bytes are allowed (will be handeld saftey)
+ * the buffer is NOT null terminated, but minimum as long
+ * as the
+ * @param size
+ * says.
+ * @return 0 on success*/
+int convert_stream (char * buffer, int size, FILE * stream)
+{
+       int i;
+       char conv [2];
+       for (i=0; i<size; i++)
+       {
+               conv[0] = buffer[i];
+               convert_engine (conv);
+               if (conv[1] == '\0') fprintf (stream, "%c", conv[0]);
+               else fprintf (stream, "%c%c", conv[0], conv[1]);
+       }
+       return 0;
+}
+
+
+/**
+ * Read one key out of a file.
+ *
+ * It does not check the Name of the Key.
+ *
+ * @param key: Will contain information of key
+ * @param root: The prefix name of the key
+ *
+ * state logic is inside parse_buffer
+ * 
+ * @return -1 on failure (memory, EOF, comment line), 0 on sucess
+ * 
+ * @ingroup ini
+ */
+int read_key (KDB *handle, Key * key, char * root)
+{
+       char * buffer = NULL;
+       char * buffer_value = NULL;
+       char * buffer_key = NULL;
+       char * buffer_comment = NULL;
+       int rc;
+       
+       int i;
+       
+       int string_length = BUFFER_SIZE;
+       int value_length = BUFFER_SIZE;
+       int key_length = BUFFER_SIZE;
+       int comment_length = BUFFER_SIZE;
+       
+       int v=0;        /* position of value*/
+       int k=0;        /* position of key*/
+       int c=0;        /* position of comment*/
+       
+       state= STATE_BEG;
+       if (FILEPTR == NULL)
+       {
+#if DEBUG
+               fprintf (stderr, "File not opend\n");
+#endif
+               return -1;
+       }
+       
+       buffer = (char*) malloc (BUFFER_SIZE+1);
+       if (buffer == NULL) goto memerror;
+       if (fgets (buffer, BUFFER_SIZE, FILEPTR) == NULL) {
+#if DEBUG
+               fprintf (stderr, "End of File\n");
+#endif
+               free (buffer);
+               /*Could not receive key*/
+               return -1;
+       }
+       
+       buffer_value = (char*) malloc (BUFFER_SIZE+1);
+       if (buffer_value == NULL) goto memerror;
+       buffer_key = (char*) malloc (BUFFER_SIZE+1);
+       if (buffer_key == NULL) goto memerror;
+       buffer_comment = (char*) malloc (BUFFER_SIZE+1);
+       if (buffer_comment == NULL) goto memerror;
+       
+       for (i=0; i < string_length; i++) {
+#if DEBUG && VERBOSE
+               fprintf (stderr, "Processing |%c|%d|\n", buffer[i], buffer[i]);
+#endif
+               rc = parse_buffer (&buffer[i]);
+               if (rc == CHAR_OK) continue;
+               else if (rc == CHAR_SEC) break;
+               else if (rc == CHAR_ERR) {
+#if DEBUG
+                       fprintf (stderr, "Error reading char\n");
+#endif
+                       return -1;
+               }
+               else if (rc == CHAR_NEWLINE) { /* end of line found*/
+#if DEBUG && VERBOSE
+                       fprintf (stderr, "Found end of key (\\n)\n");
+#endif
+                       break;
+               }
+               else if (rc == CHAR_NULL ) {    /* anticipated end?*/
+                       if (i==string_length-1) { /* no its not*/
+                               string_length += BUFFER_SIZE;
+                               if (kdbiRealloc ((void**) & buffer, string_length) < 0)
+                                       goto memerror;
+                               else fprintf (stderr, "Realloc ok buffer (%p, %d)\n", buffer, string_length);
+                               fgets (buffer+string_length-BUFFER_SIZE,
+                                       BUFFER_SIZE, FILEPTR);
+                       } else {
+#if DEBUG
+                               fprintf (stderr, "No Enter found in this line?\n");
+#endif
+                               return -1;
+                       }
+               }
+               /*if (rc == CHAR_ADD); Fallthrough states*/
+               else if (state == STATE_KEY) {
+                       buffer_key [k++] = buffer[i];
+                       if (k == key_length-1)
+                       {
+                               key_length += BUFFER_SIZE;
+                               if (kdbiRealloc ((void **) & buffer_key, key_length) < 0)
+                                       goto memerror;
+                               else fprintf (stderr, "Realloc ok key\n");
+                       }
+               }
+               else if (state == STATE_VALUE) {
+                       buffer_value [v++] = buffer[i];
+                       if (v == value_length-1) 
+                       {
+                               value_length += BUFFER_SIZE;
+                               if (kdbiRealloc ((void **) & buffer_value, value_length) < 0) 
+                                       goto memerror;
+                               else fprintf (stderr, "Realloc ok value\n");
+                       }
+               }
+               else if (state == STATE_COMMENT) {
+                       buffer_comment [c++] = buffer[i];
+                       if (c == comment_length-1)
+                       {
+                               comment_length += BUFFER_SIZE;
+                               if (kdbiRealloc ((void **) & buffer_comment, comment_length) < 0)
+                                       goto memerror;
+                               else fprintf (stderr, "Realloc ok comment\n");
+                       }
+               }
+       }
+
+       buffer_value [v] = 0;
+       buffer_key [k] = 0;
+       buffer_comment [c] = 0; /* key eingelesen*/
+
+       if (make_key (key, root, buffer_key, buffer_value, buffer_comment) == -1)
+               goto memerror;
+       
+       free (buffer);
+       free (buffer_value);
+       free (buffer_key);
+       free (buffer_comment);
+       
+       return 0; /* success */
+
+memerror:
+#if DEBUG
+       fprintf (stderr, "Allocation error\n");
+#endif
+       free (buffer);
+       free (buffer_key);
+       free (buffer_value);
+       free (buffer_comment);
+       /*errno = KDB_ERR_NOMEM;*/
+       return -1;
+}
+
+/**This function makes a key out of the
+ * @param root is the root (prefix) of the keyname
+ * @param buffer_key is the postfix of the keyname
+ * @param buffer_value is the value
+ * @param buffer_comment contains the comment
+ * @return -1 on allocation error, 0 else*/
+int make_key (Key * key, char * root, char * buffer_key, char * buffer_value, char * buffer_comment)
+{
+       char * buffer_name = NULL;
+
+       if ((buffer_name = malloc (strlen(buffer_key) + strlen(root) + 2)) == NULL)
+               return -1;
+       
+       
+       buffer_name[0] = '\0';  /* buffer_name is empty*/
+       strcat (buffer_name, root);
+       strcat (buffer_name, "/");
+       strcat (buffer_name, buffer_key);
+       if (keySetName (key, buffer_name) == 0)
+#if DEBUG
+               fprintf (stderr, "Unable to set name\n");
+       else    fprintf (stderr, "Name set to %s\n", buffer_name);
+#endif
+       ; /**Semikolon in the beginning because of DEBUG not set*/
+       /**Freeing buffer_name here twice (see bottom) causes
+        * a very fancy libc bug, it crashes at another free()..*/
+       
+       if (keySetString (key, buffer_value) == 0)
+#if DEBUG
+               fprintf (stderr, "Unable to set value\n");
+       else    fprintf (stderr, "Value set to %s\n", buffer_value);
+#endif
+       
+       ;if (keySetComment (key, buffer_comment) == 0) /**Semikolon needed at begin*/
+#if DEBUG
+               fprintf (stderr, "Unable to set comment\n");
+       else    fprintf (stderr, "Comment set to %s\n", buffer_comment);
+#endif
+       ; /*WARNING semikolon needed*/
+       
+       key->flags &= ~KEY_FLAG_SYNC; /* remove sync flag*/
+       
+       free (buffer_name);
+       
+       return 0;
+}
+
+
+/**
+ * Writes out a key into file on pos.
+ * keySet is the key which should be written there
+ *
+ * @ret Returnes 0 on success.
+ * 
+ * @ingroup ini
+ */
+int write_key (KDB *handle, Key * setKey, long oldpos)
+{
+       long newpos;
+       long needed_size, sname, svalue, scomment; /**needed sizes*/
+       
+       char * name;
+       char * value;
+       char * comment;
+
+#if DEBUG
+       fprintf (stderr, "write_key (Key, pos: %ld)\n", oldpos);
+#endif
+       /** use setkey to set the key to wished values*/
+       newpos = ftell (FILEPTR);
+       name = strrchr (keyName (setKey),'/')+1;
+       value = (char*)keyValue (setKey);
+       comment = (char*)keyComment (setKey);
+
+       sname = convert_strlen (name, strlen (name));
+       svalue = convert_strlen (value, keyGetValueSize (setKey));
+       scomment = convert_strlen (comment, keyGetCommentSize (setKey));
+       needed_size = sname + svalue + scomment + 1; /* +\n */
+       
+       if (newpos - oldpos > needed_size)
+       {
+               shrink_file (handle, oldpos, newpos - oldpos -needed_size);
+       } else if (newpos - oldpos < needed_size) {
+               enlarge_file (handle, newpos, needed_size - (newpos - oldpos));
+       }
+       
+#if DEBUG
+       fprintf(stderr, "Writing key to disc (pos: %ld|%ld|%ld) ...\n",
+               oldpos, newpos, needed_size);
+#endif
+       fseek (FILEPTR, oldpos, SEEK_SET);
+       
+       convert_stream (name, sname, FILEPTR);
+       fwrite ("=", 1,1,FILEPTR);
+       convert_stream (value, svalue, FILEPTR);
+       fwrite (";", 1,1,FILEPTR);
+       convert_stream (comment, scomment, FILEPTR);
+       fwrite ("\n", 1,1,FILEPTR);
+
+#if DEBUG
+       newpos = ftell (FILEPTR);
+       fprintf (stderr, "Real endpos: %ld\n", newpos);
+       fprintf (stderr, "key: %s, value: %s, comment: %s\n", 
+               setKey->key, (char *) setKey->data, setKey->comment);
+#endif
+
+                       
+       return 0;
+}
+
+/**
+ * Removes a key on pos.
+ * keySet is the key which says the size it has.
+ * So you must get the key first.
+ *
+ * @ret Returnes 0 on success.
+ * 
+ * @ingroup ini
+ */
+int remove_key (KDB *handle, Key * setKey, long oldpos)
+{
+       long newpos;
+       long delete_size, sname, svalue, scomment; /**needed sizes*/
+       
+       char * name;
+       char * value;
+       char * comment;
+       name = strrchr (keyName (setKey),'/')+1;
+       value = (char*) keyValue (setKey);
+       comment = (char*) keyComment (setKey);
+       
+#if DEBUG
+       fprintf (stderr, "remove_key (Key, pos)\n");
+#endif
+       /** use setkey to set the key to wished values*/
+       newpos = ftell (FILEPTR);
+       
+       sname = convert_strlen (name, strlen (name));
+       svalue = convert_strlen (value, keyGetValueSize (setKey));
+       scomment = convert_strlen (comment, keyGetCommentSize (setKey));
+       delete_size = sname + svalue + scomment + 1; /* +\n */
+       
+       shrink_file (handle, oldpos, newpos - oldpos - delete_size);
+       
+#if DEBUG
+       fprintf(stderr, "Deleting key on disc (pos: %ld|%ld|%ld) ...\n",
+               oldpos, newpos, delete_size);
+#endif
+       return 0;
+}
+
diff --git a/src/backends/passwd/Makefile.am b/src/backends/passwd/Makefile.am
new file mode 100644 (file)
index 0000000..431ae7f
--- /dev/null
@@ -0,0 +1,22 @@
+#elekpasswddir = $(develdocdir)/backend-passwd/
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+sources = passwd.h passwd.c
+
+noinst_LIBRARIES = libelektra-passwd.a
+libelektra_passwd_a_SOURCES = $(sources) ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_passwd_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+
+backend_LTLIBRARIES = libelektra-passwd.la
+libelektra_passwd_la_SOURCES = $(sources) ../../include/kdb.h ../../include/kdbbackend.h
+libelektra_passwd_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_passwd_la_LIBADD = ../../libelektra/libelektra.la
+libelektra_passwd_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
diff --git a/src/backends/passwd/passwd.c b/src/backends/passwd/passwd.c
new file mode 100644 (file)
index 0000000..7d53b41
--- /dev/null
@@ -0,0 +1,377 @@
+/***************************************************************************
+            passwd.c  -  Access the /etc/passwd file
+                             -------------------
+    begin                : Nov 15 2007
+    copyright            : (C) 2007 by Patrick Sabin
+    email                : patricksabin@gmx.at
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <passwd.h>
+
+int kdbOpen_passwd(KDB *handle)
+{
+       KeySet *ks;
+       Key *key;
+       const char *s, *t;
+       int prefix_len;
+       PasswdData *data;
+       const Key* mnt = kdbhGetMountpoint(handle);
+       KDBCap *cap = kdbhGetCapability (handle);
+       size_t mntsize = keyGetNameSize(mnt)+1;
+
+       if (mnt==0)
+       {
+               return -1;
+       }
+
+       data=malloc(sizeof(PasswdData));
+       kdbhSetBackendData(handle,data);
+       data->mountpoint=0;
+       ks=kdbhGetConfig(handle);
+
+       ksRewind(ks);
+       key=ksNext(ks);
+
+       s=keyName(key);
+       t=s+KDB_KEY_MOUNTPOINTS_LEN;
+
+       /* skip entry name */
+       while (!(*t=='/'||!*t))
+               t++;
+       
+       if (!*t) {
+               fprintf(stderr, "Cannot happen\n");
+               exit(1);
+       }
+
+       
+       t+=sizeof("/config/")-1;
+       prefix_len=t-s;
+
+       data->mountpoint=malloc(mntsize);
+       snprintf(data->mountpoint,mntsize,"%s/",keyName(mnt));
+
+
+       data->path="/etc/passwd";
+       data->backend=BACKENDNAME;
+       for (;key;key=ksNext(ks)) {
+               if (strlen(keyName(key))<prefix_len) continue;
+
+               if (!strncmp(keyName(key)+prefix_len,"path",sizeof("path"))) {
+                       data->path=keyValue(key);
+               }
+       }
+
+       data->mountpointlen=strlen(data->mountpoint);
+
+       cap->onlyRemoveAll=1;
+       cap->onlyAddKeys=1;
+       cap->onlyFullSet=1;
+
+       cap->onlySystem=1;
+       cap->onlyUser=1;
+
+       cap->noOwner=1;
+       cap->noValue=1;
+       cap->noComment=1;
+       cap->noUID=1;
+       cap->noGID=1;
+       cap->noMode=1;
+       cap->noDir=1;
+       cap->noATime=1;
+       cap->noMTime=1;
+       cap->noCTime=1;
+       cap->noRemove=1;
+       cap->noStat=1;
+       cap->noMount=1;
+       cap->noBinary=1;
+       cap->noString=1;
+       cap->noTypes=1;
+       cap->noError=1;
+
+       return 0;
+}
+
+
+
+
+/**
+ * Finalize the backend.
+ * Called prior to unloading the backend dynamic module. Should ensure that no
+ * functions or static/global variables from the module will ever be accessed again.
+ *
+ * Make sure to free all memory that your backend requested at runtime.
+ *
+ * After this call, libelektra.so will unload the backend library, so this is
+ * the point to shutdown any affairs with the storage.
+ *
+ * @return 0 on success, anything else otherwise.
+ * @see kdbClose()
+ * @ingroup backend
+ */
+int kdbClose_passwd(KDB *handle)
+{
+       /* free all backend resources and shut it down */
+       PasswdData *data;
+       data=kdbhGetBackendData(handle);
+       free(data->mountpoint);
+       free(data);
+
+       return 0; /* success */
+}
+
+
+
+/**
+ * Implementation for kdbGet() method.
+ *
+ * @see kdbGet() for caller.
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param returned contains a keyset with relevant keys
+ * @param parentKey contains the information where to get the keys
+ * @return the number of keys got
+ * @return 0 on success with no changed key
+ * @return -1 on failure, the current key in returned shows the position and/or errno is set
+ * @ingroup backend
+ */
+ssize_t kdbGet_passwd(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int errnosave = errno;
+       ssize_t nr_keys = 0;
+       /* get all keys below parentKey and count them with nr_keys */
+       PasswdData *data;
+       const char *parent;
+       int parent_len;
+       /*KeySet *ks;
+       Key *key;
+       char *s,*t;
+       int prefix_len;
+       */
+       data=kdbhGetBackendData(handle);
+       parent=keyName(parentKey);
+       parent_len=strlen(parent);
+
+       if (parent_len<data->mountpointlen-1 ||
+                       strncmp(data->mountpoint,parent,data->mountpointlen-1))
+       {
+               // parentkey covers keys not handled by the backend
+               return -1;
+       }
+
+       if (parent_len==data->mountpointlen-1 &&
+                       !strncmp(data->mountpoint, parent,data->mountpointlen-1))
+       {
+               // return all username directories
+               char *name;
+               FILE *f;
+               struct passwd *pwd;
+
+               f=fopen(data->path,"r");
+
+               if (f==NULL) {
+                       /*errno=KDB_ERR_BACKEND;*/
+                       errno = errnosave;
+                       return -1;
+               }
+               while ((pwd=fgetpwent(f))) {
+                       char *pw_name;
+                       int pw_name_len;
+                       pw_name=pwd->pw_name;
+                       pw_name_len=strlen(pw_name);
+
+                       name=malloc(data->mountpointlen+pw_name_len+1);
+                       strncpy(name,data->mountpoint,data->mountpointlen+1);
+                       strncat(name,pw_name,pw_name_len);
+                       ksAppendKey(returned, keyNew(name,KEY_VALUE,"",0));
+                       nr_keys++;
+                       free (name);
+               }
+
+               fclose(f);
+
+       } else if (parent_len>data->mountpointlen &&
+                       !strncmp(data->mountpoint,parent,data->mountpointlen))
+       {
+               const char *username, *p;
+               p=username=parent+data->mountpointlen;
+               while (!(*p=='/'||*p==0)) p++;
+               if (*p==0) {
+                       FILE *f;
+                       struct passwd *pwd;
+                       int usernamelen;
+
+                       usernamelen=p-username;
+                       username=strndup(username,usernamelen);
+
+                       /* get username directory */
+                       f=fopen(data->path,"r");
+                       if (!f)
+                       {
+                               errno = errnosave;
+                               return -1;
+                       }
+                       while ((pwd=fgetpwent(f))) {
+                               if (!strncmp(pwd->pw_name,username,usernamelen)) {
+                                       char *name;
+                                       char *id;
+                                       size_t id_size;
+
+                                       name=malloc(data->mountpointlen+usernamelen+sizeof("/gecos"));
+                                       strncpy(name,data->mountpoint,data->mountpointlen+1);
+                                       strncat(name,username,usernamelen);
+                                       strncat(name,"/gecos",sizeof("/gecos")-1);
+                                       ksAppendKey(returned, keyNew(name,KEY_VALUE,pwd->pw_gecos,0));
+                                       nr_keys++;
+                                       free (name);
+
+                                       name=malloc(data->mountpointlen+usernamelen+sizeof("/home"));
+                                       strncpy(name,data->mountpoint,data->mountpointlen+1);
+                                       strncat(name,username,usernamelen);
+                                       strncat(name,"/home",sizeof("/home")-1);
+                                       ksAppendKey(returned, keyNew(name,KEY_VALUE,pwd->pw_dir,0));
+                                       nr_keys++;
+                                       free (name);
+
+                                       name=malloc(data->mountpointlen+usernamelen+sizeof("/shell"));
+                                       strncpy(name,data->mountpoint,data->mountpointlen+1);
+                                       strncat(name,username,usernamelen);
+                                       strncat(name,"/shell",sizeof("/shell")-1);
+                                       ksAppendKey(returned, keyNew(name,KEY_VALUE,pwd->pw_shell,0));
+                                       nr_keys++;
+                                       free (name);
+
+                                       name=malloc(data->mountpointlen+usernamelen+sizeof("/uid"));
+                                       strncpy(name,data->mountpoint,data->mountpointlen+1);
+                                       strncat(name,username,usernamelen);
+                                       strncat(name,"/uid",sizeof("/uid")-1);
+                                       id_size=snprintf(NULL,0,"%d",(int)pwd->pw_uid);
+                                       id=malloc(id_size);
+                                       snprintf(id,id_size,"%d",(int)pwd->pw_uid);
+                                       ksAppendKey(returned, keyNew(name,KEY_VALUE,id,0));
+                                       nr_keys++;
+                                       free (name);
+                                       free (id);
+
+                                       name=malloc(data->mountpointlen+usernamelen+sizeof("/gid"));
+                                       strncpy(name,data->mountpoint,data->mountpointlen+1);
+                                       strncat(name,username,usernamelen);
+                                       strncat(name,"/gid",sizeof("/gid")-1);
+                                       id_size=snprintf(NULL,0,"%d",(int)pwd->pw_gid);
+                                       id=malloc(id_size);
+                                       snprintf(id,id_size,"%d",(int)pwd->pw_gid);
+                                       ksAppendKey(returned, keyNew(name,KEY_VALUE,id,0));
+                                       nr_keys++;
+                                       free (name);
+                                       free (id);
+
+                                       break;
+                               }
+                       }
+                       fclose (f);
+                       kdbiFree ((void*)username);
+                       if (!pwd) {
+                               /* entry not found */
+                               /*errno=KDB_ERR_NOTFOUND;*/
+                               errno = errnosave;
+                               return -1;
+                       }
+               } else {
+                       /* parent key not a valid directory */
+                       errno = errnosave;
+                       return -1;
+               }
+       }
+
+       errno = errnosave;
+       return nr_keys; /* success */
+}
+
+
+/**
+ * Implementation for kdbSet() method.
+ *
+ * @see kdbSet() for caller.
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param returned contains a keyset with relevant keys
+ * @param parentKey contains the information where to set the keys
+ * @return the number of keys set
+ * @return 0 on success with no changed key in database
+ * @return -1 on failure, the current key in returned shows the position and/or errno is set
+ * @ingroup backend
+ */
+ssize_t kdbSet_passwd(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       int errnosave = errno;
+       ssize_t nr_keys = 0;
+       Key *key;
+       PasswdData *data;
+       const char *parent;
+       int parent_len;
+
+       parent=keyName(parentKey);
+       parent_len=strlen(parent);
+
+       data=kdbhGetBackendData(handle);
+
+       if (parent_len<data->mountpointlen-1 || strncmp(data->mountpoint,parent,data->mountpointlen-1)) {
+               // parentkey covers keys not handled by the backend
+               errno = errnosave;
+               return -1;
+       }
+
+       ksRewind(returned);
+       key=ksNext(returned);
+       for (;key;ksNext(returned)) {
+               const char *name=keyName(key);
+               int name_len=strlen(name);
+
+               if (name_len>data->mountpointlen-1 && !strncmp(data->mountpoint,name,name_len)) {
+                       /* keyname is mounted name. This key is not writeable */
+
+               } else if (name_len>data->mountpointlen && !strncmp(data->mountpoint,name,data->mountpointlen)) {
+
+               } else {
+
+               }
+       }
+       /* set all keys below parentKey and count them with nr_keys */
+       errno = errnosave;
+       return nr_keys;
+}
+
+/**
+ * All KDB methods implemented by the backend can have random names, except
+ * kdbBackendFactory(). This is the single symbol that will be looked up
+ * when loading the backend, and the first method of the backend
+ * implementation that will be called.
+ * 
+ * Its purpose is to "publish" the exported methods for libelektra.so. The
+ * implementation inside the provided skeleton is usually enough: simply
+ * call kdbBackendExport() with all methods that must be exported.
+ * 
+ * @return whatever kdbBackendExport() returns
+ * @see kdbBackendExport() for an example
+ * @see kdbOpenBackend()
+ * @ingroup backend
+ */
+KDBEXPORT(passwd)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_passwd,
+               KDB_BE_CLOSE,   &kdbClose_passwd,
+               KDB_BE_GET,     &kdbGet_passwd,
+               KDB_BE_SET,     &kdbSet_passwd,
+               KDB_BE_VERSION, BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Patrick Sabin <patricksabin@gmx.at>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION,
+                       "Reads and writes /etc/passwd content",
+               KDB_BE_END);
+}
diff --git a/src/backends/passwd/passwd.h b/src/backends/passwd/passwd.h
new file mode 100644 (file)
index 0000000..bea9bd2
--- /dev/null
@@ -0,0 +1,63 @@
+/***************************************************************************
+            passwd.h  -  Access the /etc/passwd file
+                             -------------------
+    begin                : Nov 15 2007
+    copyright            : (C) 2007 by Patrick Sabin
+    email                : patricksabin@gmx.at
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef PASSWD_H
+#define PASSWD_H
+
+
+#define _GNU_SOURCE
+
+#include <kdbbackend.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdio.h>
+
+
+#define BACKENDNAME "passwd"
+#define BACKENDVERSION "0.0.1"
+#define BACKENDPATH "system/users"
+#define BACKENDDESCR "add description here"
+
+int kdbbWriteLock (FILE *f);
+int kdbbReadLock (FILE *f);
+int kdbbUnlock (FILE *f);
+
+struct _PasswdData {
+       char *backend;
+       const char *path;
+       char *mountpoint; /* path of the mountpoint inclusive '/' */
+       int mountpointlen; /* strlen(root) */
+};
+
+typedef struct _PasswdData PasswdData;
+
+/**
+ * the passwd backend can be configured with three config variables:
+ * backendname (optional) is the name of this backend. Default: "passwd"
+ * passwd_path (optional) is the path where the passwd file is stored. Default: /etc/passwd
+ * mountpoint (optional) is the mountpoint. Default: take value from handle->mountpoint.
+ */
+
+int kdbOpen_passwd(KDB *handle);
+int kdbClose_passwd(KDB *handle);
+ssize_t kdbGet_passwd(KDB *handle, KeySet *ks, const Key *parentKey);
+ssize_t kdbSet_passwd(KDB *handle, KeySet *ks, const Key *parentKey);
+KDBEXPORT(passwd);
+
+#endif
diff --git a/src/backends/template/Makefile.am b/src/backends/template/Makefile.am
new file mode 100644 (file)
index 0000000..a286824
--- /dev/null
@@ -0,0 +1,21 @@
+#elektemplatedir = $(develdocdir)/backend-template/
+
+#EXTRA_DIST = template.c README
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+template_sources = template.c template.h ../../include/kdb.h ../../include/kdbbackend.h
+
+noinst_LIBRARIES = libelektra-template.a
+libelektra_template_a_SOURCES = $(template_sources)
+libelektra_template_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+
+backend_LTLIBRARIES = libelektra-template.la
+libelektra_template_la_SOURCES = $(template_sources)
+libelektra_template_la_LDFLAGS = -version-info $(BACKEND_VERSION_API) -module
+libelektra_template_la_LIBADD = ../../libelektra/libelektra.la
+libelektra_template_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+../../libelektra/libelektra.la:
+       cd ../../libelektra/ && $(MAKE) libelektra.la
diff --git a/src/backends/template/template.c b/src/backends/template/template.c
new file mode 100644 (file)
index 0000000..9998d8a
--- /dev/null
@@ -0,0 +1,118 @@
+/***************************************************************************
+            template.c  -  Skeleton of backends to access the Key Database
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/***************************************************************************
+ *                                                                         *
+ *   This is the skeleton of the methods you'll have to implement in order *
+ *   to provide libelektra.so a valid backend.                             *
+ *   Simple fill the empty _template functions with your code and you are   *
+ *   ready to go.                                                          *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <template.h>
+
+int kdbOpen_template(KDB *handle)
+{
+       int errnosave = errno;
+       KDBCap *cap = kdbhGetCapability (handle);
+
+       cap->onlyFullGet=1;
+       cap->noStat=1;
+
+       cap->onlyRemoveAll=1;
+
+       cap->onlyFullSet=1;
+       cap->onlyAddKeys=1;
+
+       cap->onlySystem=1;
+       cap->onlyUser=1;
+
+       cap->noOwner=1;
+       cap->noValue=1;
+       cap->noComment=1;
+       cap->noUID=1;
+       cap->noGID=1;
+       cap->noMode=1;
+       cap->noDir=1;
+       cap->noATime=1;
+       cap->noMTime=1;
+       cap->noCTime=1;
+       cap->noRemove=1;
+       cap->noLink=1;
+       cap->noMount=1;
+       cap->noBinary=1;
+       cap->noString=1;
+       cap->noTypes=1;
+       cap->noError=1;
+
+       cap->noLock=1;
+       cap->noThread=1;
+
+       /* backend initialization logic */
+
+       errno = errnosave;
+       return 0;
+}
+
+int kdbClose_template(KDB *handle)
+{
+       int errnosave = errno;
+       /* free all backend resources and shut it down */
+
+       errno = errnosave;
+       return 0; /* success */
+}
+
+ssize_t kdbGet_template(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       ssize_t nr_keys = 0;
+       int errnosave = errno;
+
+       /* get all keys below parentKey and count them with nr_keys */
+
+       errno = errnosave;
+       return nr_keys; /* success */
+}
+
+ssize_t kdbSet_template(KDB *handle, KeySet *returned, const Key *parentKey)
+{
+       ssize_t nr_keys = 0;
+       int errnosave = errno;
+
+       /* set all keys below parentKey and count them with nr_keys */
+
+       errno = errnosave;
+       return nr_keys;
+}
+
+KDBEXPORT(template)
+{
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,    &kdbOpen_template,
+               KDB_BE_CLOSE,   &kdbClose_template,
+               KDB_BE_GET,     &kdbGet_template,
+               KDB_BE_SET,     &kdbSet_template,
+               KDB_BE_VERSION,        BACKENDVERSION,
+               KDB_BE_AUTHOR,  "Full Name <email@libelektra.org>",
+               KDB_BE_LICENCE, "BSD",
+               KDB_BE_DESCRIPTION,
+                       "Add description here",
+               KDB_BE_END);
+}
+
diff --git a/src/backends/template/template.h b/src/backends/template/template.h
new file mode 100644 (file)
index 0000000..3752332
--- /dev/null
@@ -0,0 +1,40 @@
+/***************************************************************************
+            template.h  -  Skeleton of backends to access the Key Database
+                             -------------------
+    begin                : Mon Dec 26 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/***************************************************************************
+ *                                                                         *
+ *   This is the skeleton of the methods you'll have to implement in order *
+ *   to provide libelektra.so a valid backend.                             *
+ *   Simple fill the empty _template functions with your code and you are   *
+ *   ready to go.                                                          *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+#include <kdbbackend.h>
+#include <errno.h>
+
+
+#define BACKENDNAME "template"
+#define BACKENDVERSION "0.0.1"
+
+int kdbOpen_template(KDB *handle);
+int kdbClose_template(KDB *handle);
+ssize_t kdbGet_template(KDB *handle, KeySet *ks, const Key *parentKey);
+ssize_t kdbSet_template(KDB *handle, KeySet *ks, const Key *parentKey);
+KDBEXPORT(template);
diff --git a/src/backends/winregistry/winregistry.cpp b/src/backends/winregistry/winregistry.cpp
new file mode 100644 (file)
index 0000000..f82f7d7
--- /dev/null
@@ -0,0 +1,426 @@
+// winregistry.cpp : Defines the entry point for the DLL application.
+// $Id$
+
+#include "winregistry.h"
+#include <windows.h>
+
+int mapError(LONG err);
+
+BOOL APIENTRY DllMain( HANDLE hModule, 
+                       DWORD  ul_reason_for_call, 
+                       LPVOID lpReserved
+                                        )
+{
+       switch (ul_reason_for_call)
+       {
+       case DLL_PROCESS_ATTACH:
+       case DLL_THREAD_ATTACH:
+       case DLL_THREAD_DETACH:
+       case DLL_PROCESS_DETACH:
+               break;
+       }
+    return TRUE;
+}
+
+
+
+/**
+ * @defgroup backend Elektra framework for pluggable backends
+ * @brief The tactics to create pluggable backends to libelektra.so
+ *
+ * Since version 0.4.9, Elektra can dynamically load different key storage
+ * backends. Fast jump to kdbBackendExport() to see an example of a backend
+ * implementation.
+ * 
+ * The methods of class KDB that are backend dependent are kdbOpen(),
+ * kdbClose(), kdbGetKey(), kdbSetKey(), kdbStatKey(),
+ * kdbGetKeyChildKeys(), kdbRemove(), kdbRename(). So a backend must
+ * reimplement these methods.
+ * 
+ * And methods that have a builtin default high-level inefficient
+ * implementation are kdbSetKeys(), kdbMonitorKey(), kdbMonitorKeys(). So
+ * it is suggested to reimplement them too, to make them more efficient.
+ *
+ * The other KDB methods are higher level. They use the above methods to
+ * do their job, and generally don't have to be reimplemented for a
+ * different backend.
+ * 
+ * The backend must implement a method with name kdbBackendFactory() and no
+ * parameters, that is responsible of exporting the implementations of 
+ * libelektra.so backend dependent methods.
+ * 
+ * The backend implementation must:
+ * @code
+#include <kdbbackend.h>
+ * @endcode
+ * 
+ * <b>Better than that, a skeleton of a backend implementation is provided inside
+ * Elektra development package or source code tree, and should be used as a
+ * base for the implementation.</b>
+ * 
+ * An elektrified program will use the backend defined by environment variable
+ * @e $KDB_BACKEND, The backend library is dynamically loaded when the program
+ * calls kdbOpen(), unless if the program is security/authentication/setuid
+ * related, in which it probably uses the more secure kdbOpenDefault() which
+ * completely ignores the @e $KDB_BACKEND environment and will use the
+ * @c "default" named backend defined by the sysadmin. Look at
+ * @c /lib/libelektra-default.so link to see the default backend for your
+ * system.
+ * 
+ * Elektra source code or development package provides a skeleton and Makefile
+ * to implement a backend, and we'll document this skeleton here.
+ * 
+ * A backend is defined by a single name, for example @c BACKENDNAME, that
+ * causes libelektra.so look for its library as @c libelektra-BACKENDNAME.so.
+ * 
+ * Elektra source code tree includes several backend implementations
+ * (http://germane-software.com/repositories/elektra/trunk/src/backends)
+ * that can also be used as a reference.
+ */
+
+
+
+
+
+/**
+ * Initialize the backend.
+ * This is the first method kdbOpenBackend() calls after dynamically loading
+ * the backend library.
+ *
+ * This method is responsible of:
+ * - backend's specific configuration gathering
+ * - all backend's internal structs initialization
+ * - initial setup of all I/O details such as opening a file, connecting to a
+ *   database, etc
+ *
+ * @return 0 on success, anything else otherwise.
+ * @see kdbOpenBackend()
+ * @see kdbOpen()
+ * @ingroup backend
+ */
+int kdbOpen_winregistry(KDB *handle) {
+       /* backend initialization logic */
+       return 0;
+}
+
+
+
+
+/**
+ * All finalization logic of the backend should go here.
+ * 
+ * Called prior to unloading the backend dynamic module. Should ensure that no
+ * functions or static/global variables from the module will ever be accessed again.
+ * Should free any memory that the backend no longer needs.
+ * After this call, libelektra.so will unload the backend library, so this is
+ * the point to shutdown any affairs with the storage.
+ *
+ * @return 0 on success, anything else otherwise.
+ * @see kdbClose()
+ * @ingroup backend
+ */
+int kdbClose_winregistry(KDB *handle) {
+       /* free all backend resources and shut it down */
+       return 0; /* success */
+}
+
+
+
+/**
+ * Implementation for kdbStatKey() method.
+ *
+ * This method is responsible of:
+ * - make necessary I/O to retrieve @p key->name's metadata
+ * - fill the @p key struct with its metadata
+ *
+ * @see kdbStatKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbStatKey_winregistry(KDB *handle, Key *key) {
+       /* get the most possible key metainfo */
+       return 0; /* success */
+}
+
+
+/**
+ * Implementation for kdbGetKey() method.
+ *
+ * This method is responsible of:
+ * - make necessary I/O to retrieve all @p key->name's value and metadata
+ * - fill the @p key struct with its value and metadata
+ *
+ * @see kdbGetKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbGetKey_winregistry(KDB *handle, Key *key) {
+       /* fully gets a key */
+       HKEY rootKey = {0};
+       HKEY desiredKey = {0};
+       char path[1024];
+       DWORD type;
+       char val[1024];
+       DWORD sizeval = sizeof(val);
+       char *keyName = NULL;
+       LONG err=0;
+       if(!kdbGetRegistryPath(key, rootKey, path, sizeof(path), &keyName))
+       {
+               return -1;
+       }
+       
+       if(err = RegGetValue(rootKey, path, keyName, RRF_RT_REG_SZ|RRF_RT_REG_MULTI_SZ, &type, val, &sizeval) != ERROR_SUCCESS)
+       {
+               errno = mapError(err);
+               return -1;
+       }
+       keySetType(key,KEY_TYPE_STRING);
+       keySetRaw(key, val, strlen(val));
+       /*if((err = RegOpenKeyEx(rootKey, path, 0, KEY_READ,&desiredKey)) != ERROR_SUCCESS)
+       {
+               /* Error opening key 
+               errno = mapError(err);
+               return -1;
+       }
+       if(RegQueryValueEx(desiredKey, keyName, 0,&type, (LPBYTE)val, &sizeval) != ERROR_SUCCESS)
+       {
+               errno = mapError(err);
+               return -1;
+       }*/
+       
+
+
+       return 0; /* success */
+}
+
+
+
+/**
+ * Implementation for kdbSetKey() method.
+ *
+ * This method is responsible of:
+ * - check the existence of @p key->name on persistent storage
+ * - prepare the backend to receive a new or updated key
+ * - use value and metadata from @p key to store them in the backend storage
+ * - fill the @p key struct with its value and metadata
+ *
+ * @see kdbSetKey() for expected behavior.
+ * @ingroup backend
+ */
+int kdbSetKey_winregistry(KDB *handle, Key *key) {
+       /* fully sets a key */
+       HKEY rootKey = {0};
+       HKEY desiredKey = {0};
+       char path[1024];
+       DWORD type;
+       char *keyName = NULL;
+       LONG err=0;
+       if(!kdbGetRegistryPath(key, rootKey, path, sizeof(path), &keyName))
+       {
+               return -1;
+       }
+       if((err = RegOpenKeyEx(rootKey, path, 0, KEY_READ,&desiredKey)) != ERROR_SUCCESS)
+       {
+               /* Error opening key */
+               errno = mapError(err);
+               return -1;
+       }
+       /* TODO: NOT DELETE VALUE HERE!!! */
+       if((err = RegSet(?)Value(desiredKey, keyName)) != ERROR_SUCCESS)
+       {
+               errno = mapError(err);
+               RegCloseKey(desiredKey);
+               return -1;
+       }
+       RegCloseKey(desiredKey);
+       return 0;  /* success */
+}
+
+
+
+/**
+ * Implementation for kdbRename() method.
+ *
+ * @see kdbRename() for expected behavior.
+ * @ingroup backend
+ */
+int kdbRename_winregistry(KDB *handle, Key *key, const char *newName) {
+       /* rename a key to another name */
+       return 0; /* success */
+}
+
+
+
+
+/**
+ * Implementation for kdbRemoveKey() method.
+ *
+ * @see kdbRemove() for expected behavior.
+ * @ingroup backend
+ */
+int kdbRemoveKey_winregistry(KDB *handle, const Key *key) {
+       /* remove a key from the database */
+       HKEY rootKey = {0};
+       HKEY desiredKey = {0};
+       char path[1024];
+       DWORD type;
+       char *keyName = NULL;
+       LONG err=0;
+       if(!kdbGetRegistryPath(key, rootKey, path, sizeof(path), &keyName))
+       {
+               return -1;
+       }
+       if((err = RegOpenKeyEx(rootKey, path, 0, KEY_READ,&desiredKey)) != ERROR_SUCCESS)
+       {
+               /* Error opening key */
+               errno = mapError(err);
+               return -1;
+       }
+       if((err = RegDeleteValue(desiredKey, keyName)) != ERROR_SUCCESS)
+       {
+               errno = mapError(err);
+               RegCloseKey(desiredKey);
+               return -1;
+       }
+       RegCloseKey(desiredKey);
+       return 0;  /* success */
+}
+
+
+
+
+/**
+ * Implementation for kdbGetKeyChildKeys() method.
+ *
+ * @see kdbGetKeyChildKeys() for expected behavior.
+ * @ingroup backend
+ */
+ssize_t kdbGetKeyChildKeys_winregistry(KDB *handle, const Key *parentKey, KeySet *returned, unsigned long options) {
+       /* retrieve multiple hierarchical keys */
+       return (ssize_t)returned->size; /* success */
+}
+
+
+/**
+ * Implementation for kdbSetKeys() method.
+ * 
+ * The implementation of this method is optional, and a builtin, probablly 
+ * inefficient implementation can be explicitly used when exporting the
+ * backend with kdbBackendExport(), using kdbSetKeys_default().
+ * 
+ * @see kdbSetKeys() for expected behavior.
+ * @ingroup backend
+ */
+int kdbSetKeys_winregistry(KDB *handle, KeySet *ks) {
+       /* set many keys */
+       return 0;
+}
+
+
+/**
+ * The implementation of this method is optional.
+ * The builtin inefficient implementation will use kdbGetKey() for each
+ * key inside @p interests.
+ *
+ * @see kdbMonitorKeys() for expected behavior.
+ * @ingroup backend
+ */
+uint32_t kdbMonitorKeys_winregistry(KDB *handle, KeySet *interests, uint32_t diffMask,
+               unsigned long iterations, unsigned sleep) {
+       return 0;
+}
+
+
+
+/**
+ *
+ * The implementation of this method is optional.
+ * The builtin inefficient implementation will use kdbGetKey() for
+ * @p interest.
+ *
+ * @see kdbMonitorKey() for expected behavior.
+ * @ingroup backend
+ */
+uint32_t kdbMonitorKey_winregistry(KDB *handle, Key *interest, uint32_t diffMask,
+               unsigned long iterations, unsigned sleep) {
+       return 0;
+}
+
+KDBEXPORT(BACKENDNAME) {
+       return kdbBackendExport(BACKENDNAME,
+               KDB_BE_OPEN,           &kdbOpen_winregistry,
+               KDB_BE_CLOSE,          &kdbClose_winregistry,
+               KDB_BE_GETKEY,         &kdbGetKey_winregistry,
+               KDB_BE_SETKEY,         &kdbSetKey_winregistry,
+               KDB_BE_STATKEY,        &kdbStatKey_winregistry,
+               KDB_BE_RENAME,         &kdbRename_winregistry,
+               KDB_BE_REMOVEKEY,      &kdbRemoveKey_winregistry,
+               KDB_BE_GETCHILD,       &kdbGetKeyChildKeys_winregistry,
+               KDB_BE_END);
+}
+
+/**
+ * Calculate the real file name for a key.
+ *
+ * @param returned the buffer to return the calculated filename
+ * @param maxSize maximum number of bytes that fit the buffer
+ * @see kdbCalcRelativeFilename()
+ * @return number of bytes written to the buffer, or 0 on error
+ * @ingroup internals
+ */
+size_t kdbGetRegistryPath(const Key *forKey,HKEY rootkey, char *path, size_t maxSize, char **keyName) {
+       size_t length=0;
+       char *sid = NULL;
+
+       switch (keyGetNamespace(forKey)) {
+               case KEY_NS_SYSTEM: 
+                       /* Prepare to use the 'system/ *' database */
+                       rootkey = HKEY_LOCAL_MACHINE;
+                       length = snprintf(path, maxSize, "%s/%s", KDB_REGISTRY_PATH, forKey->key);
+                       break;
+
+               case KEY_NS_USER: 
+                       /* Prepare to use the 'user:????/ *' database */
+                       if (forKey->userDomain) 
+                       {
+                               /* This is unsupported so far */
+                               errno = KDB_ERR_INVALIDKEY;
+                               return 0;
+                               /* FIXME: The Following code should work immediately as soon as getSID is implemented */
+                               /*rootkey = HKEY_USERS;
+                               sid=getSID(forKey->userDomain);
+                               if (!sid) return 0; /* propagate errno 
+                               length = snprintf(path,maxSize, "%s/%s/%s", sid, KDB_REGISTRY_PATH, forKey->key);*/
+                       }
+                       else
+                       {
+                               rootkey = HKEY_CURRENT_USER;
+                               length = snprintf(path, maxSize, "%s/%s", KDB_REGISTRY_PATH, forKey->key);
+                       }
+                       break;
+
+               default: {
+                       errno=KDB_ERR_INVALIDKEY;
+                       return 0;
+               }
+       }
+       *keyName = strrchr(path, '/');
+       *keyName = '\0';
+       *keyName++;
+       return length;
+}
+
+/* getSID finds the Security identifier of a given username. This is needed to use user:username/ elektra urls */
+size_t getSID(char *username, char *sid, int size)
+{
+       return 0;
+}
+
+int mapError(LONG err)
+{
+       switch(err)
+       {
+       case ERROR_ACCESS_DENIED: return KDB_ERR_NOCRED;
+       default:
+               return KDB_ERR_NOTFOUND;
+       }
+}
+
diff --git a/src/backends/winregistry/winregistry.h b/src/backends/winregistry/winregistry.h
new file mode 100644 (file)
index 0000000..fbe15c6
--- /dev/null
@@ -0,0 +1,65 @@
+// $Id$
+
+// The following ifdef block is the standard way of creating macros which make exporting 
+// from a DLL simpler. All files within this DLL are compiled with the WINREGISTRY_EXPORTS
+// symbol defined on the command line. this symbol should not be defined on any project
+// that uses this DLL. This way any other project whose source files include this file see 
+// WINREGISTRY_API functions as being imported from a DLL, whereas this DLL sees symbols
+// defined with this macro as being exported.
+#ifdef WINREGISTRY_EXPORTS
+#define WINREGISTRY_API __declspec(dllexport)
+#else
+#define WINREGISTRY_API __declspec(dllimport)
+#endif
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kdb.h>
+#include <kdbbackend.h>
+
+#define BACKENDNAME "winregistry"
+#define KDB_REGISTRY_PATH "SOFTWARE/Elektra"
+
+/* Private functions */
+size_t kdbGetRegistryPath(const Key *forKey,HKEY rootkey, char *path, size_t maxSize, char **keyName);
+size_t getSID(char *username, char *sid, int size);
+
+/* Exported functions */
+#ifdef __cplusplus
+extern "C" {
+#endif
+int kdbOpen_winregistry(KDB *handle);
+int kdbClose_winregistry(KDB *handle);
+int kdbStatKey_winregistry(KDB handle, Key *key);
+int kdbGetKey_winregistry(KDB handle, Key *key);
+int kdbSetKey_winregistry(KDB handle, Key *key);
+int kdbRename_winregistry(KDB handle, Key *key, const char *newName);
+int kdbRemoveKey_winregistry(const Key *key);
+ssize_t kdbGetKeyChildKeys_winregistry(const Key *parentKey, KeySet *returned, unsigned long options);
+
+/* While the windows registry does provide a far more efficient implementation of these, it's fine to live without them for now */
+/*int kdbSetKeys_winregistry(KeySet *ks);
+uint32_t kdbMonitorKeys_winregistry(KeySet *interests, uint32_t diffMask, unsigned long iterations, unsigned sleep);
+uint32_t kdbMonitorKey_winregistry(Key *interest, uint32_t diffMask, unsigned long iterations, unsigned sleep);*/
+
+/**
+ * All KDB methods implemented by the backend can have random names, except
+ * kdbBackendFactory(). This is the single symbol that will be looked up
+ * when loading the backend, and the first method of the backend
+ * implementation that will be called.
+ * 
+ * Its purpose is to "publish" the exported methods for libelektra.so. The
+ * implementation inside the provided skeleton is usually enough: simply
+ * call kdbBackendExport() with all methods that must be exported.
+ * 
+ * @return whatever kdbBackendExport() returns
+ * @see kdbBackendExport() for an example
+ * @see kdbOpenBackend()
+ * @ingroup backend
+ */
+WINREGISTRY_API KDBEXPORT(BACKENDNAME);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/bindings/Makefile.am b/src/bindings/Makefile.am
new file mode 100644 (file)
index 0000000..88c1bdb
--- /dev/null
@@ -0,0 +1,6 @@
+
+# $Id: Makefile.am 658 2006-03-05 11:31:26Z aviram $
+
+DIST_SUBDIRS = cpp python
+SUBDIRS = @BINDINGS@
+
diff --git a/src/bindings/cpp/.deps/libelektra_cpp_a-kdb.Po b/src/bindings/cpp/.deps/libelektra_cpp_a-kdb.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/.deps/libelektra_cpp_a-key.Po b/src/bindings/cpp/.deps/libelektra_cpp_a-key.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/.deps/libelektra_cpp_a-keyset.Po b/src/bindings/cpp/.deps/libelektra_cpp_a-keyset.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/.deps/libelektra_cpp_la-kdb.Plo b/src/bindings/cpp/.deps/libelektra_cpp_la-kdb.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/.deps/libelektra_cpp_la-key.Plo b/src/bindings/cpp/.deps/libelektra_cpp_la-key.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/.deps/libelektra_cpp_la-keyset.Plo b/src/bindings/cpp/.deps/libelektra_cpp_la-keyset.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/Makefile.am b/src/bindings/cpp/Makefile.am
new file mode 100644 (file)
index 0000000..9c8161d
--- /dev/null
@@ -0,0 +1,32 @@
+INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/bindings/cpp/include
+AM_CXXFLAGS = $(CXXSTDFLAGS) $(CXXOPTFLAGS) $(CXXDBGFLAGS)
+
+SUBDIRS = tests
+DIST_SUBDIRS = tests
+
+include_HEADERS = include/kdb include/key include/keyset
+
+dependencies = ../../include/kdb.h
+
+sources =  kdb.cpp key.cpp keyset.cpp include/kdb include/key include/keyset
+
+objects = kdb.o key.o keyset.o
+
+lib_LIBRARIES = libelektra-cpp.a
+libelektra_cpp_a_SOURCES = $(sources)
+libelektra_cpp_a_DEPENDENCIES =
+libelektra_cpp_a_CXXFLAGS = $(cxxflags)
+libelektra_cpp_a_CPPFLAGS = -DELEKTRA_STATIC $(cppflags)
+libelektra_cpp_a_LIBADD =
+BUILT_SOURCES =
+
+lib_LTLIBRARIES = libelektra-cpp.la
+libelektra_cpp_la_SOURCES = $(sources)
+libelektra_cpp_la_DEPENDENCIES =
+libelektra_cpp_la_CXXFLAGS = $(cxxflags)
+libelektra_cpp_la_CPPFLAGS = $(cppflags)
+libelektra_cpp_la_LDFLAGS = -version-info 0:0:0
+libelektra_cpp_la_LIBADD = ../../libelektra/libelektra.la
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
diff --git a/src/bindings/cpp/include/kdb b/src/bindings/cpp/include/kdb
new file mode 100644 (file)
index 0000000..747503f
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef CPP_KDB_H
+#define CPP_KDB_H
+
+#include <string>
+#include <key>
+#include <keyset>
+
+#include <kdb.h>
+
+
+namespace kdb {
+
+class KDBException : public std::exception
+{
+       const char* what() {return "KDB Exception";}
+};
+
+class KDB
+{
+public:
+       KDB ();
+       ~KDB ();
+
+       size_t get (KeySet & returned, const Key &parentKey, option_t options = KDB_O_NONE);
+       size_t get (KeySet & returned, const std::string &parentName, option_t options = KDB_O_NONE);
+       size_t get (KeySet & returned, const char * parentName, option_t options = KDB_O_NONE);
+
+       size_t set (KeySet & returned, const Key &parentKey, option_t options = KDB_O_NONE);
+
+       void get (Key & toGet);
+       void set (const Key & toSet);
+
+       void getString (const std::string &keyname, std::string value, size_t maxSize);
+       void setString (const std::string &keyname, const std::string &value);
+       void remove (const std::string &keyname);
+
+protected:
+       /**You may use the KDB in an inherited class*/
+       ckdb::KDB* handle;
+};
+
+} // end of namespace kdb
+
+#endif
+
diff --git a/src/bindings/cpp/include/key b/src/bindings/cpp/include/key
new file mode 100644 (file)
index 0000000..66f20d4
--- /dev/null
@@ -0,0 +1,173 @@
+#ifndef CPP_KEY_H
+#define CPP_KEY_H
+
+#include <sstream>
+#include <string>
+#include <kdb.h>
+
+namespace kdb {
+
+class KeyException : public std::exception
+{
+       const char* what() {return "Key Exception";}
+};
+
+class KeyInvalidName : public KeyException
+{
+       const char* what() {return "Invalid Keyname";}
+};
+
+/**Key represents an elektra key.*/
+class Key
+{
+public:
+       Key () :key (ckdb::keyNew (0)) { operator++(); }
+       Key (ckdb::Key *k) :key(k) { operator++(); }
+       Key (Key &k) :key (k.key) { operator++(); }
+       Key (const Key &k) :key (k.key) { operator++(); }
+       Key (const char * name, va_list ap);
+       Key (const char * name, ...);
+       Key (const std::string name, ...);
+
+       void del () { operator --(); ckdb::keyDel(key); }
+       void copy (const Key &other) { ckdb::keyCopy(key,other.key); }
+       void clear () { ckdb::keyCopy(key,0); }
+       ~Key () { del(); }
+
+       void operator ++(int) { operator++(); }
+       void operator ++() { ckdb::keyIncRef(key); }
+
+       void operator --(int) { operator--(); }
+       void operator --() { ckdb::keyDecRef(key); }
+
+       bool operator ==(const Key &k) const { return key == k.key; }
+
+       template <class T>
+       T get()
+       {
+               T x;
+               std::string str;
+               str = getString();
+               std::istringstream ist(str);
+               ist >> x;       // convert string to type
+               return x;
+       }
+
+       template <class T>
+       void set(T x)
+       {
+               std::string str;
+               std::ostringstream ost;
+               ost << x;       // convert type to string
+               setString (ost.str());
+       }
+
+       /*Passes out the pointer, maybe directly changing this object*/
+       ckdb::Key* getKey () const { return key; }
+       ckdb::Key* operator* () const { return key; }
+       ckdb::Key* dup () const { return ckdb::keyDup(getKey()); }
+
+       Key& operator= (ckdb::Key *k);
+       Key& operator= (const Key &k);
+
+       Key& operator=  (const std::string &name);
+       Key& operator+= (const std::string &name);
+       Key& operator-= (const std::string &name);
+
+       Key& operator=  (const char *name);
+       Key& operator+= (const char *name);
+       Key& operator-= (const char *name);
+
+       type_t getType() const;
+       void setType(type_t type = KEY_TYPE_STRING);
+
+       std::string getName() const;
+       const char* name() const;
+       size_t getNameSize() const;
+
+       std::string getBaseName() const;
+       const char* baseName() const;
+       size_t getBaseNameSize() const;
+
+       void setName (const std::string &name);
+       void setBaseName (const std::string &basename);
+
+       size_t getFullNameSize() const;
+       std::string getFullName() const;
+
+       std::string getComment() const;
+       const char* comment() const;
+       size_t getCommentSize() const;
+       void setComment(const std::string &comment);
+
+       uid_t getUID() const;
+       void setUID(uid_t uid);
+
+       gid_t getGID() const;
+       void setGID(gid_t gid);
+
+       mode_t getMode() const;
+       void setMode(mode_t mode);
+
+       std::string getOwner() const;
+       const char* owner() const;
+       size_t getOwnerSize() const;
+       void setOwner(const std::string &owner);
+
+       const void* value() const;
+       size_t getValueSize() const;
+
+       std::string getString() const;
+       void setString(std::string newString);
+
+       size_t getBinary(void *returnedBinary, size_t maxSize) const;
+       size_t setBinary(const void *newBinary, size_t dataSize);
+
+       void setDir ();
+
+       void setMTime(time_t time);
+       void setATime(time_t time);
+       void setCTime(time_t time);
+
+       time_t getMTime() const;
+       time_t getATime() const;
+       time_t getCTime() const;
+
+       bool isSystem() const;
+       bool isUser() const;
+
+       int getNamespace() const;
+       size_t getReference() const;
+
+       bool isDir() const;
+       bool isString() const;
+       bool isBinary() const;
+
+       bool isInactive() const;
+       bool isBelow(const Key &k) const;
+       bool isDirectBelow(const Key &k) const;
+
+       bool needSync() const;
+       bool needRemove() const;
+       bool needStat() const;
+
+       void remove();
+       void stat();
+
+       /*
+       ssize_t toStream(FILE* stream = stdout, unsigned long options = 0) const;
+       ssize_t output(FILE* stream = stdout, unsigned long options = 0) const;
+       ssize_t generate(FILE* stream = stdout, unsigned long options = 0) const;
+       */
+
+       friend std::ostream & operator << (std::ostream & os, const Key &d);
+       friend std::istream & operator >> (std::istream & os, Key &d);
+
+private:
+       ckdb::Key * key; // holds elektra key struct
+};
+
+} // end of namespace kdb
+
+#endif
+
diff --git a/src/bindings/cpp/include/keyset b/src/bindings/cpp/include/keyset
new file mode 100644 (file)
index 0000000..a37683e
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef CPP_KEYSET_H
+#define CPP_KEYSET_H
+
+#include <string>
+
+#include <kdb.h>
+#include <key>
+
+namespace kdb {
+
+class KeySetException : public std::exception
+{
+       const char* what() {return "Key Exception";}
+};
+
+class KeySetOutOfRange : public KeySetException
+{
+       const char* what() {return "Out of range in KeySet";}
+};
+
+class KeySetNotFound : public KeySetException
+{
+       const char* what() {return "Could not find Key in KeySet";}
+};
+
+/**Key represents an elektra key.*/
+class KeySet
+{
+public:
+       KeySet () :ks (ckdb::ksNew(0)) {}
+       KeySet (ckdb::KeySet *k) :ks(k) {}
+       KeySet (const KeySet &other);
+       KeySet (size_t alloc, va_list ap);
+       KeySet (size_t alloc, ...);
+       ~KeySet ();
+
+       ckdb::KeySet * getKeySet () const { return ks; }
+       void setKeySet (ckdb::KeySet * k) { ks = k; }
+
+       ckdb::KeySet* dup () const { return ckdb::ksDup(ks); }
+
+       void copy (const KeySet &other) { ckdb::ksCopy(ks,other.ks); }
+       void clear () { ckdb::ksCopy(ks,0); }
+
+       void sort();
+
+       size_t size () const;
+
+       size_t append (const Key &toAppend);
+       size_t append (const KeySet &toAppend);
+
+       Key pop();
+
+       void rewind () const;
+       Key next();
+       Key current() const;
+
+       Key head() const;
+       Key tail() const;
+
+       void setCursor(cursor_t cursor);
+       cursor_t getCursor() const;
+
+       Key lookup (const Key &k, const option_t options = KDB_O_NONE) const;
+       Key lookup (const std::string &name, const option_t options = KDB_O_NONE) const;
+       Key lookup (const char *name, const option_t options = KDB_O_NONE) const;
+
+       /*
+       ssize_t toStream(FILE* stream = stdout, option_t options = (option_t)0) const;
+       ssize_t output(FILE* stream = stdout, option_t options = 0) const;
+       ssize_t generate(FILE* stream = stdout, option_t options = 0) const;
+       */
+
+       friend std::ostream & operator << (std::ostream & os, const KeySet &d);
+       friend std::istream & operator >> (std::istream & os, KeySet &d);
+
+private:
+       /*
+       KeySet (KeySet &k) :ks (k.ks) { }
+       KeySet (const KeySet &k) :ks (k.ks) { }
+       */
+
+private:
+       ckdb::KeySet * ks; // holds elektra keyset struct
+};
+
+} // end of namespace kdb
+
+#endif
+
diff --git a/src/bindings/cpp/kdb.cpp b/src/bindings/cpp/kdb.cpp
new file mode 100644 (file)
index 0000000..39a548f
--- /dev/null
@@ -0,0 +1,97 @@
+#include <kdb>
+#include <iostream>
+
+namespace kdb
+{
+
+/**
+ * Constructs a class KDB.
+ */
+KDB::KDB ()
+{
+       handle = ckdb::kdbOpen();
+}
+
+/**
+ * The destructor closes the database.
+ */
+KDB::~KDB ()
+{
+       ckdb::kdbClose(handle);
+}
+
+/**
+ * Get all keys below parentKey inside returned.
+ *
+ * @param returned the keyset where the keys will be in
+ * @param parentKey the parentKey of returned
+ * @param options to change the behaviour which keys to fetch
+ */
+size_t KDB::get (KeySet & returned, const Key & parentKey, option_t options)
+{
+       ssize_t ret = ckdb::kdbGet (handle, returned.getKeySet(), parentKey.getKey(), options);
+       if (ret == -1) throw KDBException();
+       return ret;
+}
+
+
+size_t KDB::set (KeySet & returned, const Key & parentKey, option_t options)
+{
+       ssize_t ret = ckdb::kdbSet(handle, returned.getKeySet(), parentKey.getKey(), options);
+       if (ret == -1) throw KDBException();
+       return ret;
+}
+
+
+size_t KDB::get (KeySet & returned, const std::string &parentName, option_t options)
+{
+       ssize_t ret = ckdb::kdbGetByName (handle, returned.getKeySet(), parentName.c_str(), options);
+       if (ret == -1) throw KDBException();
+       return ret;
+}
+
+size_t KDB::get (KeySet & returned, const char * parentName, option_t options)
+{
+       ssize_t ret = ckdb::kdbGetByName (handle, returned.getKeySet(), parentName, options);
+       if (ret == -1) throw KDBException();
+       return ret;
+}
+
+
+void KDB::getString (const std::string &keyname, std::string value, size_t maxSize)
+{
+       char *c = new char[maxSize];
+       ckdb::kdbGetString(handle, keyname.c_str(), c, maxSize);
+       value = c;
+       delete (c);
+}
+
+void KDB::setString (const std::string &keyname, const std::string &value)
+{
+       ckdb::kdbSetString(handle, keyname.c_str(), value.c_str());
+}
+
+void KDB::remove (const std::string &keyname)
+{
+       ckdb::kdbRemove(handle, keyname.c_str());
+}
+
+/**
+ * Get a single key.
+ *
+ * @param toGet the key to get
+ */
+void KDB::get (Key & toGet)
+{
+       int ret = ckdb::kdbGetKey(handle, toGet.getKey());
+       if (ret == -1) throw KDBException();
+}
+
+void KDB::set (const Key & toSet)
+{
+       int ret = ckdb::kdbSetKey(handle, toSet.getKey());
+       if (ret == -1) throw KDBException();
+}
+
+} // end of namespace kdb
+
diff --git a/src/bindings/cpp/key.cpp b/src/bindings/cpp/key.cpp
new file mode 100644 (file)
index 0000000..572c74e
--- /dev/null
@@ -0,0 +1,457 @@
+#include <key>
+#include <iostream>
+
+#include <cstdarg>
+
+namespace kdb {
+
+Key::Key (const char * str, va_list ap)
+{
+       key = ckdb::keyVNew (str, ap);
+
+       operator++();
+}
+
+Key::Key (const char * str, ...)
+{
+       va_list ap;
+
+       va_start(ap, str);
+       key = ckdb::keyVNew (str, ap);
+       va_end(ap);
+
+       operator++();
+}
+
+Key::Key (const std::string str, ...)
+{
+       va_list ap;
+
+       va_start(ap, str);
+       key = ckdb::keyVNew (str.c_str(), ap);
+       va_end(ap);
+
+       operator++();
+}
+
+Key& Key::operator= (ckdb::Key *k)
+{
+       if (key != k)
+       {
+               del();
+               key = k;
+               operator++();
+       }
+       return *this;
+}
+
+Key& Key::operator= (const Key &k)
+{
+       if (this != &k)
+       {
+               del();
+               key = k.key;
+               operator++();
+       }
+       return *this;
+}
+
+Key& Key::operator= (const std::string &name)
+{
+       ckdb::keySetName(getKey(), name.c_str());
+       return *this;
+}
+
+Key& Key::operator+= (const std::string &name)
+{
+       ckdb::keyAddBaseName(getKey(), name.c_str());
+       return *this;
+}
+
+Key& Key::operator-= (const std::string &name)
+{
+       ckdb::keySetBaseName(getKey(), name.c_str());
+       return *this;
+}
+
+Key& Key::operator= (const char *name)
+{
+       ckdb::keySetName(getKey(), name);
+       return *this;
+}
+
+Key& Key::operator+= (const char *name)
+{
+       ckdb::keyAddBaseName(getKey(), name);
+       return *this;
+}
+
+Key& Key::operator-= (const char *name)
+{
+       ckdb::keySetBaseName(getKey(), name);
+       return *this;
+}
+
+/**Returns the type of the Key*/
+type_t Key::getType() const
+{
+       return ckdb::keyGetType (key);
+}
+
+/**Sets the type of the Key*/
+void Key::setType(type_t type)
+{
+       ckdb::keySetType (getKey(), type);
+}
+
+size_t Key::getNameSize() const
+{
+       return ckdb::keyGetNameSize (getKey());
+}
+
+std::string Key::getName() const
+{
+       return std::string (ckdb::keyName(key));
+}
+
+const char* Key::name() const
+{
+       return ckdb::keyName(getKey());
+}
+
+size_t Key::getBaseNameSize() const
+{
+       return ckdb::keyGetBaseNameSize (getKey());
+}
+
+std::string Key::getBaseName() const
+{
+       return std::string (ckdb::keyBaseName(key));
+}
+
+
+const char* Key::baseName() const
+{
+       return ckdb::keyBaseName(getKey());
+}
+
+
+/**Sets a name for a key.
+ * Throws kdb::KeyInvalidName when the name is not valid*/
+void Key::setName (const std::string &name)
+{
+       if (ckdb::keySetName (getKey(), name.c_str()) == -1)
+               throw KeyInvalidName();
+}
+
+/**Sets a base name for a key.
+ * Throws kdb::KeyInvalidName when the name is not valid*/
+void Key::setBaseName (const std::string &name)
+{
+       if (ckdb::keySetBaseName (getKey(), name.c_str()) == -1)
+               throw KeyInvalidName();
+}
+
+size_t Key::getFullNameSize() const
+{
+       return ckdb::keyGetFullNameSize (getKey());
+}
+
+std::string Key::getFullName() const
+{
+       size_t csize = ckdb::keyGetFullNameSize (getKey());
+       std::string str;
+       char * field = new char [csize];
+
+       ckdb::keyGetFullName (getKey(), field, csize);
+       str = field;
+       delete [] field;
+       return str;
+}
+
+/**Returns the comment for the key.*/
+std::string Key::getComment() const
+{
+       return std::string(ckdb::keyComment(key));
+}
+
+const char* Key::comment() const
+{
+       return ckdb::keyComment (key);
+}
+
+/**Returns the size of the comment*/
+size_t Key::getCommentSize() const
+{
+       return ckdb::keyGetCommentSize (key);
+}
+
+/**Sets a comment for the specified key.*/
+void Key::setComment(const std::string &comment)
+{
+       ckdb::keySetComment (getKey(), comment.c_str());
+}
+
+/**Returns the UID of the the key. It always
+ * returs the current UID*/
+uid_t Key::getUID() const
+{
+       return ckdb::keyGetUID (getKey());
+}
+
+/**Sets another UID for a key. This will always
+ * fail, because you are a user.*/
+void Key::setUID(uid_t uid)
+{
+       ckdb::keySetUID (getKey(), uid);
+}
+
+/**Gets the Groupid from a specific key.*/
+gid_t Key::getGID() const
+{
+       return ckdb::keyGetGID (getKey());
+}
+
+/**Sets the Groupid for a specific key. Only
+ * groups where the user is a member a valid.*/
+void Key::setGID(gid_t gid)
+{
+       ckdb::keySetGID (getKey(), gid);
+}
+
+/**Returns the Mode of a key.
+ * It is based on 4 Octets: special, user, group and other
+ * The first bit is execute (not used for kdb)
+ * The second bit is write mode.
+ * The third bit is read mode.
+ * See more in chmod (2).
+ * An easier description may be in chmod (1), but
+ * the +-rwx method is not supported.*/
+mode_t Key::getMode() const
+{
+       return ckdb::keyGetMode(getKey());
+}
+
+/**Sets the Mode of a key. For more info see
+ * getMode (std::string ).*/
+void Key::setMode(mode_t mode)
+{
+       ckdb::keySetMode (getKey(),mode);
+}
+
+/**Returns the Owner of the Key. It will always return
+ * the current user.*/
+std::string Key::getOwner() const
+{
+       return std::string (ckdb::keyOwner(key));
+}
+
+const char* Key::owner() const
+{
+       return ckdb::keyOwner(key);
+}
+
+size_t Key::getOwnerSize() const
+{
+       return ckdb::keyGetOwnerSize(key);
+}
+
+/**Sets the Owner of the Key. It will fail, because
+ * you are not root.*/
+void Key::setOwner(const std::string &owner)
+{
+       ckdb::keySetOwner(getKey(), owner.c_str());
+}
+
+const void*Key::value() const
+{
+       return ckdb::keyValue (key);
+}
+
+/**Returns the DataSize of the Binary or String. It is used
+ * for the internal malloc().*/
+size_t Key::getValueSize() const
+{
+       return ckdb::keyGetValueSize (key);
+}
+
+/**Returns the string directly from the key. It should be
+ * the same as get().*/
+std::string Key::getString() const
+{
+       size_t csize =  ckdb::keyGetValueSize (getKey());
+       std::string str;        
+       char * field = new char [csize];
+
+       ckdb::keyGetString (getKey(), field, csize);
+       str = field;
+       delete [] field;
+       return str;
+}
+
+/**Sets the String of a key.*/
+void Key::setString(std::string newString)
+{
+       ckdb::keySetString (getKey(), newString.c_str());
+}
+
+/**Returns the binary Value of the key. It will not be encoded
+ * or decoded.*/
+size_t Key::getBinary(void *returnedBinary, size_t maxSize) const
+{
+       return ckdb::keyGetBinary (getKey(), returnedBinary, maxSize);
+}
+
+/**Sets a binary Value of a key*/
+size_t Key::setBinary(const void *newBinary, size_t dataSize)
+{      
+       size_t s = ckdb::keySetBinary (getKey(), newBinary, dataSize);
+       return s;
+}
+
+void Key::setDir ()
+{
+       ckdb::keySetDir (key);
+}
+
+void Key::setMTime (time_t time)
+{
+       ckdb::keySetMTime (key, time);
+}
+
+void Key::setATime (time_t time)
+{
+       ckdb::keySetATime (key, time);
+}
+
+void Key::setCTime (time_t time)
+{
+       ckdb::keySetCTime (key, time);
+}
+
+/**Returns the time the Key was modified*/
+time_t Key::getMTime() const
+{
+       return ckdb::keyGetMTime(key);
+}
+
+/**Returns the last access time.*/
+time_t Key::getATime() const
+{
+       return ckdb::keyGetATime(key);
+}
+
+/**Returns when the Key last was changed.*/
+time_t Key::getCTime() const
+{
+       return ckdb::keyGetCTime(key);
+}
+
+bool Key::isSystem() const
+{
+       return ckdb::keyIsSystem(key);
+}
+
+bool Key::isUser() const
+{
+       return ckdb::keyIsUser(key);
+}
+
+size_t Key::getReference() const
+{
+       return ckdb::keyGetRef(key);
+}
+
+bool Key::isDir() const
+{
+       return ckdb::keyIsDir(key);
+}
+
+bool Key::isString() const
+{
+       return ckdb::keyIsString(key);
+}
+
+bool Key::isBinary() const
+{
+       return ckdb::keyIsBinary(key);
+}
+
+bool Key::isInactive () const
+{
+       return ckdb::keyIsInactive (key);
+}
+
+bool Key::isBelow(const Key & k) const
+{
+       return ckdb::keyIsBelow(key, k.getKey());
+}
+
+bool Key::isDirectBelow(const Key & k) const
+{
+       return ckdb::keyIsDirectBelow(key, k.getKey());
+}
+
+bool Key::needSync() const
+{
+       return ckdb::keyNeedSync(key);
+}
+
+bool Key::needRemove() const
+{
+       return ckdb::keyNeedRemove(key);
+}
+
+bool Key::needStat() const
+{
+       return ckdb::keyNeedStat(key);
+}
+
+void Key::remove()
+{
+       ckdb::keyRemove (key);
+}
+
+void Key::stat()
+{
+       ckdb::keyStat (key);
+}
+
+/*
+ssize_t Key::toStream(FILE* stream, unsigned long options) const
+{
+       return ckdb::keyToStream (getKey(), stream, options);
+}
+
+ssize_t Key::output(FILE* stream, unsigned long options) const
+{
+       return ckdb::keyOutput (getKey(), stream, options);
+}
+
+ssize_t Key::generate(FILE* stream, unsigned long options) const
+{
+       return ckdb::keyGenerate(getKey(), stream, options);
+}
+*/
+
+std::ostream & operator << (std::ostream & os, const Key &k)
+{
+       os << "Name: " << k.getName() 
+          << " Value: " << k.getString() 
+          << " Comment: " << k.getComment()
+          << " Domain: " << (k.isUser() ? "user" : "system");
+       return os;
+}
+
+std::istream & operator >> (std::istream & is, Key &k)
+{
+       std::string name, value, comment;
+       is >> name >> value >> comment;
+       k.setName(name);
+       k.set<std::string>(value);
+       k.setComment(comment);
+       return is;
+}
+
+} // end of namespace kdb
+
diff --git a/src/bindings/cpp/keyset.cpp b/src/bindings/cpp/keyset.cpp
new file mode 100644 (file)
index 0000000..6eae765
--- /dev/null
@@ -0,0 +1,148 @@
+#include <keyset>
+
+namespace kdb {
+
+KeySet::KeySet (size_t alloc, va_list ap)
+{
+       ks = ckdb::ksVNew (alloc, ap);
+}
+
+KeySet::KeySet (size_t alloc, ...)
+{
+       va_list va;
+
+       va_start (va, alloc);
+       ks = ckdb::ksVNew (alloc, va);
+       va_end (va);
+}
+
+KeySet::KeySet (const KeySet &other)
+{
+       ks = ckdb::ksNew(0);
+       ckdb::ksCopy(ks, other.ks);
+}
+
+KeySet::~KeySet ()
+{
+       ckdb::ksDel (ks);
+}
+
+size_t KeySet::size () const
+{
+       return ckdb::ksGetSize(ks);
+}
+
+void KeySet::rewind () const
+{
+       ckdb::ksRewind(ks);
+}
+
+
+void KeySet::setCursor(cursor_t cursor)
+{
+       ckdb::ksSetCursor(ks, cursor);
+}
+
+cursor_t KeySet::getCursor() const
+{
+       return ckdb::ksGetCursor(ks);
+}
+
+
+Key KeySet::next()
+{
+       ckdb::Key *k = ckdb::ksNext(ks);
+       if (k==0) throw KeySetOutOfRange();
+       return Key(k);
+}
+
+Key KeySet::current() const
+{
+       return Key(ckdb::ksCurrent(ks));
+}
+
+Key KeySet::head() const
+{
+       return Key(ckdb::ksHead(ks));
+}
+
+Key KeySet::tail() const
+{
+       return Key(ckdb::ksTail(ks));
+}
+
+Key KeySet::pop()
+{
+       ckdb::Key *k = ckdb::ksPop(ks);
+       if (k==0) throw KeySetOutOfRange();
+       Key key(k);
+       return key;
+}
+
+Key KeySet::lookup (const Key &key, const option_t options) const
+{
+       ckdb::Key *k = ckdb::ksLookup(ks, key.getKey(), options);
+       if (k==0) throw KeySetNotFound();
+       return Key(k);
+}
+
+Key KeySet::lookup (const std::string &name, const option_t options) const
+{
+       ckdb::Key *k = ckdb::ksLookupByName(ks, name.c_str(), options);
+       if (k==0) throw KeySetNotFound();
+       return Key(k);
+}
+
+Key KeySet::lookup (const char *name, const option_t options) const
+{
+       ckdb::Key *k = ckdb::ksLookupByName(ks, name, options);
+       if (k==0) throw KeySetNotFound();
+       return Key(k);
+}
+
+size_t KeySet::append (const Key &toAppend)
+{
+       return ckdb::ksAppendKey(ks, toAppend.getKey());
+}
+
+size_t KeySet::append (const KeySet &toAppend)
+{
+       return ckdb::ksAppend(ks, toAppend.getKeySet());
+}
+
+void KeySet::sort()
+{
+       ckdb::ksSort(ks);
+}
+
+/*
+ssize_t KeySet::toStream(FILE* stream, option_t options) const
+{
+       return ckdb::ksToStream (getKeySet(), stream, options);
+}
+
+ssize_t KeySet::output(FILE* stream, option_t options) const
+{
+       return ckdb::ksOutput (getKeySet(), stream, options);
+}
+
+ssize_t KeySet::generate(FILE* stream, option_t options) const
+{
+       return ckdb::ksGenerate(getKeySet(), stream, options);
+}
+*/
+
+std::ostream & operator << (std::ostream & os, const KeySet &k)
+{
+       os << "Keyset";
+       return os;
+}
+
+std::istream & operator >> (std::istream & is, KeySet &k)
+{
+       return is;
+}
+
+
+} // end of KeySet
+
diff --git a/src/bindings/cpp/tests/.deps/test_kdb.Po b/src/bindings/cpp/tests/.deps/test_kdb.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/tests/.deps/test_key.Po b/src/bindings/cpp/tests/.deps/test_key.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/tests/.deps/test_ks.Po b/src/bindings/cpp/tests/.deps/test_ks.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/tests/.deps/tests.Po b/src/bindings/cpp/tests/.deps/tests.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/cpp/tests/Makefile.am b/src/bindings/cpp/tests/Makefile.am
new file mode 100644 (file)
index 0000000..233142d
--- /dev/null
@@ -0,0 +1,29 @@
+INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/bindings/cpp/include
+AM_CXXFLAGS= $(CXXOPTFLAGS) $(CXXDBGFLAGS)
+
+TESTS=test_key test_ks test_kdb
+
+check_PROGRAMS=test_key test_ks test_kdb
+
+if VALGRINDTESTS
+TESTS_ENVIRONMENT = $(VALGRIND) --quiet --show-reachable=yes --leak-check=yes
+endif
+
+test_key_SOURCES = test_key.cpp tests.h tests.cpp
+test_key_LDADD = $(privatelibs) ../libelektra-cpp.a ../../../libelektra/libelektra.a
+
+test_ks_SOURCES = test_ks.cpp tests.h tests.cpp
+test_ks_LDADD = $(privatelibs) ../libelektra-cpp.a ../../../libelektra/libelektra.a
+
+test_kdb_SOURCES = test_kdb.cpp tests.h tests.cpp
+test_kdb_LDADD = $(privatelibs) ../libelektra-cpp.a ../../../libelektra/libelektra.a
+
+../libelektra-cpp.a:
+       cd .. && $(MAKE) libelektra-cpp.a
+
+../../../libelektra/libelektra.a:
+       cd ../../../libelektra && $(MAKE) libelektra.a
+
+clean-local:
+       rm -rf .kdb
+       rm -f *.gcno *.gcda *.gcno
diff --git a/src/bindings/cpp/tests/test_kdb.cpp b/src/bindings/cpp/tests/test_kdb.cpp
new file mode 100644 (file)
index 0000000..7840dfa
--- /dev/null
@@ -0,0 +1,54 @@
+#include <tests.h>
+
+void test_kdbGetSet()
+{
+       bool ok;
+       cout << "testing kdbSet() and kdbGet()" << endl;
+
+       KeySet ks_set (5,
+               *Key ("user/tests/key3", KEY_DIR, KEY_END),
+               *Key ("user/tests/key3/1", KEY_END),
+               *Key ("user/tests/key3/2", KEY_END),
+               *Key ("user/tests/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+       KDB kdb;
+       kdb.set (ks_set, Key("user/tests/key3", KEY_END));
+
+       Key nonexist ("user/this/key/shuld/really/not/exist",KEY_END);
+       ok = false;
+       try {
+               kdb.get (nonexist);
+       } catch (KDBException) {
+               ok = true;
+       }
+       succeed_if (ok, "get did not throw KDBException");
+
+       KeySet ks_get;
+       kdb.get (ks_get, Key("user/tests/key3", KEY_END));
+       // ks_get.toStream();
+       // ks_set.generate();
+       
+       // now remove keys
+       kdb.set (ks_set, Key("user/tests", KEY_END), KDB_O_REMOVEONLY);
+}
+
+#include <cstdlib>
+
+int main()
+{
+       cout << "KDB CLASS TESTS" << endl;
+       cout << "==================" << endl << endl;
+
+#ifdef HAVE_CLEARENV
+       clearenv();
+#endif
+#ifdef HAVE_SETENV
+       setenv ("KDB_HOME",".",1);
+#endif
+
+       test_kdbGetSet();
+
+       cout << endl;
+       cout << "test_key RESULTS: " << nbTest << " test(s) done. " << nbError << " error(s)." << endl;
+       return nbError;
+}
diff --git a/src/bindings/cpp/tests/test_key.cpp b/src/bindings/cpp/tests/test_key.cpp
new file mode 100644 (file)
index 0000000..5e3c9d1
--- /dev/null
@@ -0,0 +1,373 @@
+#include <tests.h>
+
+void test_keynew()
+{
+       cout << "testing keynew" << endl;
+
+       char array[] = "here is some data stored";
+       char * getBack;
+
+       Key key0;
+       succeed_if(!key0.needSync(), "key1 need sync");
+       succeed_if(!key0.needRemove(), "key1 need remove");
+       succeed_if( key0.getName() == "", "key0 has wrong name");
+
+       // Empty key
+       Key key1 ("", KEY_END);
+       succeed_if(!key1.needSync(), "key1 need sync");
+       succeed_if(!key1.needRemove(), "key1 need remove");
+       succeed_if( key1.getName() == "", "key0 has wrong name");
+
+       // Key with name
+       Key key2 ("system/sw/test", KEY_END);
+       succeed_if( key2.needSync(), "key2 does not need sync");
+       succeed_if(!key2.needRemove(), "key2 need remove");
+       succeed_if (key2.getBaseName() == "test", "wrong base name");
+       succeed_if( key2.getName() == "system/sw/test", "key2 has wrong name");
+       key2.copy(key0);
+       succeed_if( key2.getName() == "", "key0 has wrong name");
+       succeed_if (key2.getBaseName() == "", "wrong base name");
+
+       // Key with name
+       Key key3 ("system/sw/test", KEY_REMOVE, KEY_END);
+       succeed_if(key3.needRemove(), "KEY_REMOVE not set");
+       succeed_if(key3.getName() == "system/sw/test", "key3 has wrong name");
+       succeed_if(key3.getBaseName() == "test", "wrong base name");
+       key3 = "system/other/name";
+       succeed_if(key3.getName() == "system/other/name", "key3 has wrong name");
+       succeed_if(key3.getBaseName() == "name", "wrong base name");
+       key3 += "base";
+       succeed_if(key3.getName() == "system/other/name/base", "key3 has wrong name");
+       succeed_if(key3.getBaseName() == "base", "wrong base name");
+       key3 -= "name";
+       succeed_if(key3.getName() == "system/other/name/name", "key3 has wrong name");
+       succeed_if(key3.getBaseName() == "name", "wrong base name");
+
+       // Key with name + value (default type must be KEY_TYPE_STRING)
+       Key key4 ("system/sw/test",
+                       KEY_VALUE, "test",
+                       KEY_END);
+       succeed_if(key4.getName() == "system/sw/test", "key4 has wrong name");
+       succeed_if(key4.get<string>() == "test", "key4 has wrong name");
+       // key4.generate(stdout, 0); cout << endl;
+
+       // Key with name + UID/GID
+       Key key5 ("system/sw/test",
+                       KEY_UID, 123,
+                       KEY_GID, 456,
+                       KEY_END);
+       succeed_if(key5.getUID() == 123, "key5 UID no set correctly");
+       succeed_if(key5.getGID() == 456, "key5 GID not set correctly");
+       succeed_if(key5.getName() == "system/sw/test", "key5 has wrong name");
+       // key5.output(stdout, 0);
+
+       // Key with name + MODE
+       Key key6 ("system/sw/test",
+                       KEY_MODE, 0642,
+                       KEY_END);
+       succeed_if(key6.getMode() == 0642, "key6 mode no set correctly");
+       succeed_if(key6.getName() == "system/sw/test", "key6 has wrong name");
+
+       // Key with name + owner
+       Key key7 ("system/sw/test",
+                       KEY_OWNER, "yl",
+                       KEY_END);
+       succeed_if( key7.getOwner() ==  "yl", "key7 owner not set correctly");
+       succeed_if (!key7.isInactive(), "key should not be inactive");
+
+       Key key8 ("system/valid/there",
+                       KEY_TYPE, KEY_TYPE_BINARY,
+                       KEY_SIZE, sizeof(array),
+                       KEY_VALUE, array,
+                       KEY_END);
+       succeed_if(key8.getName() == "system/valid/there", "key8 has wrong name");
+       succeed_if(key8.isBinary (), "Could not set type to binary");
+       succeed_if(key8.getValueSize() == sizeof(array), "Value size not correct");
+       getBack = new char [key8.getValueSize()];
+       key8.getBinary(getBack, key8.getValueSize());
+       succeed_if(memcmp(getBack, array, sizeof(array)) == 0, "could not get correct value with keyGetBinary");
+       delete [] getBack;
+       succeed_if (key8.getBaseName() == "there", "wrong base name");
+
+       Key key9("system/valid/.inactive", KEY_COMMENT, "inactive key", KEY_END);
+       succeed_if (key9.isInactive(), "key should be inactive");
+       succeed_if (key9.getComment() == "inactive key", "comment failed");
+       succeed_if (key9.getBaseName() == ".inactive", "wrong base name");
+}
+
+void test_constructor()
+{
+       cout << "testing constructor" << endl;
+
+       ckdb::Key *ck = ckdb::keyNew(0);
+       Key k = ck; // constructor with (ckdb::Key)
+
+       cout << "ck:   " << (void*)ck << endl;
+       cout << "k:    " << (void*)&k << endl;
+       cout << "k.ck: " << (void*)k.getKey() << endl;
+
+       k.set<int>(30);
+       succeed_if (k.get<int> () == 30, "could not get same int");
+}
+
+void test_setkey()
+{
+       cout << "testing setkey" << endl;
+
+       ckdb::Key *ck;
+       Key k;
+
+       ck = ckdb::keyNew(0);
+       k = ck; // operator= alias for setKey()
+
+       cout << "ck:   " << (void*)ck << endl;
+       cout << "k:    " << (void*)&k << endl;
+       cout << "k.ck: " << (void*)k.getKey() << endl;
+
+       k.set<int>(30);
+       succeed_if (k.get<int> () == 30, "could not get same int");
+}
+
+void test_cast()
+{
+       cout << "testing cast" << endl;
+       ckdb::Key *ck;
+       Key *k;
+
+       ck = ckdb::keyNew(0);
+       k = (Key*) &ck; // no copy, just a cast
+
+       cout << "&ck:  " << (void*)&ck << endl;
+       cout << "k:    " << (void*)&k << endl;
+       cout << "ck:   " << (void*)ck << endl;
+       cout << "k.ck: " << (void*)k->getKey() << endl;
+
+       k->set<int>(30);
+       succeed_if (k->get<int> () == 30, "could not get same int");
+
+       ckdb::keyDel (ck);
+}
+
+void test_value ()
+{
+       cout << "testing value" << endl;
+       Key test;
+
+       test.setString ("23.3");
+       succeed_if (test.get<double> () == 23.3, "could not get same double");
+       succeed_if (test.getValueSize () == 5, "value size not correct");
+
+       test.setString ("401");
+       succeed_if (test.get<int> () == 401, "could not get same int");
+       succeed_if (test.getValueSize () == 4, "value size not correct");
+
+       test.setString ("mystr");
+       succeed_if (test.get<string> () == "mystr", "could not get same string");
+       succeed_if (test.getValueSize () == 6, "value size not correct");
+
+       test.setString ("myoth");
+       succeed_if (test.get<string> () == "myoth", "could not get same string");
+       succeed_if (test.getValueSize () == 6, "value size not correct");
+
+       test.set<double> (23.3);
+       succeed_if (test.getString() == "23.3", "could not get same double");
+       succeed_if (test.getValueSize () == 5, "value size not correct");
+
+       test.set<int> (401);
+       succeed_if (test.getString() == "401", "could not get same int");
+       succeed_if (test.getValueSize () == 4, "value size not correct");
+
+       test.set<string> ("mystr");
+       succeed_if (test.getString() == "mystr", "could not get same string");
+       succeed_if (test.getValueSize () == 6, "value size not correct");
+
+       test.set<string> ("myoth");
+       succeed_if (test.getString () == "myoth", "could not get same string");
+       succeed_if (test.getValueSize () == 6, "value size not correct");
+
+       test.setComment ("mycomment");
+       succeed_if (test.getComment () == "mycomment", "could not get same comment");
+       succeed_if (test.getCommentSize () == 10, "comment size not correct");
+}
+
+void test_exceptions ()
+{
+       cout << "testing exceptions" << endl;
+       Key test;
+
+       try {
+               test.setName("no");
+       } catch (kdb::KeyInvalidName)
+       {
+               succeed_if (test.getName() == "", "not set to noname");
+       }
+
+       test.setName ("user/name");
+       succeed_if (test.getName() == "user/name", "could not get same name");
+
+       try {
+               test.setName("no");
+       } catch (kdb::KeyInvalidName)
+       {
+               succeed_if (test.getName() == "", "not set to noname");
+       }
+       
+}
+
+void test_name()
+{
+       cout << "testing name" << endl;
+
+       Key test;
+       test.setName("user:markus/test");
+       succeed_if (std::string(test.name()) == "user/test", "Wrong name");
+       succeed_if (test.getName() == "user/test", "Wrong name");
+       succeed_if (test.getFullName() == "user:markus/test", "Wrong full name");
+       succeed_if (std::string(test.owner()) == "markus", "Wrong owner");
+       succeed_if (test.getOwner() == "markus", "Wrong owner");
+       succeed_if (test.getNameSize() == 10, "wrong name size");
+       succeed_if (test.getFullNameSize() == 17, "wrong full name size");
+       succeed_if (!test.isSystem(), "key is system");
+       succeed_if ( test.isUser(), "key is not user");
+
+       test.setOwner("gerald");
+       succeed_if (std::string(test.name()) == "user/test", "Wrong name");
+       succeed_if (test.getName() == "user/test", "Wrong name");
+       succeed_if (test.getFullName() == "user:gerald/test", "Wrong full name");
+       succeed_if (test.getNameSize() == 10, "wrong name size");
+       succeed_if (test.getFullNameSize() == 17, "wrong full name size");
+       succeed_if (!test.isSystem(), "key is system");
+       succeed_if ( test.isUser(), "key is not user");
+
+       test.setName("system/test");
+       test.setOwner("markus"); // has no semantics...
+       succeed_if (std::string(test.name()) == "system/test", "Wrong name");
+       succeed_if (test.getName() == "system/test", "Wrong name");
+       succeed_if (test.getFullName() == "system/test", "Wrong full name");
+       succeed_if (std::string(test.owner()) == "markus", "Wrong owner");
+       succeed_if (test.getOwner() == "markus", "Wrong owner");
+       succeed_if (test.getNameSize() == 12, "wrong name size");
+       succeed_if (test.getFullNameSize() == 12, "wrong full name size");
+       succeed_if ( test.isSystem(), "key is system");
+       succeed_if (!test.isUser(), "key is not user");
+
+       test.setName("user/dir/test");
+       test.setBaseName ("mykey");
+       succeed_if (test.getName() == "user/dir/mykey", "Basename did not work");
+       test.setName (test.getName() + "/onedeeper"); // add basename is trivial
+       succeed_if (test.getName() == "user/dir/mykey/onedeeper", "Basename did not work");
+       succeed_if (test.getName().find('/') == 4, "user length"); // keyGetRootNameSize trivial
+       succeed_if (test.isBelow (Key("user/dir/mykey/onedeeper/below", KEY_END)), "key is below");
+       succeed_if (!test.isBelow (Key("user/dir/mykey/twodeeper/below", KEY_END)), "key is below");
+       succeed_if (test.isDirectBelow (Key("user/dir/mykey/onedeeper/below", KEY_END)), "key is direct below");
+       succeed_if (!test.isDirectBelow (Key("user/dir/mykey/onedeeper/twodeeper/below", KEY_END)), "key is direct below");
+       succeed_if (!test.isDirectBelow (Key("user/dir/mykey/twodeeper/below", KEY_END)), "key is direct below");
+}
+
+void test_meta()
+{
+       cout << "testing metainfo" << endl;
+       Key test;
+
+       test.setUID(50);
+       succeed_if (test.getUID() == 50, "could not set UID");
+
+       test.setGID(50);
+       succeed_if (test.getGID() == 50, "could not set GID");
+
+       succeed_if (test.getMode() == 0664, "not correct default mode");
+       test.setDir ();
+       succeed_if (test.isDir(), "is not dir");
+       succeed_if (test.getMode() == 0775, "not correct default mode for dir");
+
+       test.setMTime (200);
+       succeed_if (test.getMTime() == 200, "could not set MTime");
+
+       test.setATime (200);
+       succeed_if (test.getATime() == 200, "could not set ATime");
+
+       test.setCTime (200);
+       succeed_if (test.getCTime() == 200, "could not set CTime");
+}
+
+void f(Key k)
+{
+       Key h ("user/infunction", KEY_END);
+}
+
+void test_ref()
+{
+       cout << "testing ref" << endl;
+
+       Key zgr1 ("user/zgr1", KEY_END);
+       {
+               Key zgr2 ("user/zgr2", KEY_END);
+               Key zgr3 ("user/zgr3", KEY_END);
+               Key zgr4 ("user/zgr4", KEY_END);
+               Key zgr5 ("user/zgr5", KEY_END);
+               zgr2=zgr1;
+               zgr3=zgr1;
+               zgr4=zgr1;
+       }
+
+       f(zgr1);
+       f(Key ("user/passed", KEY_END));
+
+       Key test;
+       test.setName("user:markus/test");
+
+       Key ref1;
+       ref1 = test; // operator =
+
+       succeed_if (test.getName() == "user/test", "wrong name");
+       succeed_if (ref1.getName() == "user/test", "ref key wrong name");
+
+       Key ref2 = test; // copy constructor
+
+       succeed_if (test.getName() == "user/test", "wrong name");
+       succeed_if (ref2.getName() == "user/test", "ref key wrong name");
+
+       const Key consttest ("user/test", KEY_END);
+       Key ref3 = consttest; // const copy constructor
+
+       succeed_if (consttest.getName() == "user/test", "wrong name");
+       succeed_if (ref3.getName() == "user/test", "ref key wrong name");
+}
+
+void test_dup()
+{
+       cout << "testing dup" << endl;
+
+       Key test;
+       test.setName("user:markus/test");
+
+       Key dup0 = test.dup(); // directly call of dup()
+
+       succeed_if (test.getName() == "user/test", "wrong name");
+       succeed_if (dup0.getName() == "user/test", "dup key wrong name");
+}
+
+
+int main()
+{
+       cout << "KEY CLASS TESTS" << endl;
+       cout << "===============" << endl << endl;
+
+       /*
+       test_constructor();
+       test_setkey();
+       test_cast();
+       */
+
+       test_keynew();
+       test_name();
+       test_value();
+       test_exceptions();
+       test_meta();
+       test_dup();
+       test_ref();
+
+       cout << endl;
+       cout << "test_key RESULTS: " << nbTest << " test(s) done. " << nbError << " error(s)." << endl;
+       return nbError;
+}
diff --git a/src/bindings/cpp/tests/test_ks.cpp b/src/bindings/cpp/tests/test_ks.cpp
new file mode 100644 (file)
index 0000000..c51286a
--- /dev/null
@@ -0,0 +1,277 @@
+#include <tests.h>
+
+void test_ksnew()
+{
+       cout << "testing keyset new" << endl;
+
+       KeySet ks1;
+
+       KeySet ks2 (5,
+               ckdb::keyNew ("user/key2", KEY_END),
+               KS_END);
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+       // ks3.toStream(stdout, 0);
+
+       Key k1("user/key4/1", KEY_END);
+       Key k2("user/key4/2", KEY_REMOVE, KEY_END);
+       Key k3("user/key4/3", KEY_VALUE, "value", KEY_END);
+       KeySet ks4 (5,
+               *k1, // k1 will lose its key and pass it to keyset
+               *k2,
+               *k3,
+               KS_END);
+       // ks4.toStream(stdout, 0);
+
+       Key k4("user/key5/1", KEY_END);
+       Key k5("user/key5/2", KEY_REMOVE, KEY_END);
+       Key k6("user/key5/3", KEY_VALUE, "value", KEY_END);
+       KeySet ks5 (5,
+               k4.dup(),
+               k5.dup(),
+               k6.dup(),
+               KS_END);
+       // ks5.toStream(stdout, 0);
+       // k4, k5, k6 can still be used
+}
+
+void test_ksdup()
+{
+       cout << "testing ksdup" << endl;
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       KeySet ks4 (ks3.dup());
+       succeed_if (ks3.size() == 3, "size not correct");
+       succeed_if (ks4.size() == 3, "size not correct");
+
+       // ks3.toStream(stdout, 0);
+       // ks4.toStream(stdout, 0);
+}
+
+void test_kscopy()
+{
+       cout << "testing ksdup" << endl;
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       KeySet ks4 (ks3);
+       succeed_if (ks3.size() == 3, "size not correct");
+       succeed_if (ks4.size() == 3, "size not correct");
+
+       KeySet ks5;
+       ks5.copy(ks4);
+       succeed_if (ks4.size() == 3, "size not correct");
+       succeed_if (ks5.size() == 3, "size not correct");
+
+       ks5.clear();
+       succeed_if (ks5.size() == 0, "size not correct");
+
+
+
+       // ks3.toStream(stdout, 0);
+       // ks4.toStream(stdout, 0);
+}
+
+void test_iterate()
+{
+       cout << "testing iterate" << endl;
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       ks3.rewind();
+
+       Key k1 = ks3.next();
+       succeed_if (k1.getName() == "user/key3/1", "wrong keyname");
+       succeed_if (k1 == ks3.head(), "first key not head key");
+       Key k2 = ks3.next();
+       succeed_if (k2.getName() == "user/key3/2", "wrong keyname");
+       succeed_if (k2.needRemove(), "remove not set");
+       Key k3 = ks3.next();
+       succeed_if (k3.getName() == "user/key3/3", "wrong keyname");
+       succeed_if (k3.getString() == "value", "wrong value");
+       succeed_if (k3 == ks3.tail(), "last key not tail key");
+       try {
+               ks3.next();
+               succeed_if (false, "Out of Range not thrown");
+       } catch (KeySetOutOfRange) { }
+
+       ks3.rewind();
+       for (size_t i=0; i<ks3.size(); i++)
+       {
+               Key k = ks3.next();
+               char str[] = "user/key3/X";
+
+               str [10] = i+'1';
+               succeed_if (k.getName() == str, "wrong keyname");
+       }
+}
+
+void test_cursor()
+{
+       cout << "testing cursor" << endl;
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+       cursor_t cursorTest;
+
+       ks3.rewind();
+       for (size_t i=0; i<ks3.size(); i++)
+       {
+               Key k = ks3.next();
+               if (i==0) cursorTest = ks3.getCursor();
+       }
+
+       ks3.setCursor (cursorTest);
+       Key k1 = ks3.current();
+       succeed_if (k1.getName() == "user/key3/1", "wrong keyname");
+       succeed_if (k1 == ks3.head(), "first key not head key");
+}
+
+void test_pop()
+{
+       cout << "testing iterate" << endl;
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       ks3.rewind();
+
+       Key k3 = ks3.pop();
+       succeed_if (k3.getName() == "user/key3/3", "wrong keyname");
+       succeed_if (k3.getString() == "value", "wrong value");
+       Key k2 = ks3.pop();
+       succeed_if (k2.getName() == "user/key3/2", "wrong keyname");
+       succeed_if (k2.needRemove(), "remove not set");
+       Key k1 = ks3.pop();
+       succeed_if (k1.getName() == "user/key3/1", "wrong keyname");
+       try {
+               ks3.pop();
+               succeed_if (false, "Out of Range not catched");
+       } catch (KeySetOutOfRange) { }
+
+       KeySet ks4 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       ks4.rewind();
+       for (int i=ks4.size()-1; i>0; i--)
+       {
+               Key k = ks4.pop();
+               char str[] = "user/key3/X";
+
+               str [10] = i+'1';
+               succeed_if (k.getName() == str, str);
+       }
+}
+
+
+void test_lookup()
+{
+       cout << "testing lookup" << endl;
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       Key k1 = ks3.lookup("user/key3/1");
+       succeed_if (k1.getName() == "user/key3/1", "wrong keyname");
+
+       Key k3 = ks3.lookup("user/key3/3");
+       succeed_if (k3.getName() == "user/key3/3", "wrong keyname");
+       succeed_if (k3.getString() == "value", "wrong value");
+
+       try {
+               ks3.lookup("user/key3/2");
+               succeed_if (false, "Not Found not thrown for removed key");
+       } catch (KeySetNotFound) { }
+
+       try {
+               ks3.lookup("user/key3/4");
+               succeed_if (false, "Not Found not thrown for not existing key");
+       } catch (KeySetNotFound) { }
+}
+
+void test_append()
+{
+       cout << "testing keyset append" << endl;
+
+       KeySet ks1;
+
+       KeySet ks2 (5,
+               ckdb::keyNew ("user/key2", KEY_END),
+               KS_END);
+       ks1.append (ks2);
+
+       KeySet ks3 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+       ks2.append (ks3);
+       ks1.append (ks3);
+       ks3.append (ks2);
+
+       Key k1("user/key4/1", KEY_END);
+       Key k2("user/key4/2", KEY_REMOVE, KEY_END);
+       Key k3("user/key4/3", KEY_VALUE, "value", KEY_END);
+       ks1.append (k1); ks1.append (k2); ks1.append (k3);
+       ks2.append (k1); ks2.append (k2); ks2.append (k3);
+       ks3.append (k1); ks3.append (k2); ks3.append (k3);
+
+       KeySet ks4 (5,
+               *Key ("user/key3/1", KEY_END),
+               *Key ("user/key3/2", KEY_REMOVE, KEY_END),
+               *Key ("user/key3/3", KEY_VALUE, "value", KEY_END),
+               KS_END);
+
+       // ks1.toStream();
+       // ks2.toStream();
+       // ks3.toStream();
+}
+
+
+int main()
+{
+       cout << "KEYSET CLASS TESTS" << endl;
+       cout << "==================" << endl << endl;
+
+       test_ksnew();
+       test_ksdup();
+       test_kscopy();
+       test_iterate();
+       test_cursor();
+       test_pop();
+       test_lookup();
+       test_append();
+
+       cout << endl;
+       cout << "test_key RESULTS: " << nbTest << " test(s) done. " << nbError << " error(s)." << endl;
+       return nbError;
+}
diff --git a/src/bindings/cpp/tests/tests.cpp b/src/bindings/cpp/tests/tests.cpp
new file mode 100644 (file)
index 0000000..2782165
--- /dev/null
@@ -0,0 +1,4 @@
+#include <tests.h>
+
+int nbError;
+int nbTest;
diff --git a/src/bindings/cpp/tests/tests.h b/src/bindings/cpp/tests/tests.h
new file mode 100644 (file)
index 0000000..b30a69d
--- /dev/null
@@ -0,0 +1,26 @@
+/**Some common functions in use for testing framework*/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <kdb>
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <cstring>
+
+using namespace std;
+using namespace kdb;
+
+extern int nbError;
+extern int nbTest;
+
+#define warn_if_fail(x,y) {nbTest++; if (!(x)) { printf("%s:%d: warn in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y); }}
+#define succeed_if(x,y) nbTest++; if (!(x)) { nbError++; printf("%s:%d: error in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y);}
+#define exit_if_fail(x,y) nbTest++; if (!(x)) { printf("%s:%d: fatal in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y); exit(1); }
+
diff --git a/src/bindings/python/.deps/libelektra_la-libpyelektra.Plo b/src/bindings/python/.deps/libelektra_la-libpyelektra.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/bindings/python/ChangeLog b/src/bindings/python/ChangeLog
new file mode 100644 (file)
index 0000000..1acbb6e
--- /dev/null
@@ -0,0 +1,86 @@
+Mon Mar 26 19:30:32 CEST 2007  patrick@sabin.at
+  * Added a test file
+
+Mon Mar 26 17:48:46 CEST 2007  patrick@sabin.at
+  * fixed Typos
+
+Mon Mar 26 17:02:01 CEST 2007  patrick@sabin.at
+  * Added kdbRemove and examples
+
+Mon Mar 26 16:21:09 CEST 2007  patrick@sabin.at
+  * moved examples to new directories
+
+Mon Mar 26 16:09:57 CEST 2007  patrick@sabin.at
+  * Fixed bugs to make getset/el_getset work. ksNew has now name as parameter
+
+Mon Mar 26 13:40:53 CEST 2007  patrick@sabin.at
+  * Adapted libelektra to new version of elektra
+
+Mon Mar 26 08:51:10 CEST 2007  patrick@sabin.at
+  * Added get/set example program
+
+Tue Mar 13 12:10:13 CET 2007  patrick@sabin.at
+  * fixed bug in el_hello.py
+
+Tue Mar 13 11:27:52 CET 2007  patrick@sabin.at
+  * Added example hello World for elektra
+
+Tue Mar 13 11:26:48 CET 2007  patrick@sabin.at
+  * Added all essential methods to elektra
+
+Mon Mar 12 18:38:49 CET 2007  patrick@sabin.at
+  * Added help messages for all key methods
+
+Mon Mar 12 17:51:59 CET 2007  patrick@sabin.at
+  * Added all help messages for all kdb and ks functions in libelektra
+
+Mon Mar 12 16:26:52 CET 2007  patrick@sabin.at
+  * Added some help messages for kdb core methods in libelektra.c
+
+Mon Mar 12 16:03:07 CET 2007  patrick@sabin.at
+  * Added core methods to class Key and class KeySet in elektra.py
+
+Mon Mar 12 15:04:23 CET 2007  patrick@sabin.at
+  * added help some messages
+
+Mon Dec 18 22:06:47 CET 2006  patrick@sabin.at
+  * Implemented elektra library on the top of libelektra; Only class Kdb
+
+Mon Dec 18 14:48:10 CET 2006  patrick@sabin.at
+  * Implemented bindings for all keySet methods
+
+Sun Dec 17 21:38:09 CET 2006  patrick@sabin.at
+  * Implemented bindings for all key methods
+
+Sat Dec 16 19:21:35 CET 2006  patrick@sabin.at
+  * Implemented bindings for Error handling methods
+
+Sat Dec 16 19:12:39 CET 2006  patrick@sabin.at
+  * Implemented bindings for all-kdb functions
+
+Fri Dec 15 21:54:44 CET 2006  patrick@sabin.at
+  * cleaned up code using splint
+
+Fri Dec 15 21:04:51 CET 2006  patrick@sabin.at
+  * Compiles now with the update Version of the API
+
+Fri Dec 15 20:04:24 CET 2006  patrick@sabin.at
+  * Implemented bindings for functions for implementing the application.c example in python; There is a segfault, which I believe correspondens to the elektra library
+
+Fri Dec 15 13:11:38 CET 2006  patrick@sabin.at
+  * Implemented bindings for functions for implementing the application.c example in python
+
+Thu Dec 14 21:26:08 CET 2006  patrick@sabin.at
+  * Implemented bindings for functions for implementing the cascading.c example in python
+
+Thu Dec 14 20:53:53 CET 2006  patrick@sabin.at
+  * Implemented bindings for functions for implementing the binary.c example in python
+
+Thu Dec 14 19:24:37 CET 2006  patrick@sabin.at
+  * Implemented bindings for functions for implementing the hello.c example in python
+
+Thu Dec 14 15:47:30 CET 2006  patrick@sabin.at
+  * Added a example extension module
+
+Mon Dec 11 21:16:33 CET 2006  patrick@sabin.at
+  * First try
diff --git a/src/bindings/python/Makefile.am b/src/bindings/python/Makefile.am
new file mode 100644 (file)
index 0000000..c8dae1f
--- /dev/null
@@ -0,0 +1,8 @@
+PYVER = 2.4
+
+lib_LTLIBRARIES = libelektra.la
+libelektra_la_SOURCES = libpyelektra.c
+libelektra_la_LIBADD = ../../libelektra/libelektra.la -lpython$(PYVER)
+libelektra_la_CFLAGS = -I../../include -I/usr/include/python$(PYVER)
+
+EXTRA_DIST = examples application.py binary.py cascading.py elektra.py test.py
diff --git a/src/bindings/python/application.py b/src/bindings/python/application.py
new file mode 100644 (file)
index 0000000..2f3222b
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+from libelektra import *
+import sys
+
+MY_APP_ROOT="system/sw/MyApp/current"
+
+def readConfig(handle,myConfig):
+       return kdbGetChildKeys(handle,MY_APP_ROOT,myConfig,0)
+
+def changeConfig(myConfig):
+       ksRewind(myConfig)
+       current=1
+       while True:
+               current=ksNext(myConfig)
+               if current==0: break
+               keyName=keyGetFullName(current)
+               value=keyGetString(current)
+               value=value+"- modified"
+               print 'Key %s was %s'%(keyName,value)
+               keySetString(current, value)
+               value=keyGetString(current)
+
+def saveConfig(handle,myConfig):
+       return kdbSetKeys(handle,myConfig)
+
+myConfig=ksNew()
+(handle,r)=kdbOpen()
+
+if readConfig(handle,myConfig)==-1: 
+       sys.stderr.write("Couldn't get my configuration. Reason\n")
+       sys.exit(1)
+else:
+       print "retrieved %d keys"%ksGetSize(myConfig)
+
+changeConfig(myConfig)
+saveConfig(handle,myConfig)
+kdbClose(handle)
+ksDel(myConfig)
diff --git a/src/bindings/python/binary.py b/src/bindings/python/binary.py
new file mode 100644 (file)
index 0000000..8357b20
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+from libelektra import *
+
+key=keyNew()
+keySetName(key,"user/tmp/bin")
+(h,r)=kdbOpen()
+keySetBinary(key,"\000ABC")
+kdbSetKey(h,key)
+kdbClose(h)
diff --git a/src/bindings/python/cascading.py b/src/bindings/python/cascading.py
new file mode 100644 (file)
index 0000000..6f801ce
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+# try: 
+# > kdb set user/myapp "hello world!"
+# > kdb set system/myapp "hi world!"
+# > python cascading.py
+
+from libelektra import *
+import sys
+
+def BailOut(msg):
+       print 'msg'
+       print kdbStrError(kdbhGetError(0))
+       sys.exit(1)
+
+ks=ksNew()
+
+(h,r)=kdbOpen()
+if r==-1:
+       BailOut('Could not open key database')
+
+if kdbGetChildKeys(h,"user/myapp",ks,0)==-1:
+       BailOut('Could not get keys')
+
+if kdbGetChildKeys(h,"system/myapp",ks,0)==-1:
+       BailOut('Could not get keys')
+
+k=ksLookupByName(ks,"/myapp/key",0);
+if k==0:
+       BailOut('Could not lookup keys')
+
+print keyStealValue(k)
+ksDel(ks)
+kdbClose(h)
diff --git a/src/bindings/python/elektra.py b/src/bindings/python/elektra.py
new file mode 100644 (file)
index 0000000..56ef6ec
--- /dev/null
@@ -0,0 +1,350 @@
+#!/usr/bin/python
+
+import libpyelektra
+
+class ElektraException(Exception):
+       def __init__(self,msg):
+               self.msg=msg
+       def __str__(self):
+               return self.msg
+
+class Kdb:
+       """General methods to acces the key database
+"""
+       def __init__(self):
+               self.__kdb__pointer__=libpyelektra.kdbOpen()
+       def __del__(self):
+               libpyelektra.kdbClose(self.__kdb__pointer__)
+
+       #def close(self):
+       #       """Closes the backend."""
+       #       libpyelektra.kdbClose(self.__kdb__pointer__)
+
+       def getKey(self,key):
+               """Gets key from backend storage."""
+               return libpyelektra.kdbGetKey(self.__kdb__pointer__,key.__key__pointer__)
+       def getKeyChildKeys(self, key, *args, **kargs):
+               options=0
+               if 'norecursive' in kargs and kargs['norecursive']: options+=1
+               if 'excldir' in kargs and kargs['excldir']: options+=2
+               if 'dironly' in kargs and kargs['dironly']: options+=4
+               if 'noempty' in kargs and kargs['noempty']: options+=8
+               if 'statonly' in kargs and kargs['statonly']: options+=16
+               if 'inactive' in kargs and kargs['inactive']: options+=32
+               if 'unsort' in kargs and kargs['unsort']: options+=64
+               if 'nfollowlink' in kargs and kargs['nfollowlink']: options+=128
+               #if kargs['condensed']: options+=256
+               #if kargs['numbers']: options+=512
+               #if kargs['xmlheaders']: options+=1024
+               #if kargs['fullname']: options+=2048
+               #if kargs['fullugid']: options+=4096
+               #if kargs['hier']: options+=8192
+               #if kargs['nocase']: options+=16384
+               #if kargs['nospanparent']: options+=32768
+               #if kargs['all']: options+=131072
+               keySet=KeySet(libpyelektra.ksNew())
+               libpyelektra.kdbGetKeyChildKeys(self.__kdb__pointer__,key,keySet.__keyset__pointer__,options)
+               return keySet
+       def getChildKeys(self, name, *args, **kargs):
+               options=0
+               if 'norecursive' in kargs and kargs['norecursive']: options+=1
+               if 'excldir' in kargs and kargs['excldir']: options+=2
+               if 'dironly' in kargs and kargs['dironly']: options+=4
+               if 'noempty' in kargs and kargs['noempty']: options+=8
+               if 'statonly' in kargs and kargs['statonly']: options+=16
+               if 'inactive' in kargs and kargs['inactive']: options+=32
+               if 'unsort' in kargs and kargs['unsort']: options+=64
+               if 'nfollowlink' in kargs and kargs['nfollowlink']: options+=128
+               #if kargs['condensed']: options+=256
+               #if kargs['numbers']: options+=512
+               #if kargs['xmlheaders']: options+=1024
+               #if kargs['fullname']: options+=2048
+               #if kargs['fullugid']: options+=4096
+               #if kargs['hier']: options+=8192
+               #if kargs['nocase']: options+=16384
+               #if kargs['nospanparent']: options+=32768
+               #if kargs['all']: options+=131072
+               keySet=KeySet(libpyelektra.ksNew())
+               libpyelektra.kdbGetChildKeys(self.__kdb__pointer__,name,keySet.__keyset__pointer__,options)
+               return keySet
+       def setKey(self,key):
+               """Sets key in the backend storage."""
+               return libpyelektra.kdbSetKey(self.__kdb__pointer__,key.__key__pointer__)
+       def setKeys(self,keyset):
+               """Commits the ks KeySet to the backend storage, starting from ks's current position until its end.
+This is why it is suggested that you call rewind() on ks before calling this method.
+Each key is checked with keyNeedSync() before being actually commited. So only changed
+keys are updated.  If some error occurs, SetKeys() will stop. In this situation the KeySet 
+internal cursor is left on the key that generated the error.
+"""
+               return libpyelektra.setKey(self.__kdb__pointer__,keyset.__keyset__pointer__)
+       def getRootKeys(self):
+               """Returns a KeySet with all root keys currently recognized and present on the system.
+Currently, the system and current user's user keys are returned.
+This is the only valid way to get to know of the root keys.
+"""
+               keyset=ksNew()
+               libpyelektra.getRootKeys(self.__keyset_pointer__,self.__keyset__pointer__)
+               return keyset
+       def error(self,no):
+               """Provides an error string associated with errnum"""
+               return libpyelektra.kdbStrError(no)
+       def errno(self):
+               """return the errno of the last error"""
+               return libpyelektra.kdbGetErrno(self.__kdb__pointer__)
+       def remove(self,name):
+               """remove the key with the name <name>"""
+               return libpyelektra.kdbRemove(self.__kdb__pointer__,name)
+
+class Key:
+       """Is a representation of a key, that can be modified by the following methods"""
+       def __init__(self,*args,**kargs):
+               #if len(args)==1:
+               #       self.__key__pointer__=args[0]
+               if kargs.has_key('name'):
+                       self.__key__pointer__=libpyelektra.keyNew(kargs['name'])
+                       del kargs['name']
+               else:
+                       self.__key__pointer__=libpyelektra.keyNew('')
+               self.set(**kargs)
+               if kargs.has_key('value'):
+                       libpyelektra.keySetString(self.__key__pointer__,kargs['value'])
+               if kargs.has_key('lookup'):
+                       try:
+                               libpyelektra.kdbGetKey(kargs['lookup'].__kdb__pointer__,self.__key__pointer__)
+                       except AttributeError:
+                               raise ElektraException('lookup needs a initialised kdb object')
+       #def new(self,name):
+       #       """creates new key"""
+       #       self.__key__pointer__=libpyelektra.keyNew(name)
+       def set(self, **kargs):
+               if kargs.has_key('name'):
+                       self.setName(kargs['name'])
+               if kargs.has_key('value'):
+                       libpyelektra.keySetString(self.__key__pointer__,kargs['value'])
+               if kargs.has_key('lookup'):
+                       try:
+                               libpyelektra.kdbGetKey(kargs['lookup'].__kdb__pointer__,self.__key__pointer__)
+                       except AttributeError:
+                               raise ElektraException('lookup needs a initialised kdb object')
+               if kargs.has_key('set'):
+                       try:
+                               libpyelektra.kdbSetKey(kargs['set'].__kdb__pointer__,self.__key__pointer__)
+                       except AttributeError:
+                               raise ElektraException('lookup needs a initialised kdb object')
+               return self
+
+       def setName(self,name,**kargs):
+               """give the key a new name"""
+               ret= libpyelektra.keySetName(self.__key__pointer__,name) 
+               if ret == -1:
+                       raise ElektraException('Keyname invalid')
+
+               if kargs.has_key('lookup'):
+                       try:
+                               libpyelektra.kdbGetKey(kargs['lookup'].__kdb__pointer__,self.__key__pointer__)
+                       except AttributeError:
+                               raise ElektraException('lookup needs a initialised kdb object')
+       def setBinary(self):
+               """Set the binary value of a key"""
+               libpyelektra.keySetBinary(self.__key__pointer__,value)
+       def getFullName(self):
+               """Set the full name of a key"""
+               return libpyelektra.keyGetFullName(self.__key__pointer__)
+       def getString(self):
+               """Get the string value of a key"""
+               return libpyelektra.keyGetString(self.__key__pointer__)
+       def setString(self,s):
+               """Set the full name of a key"""
+               return libpyelektra.keySetString(self.__key__pointer__,s)
+       def getNameSize(self):
+               """returns the name size"""
+               return libpyelektra.keyGetNameSize(self.__key__pointer__)
+       def getDataSize(self):
+               """returns the data size"""
+               return libpyelektra.keyGetDataSize(self.__key__pointer__)
+       def getCommentSize(self):
+               """returns the comment size"""
+               return libpyelektra.keyGetCommentSize(self.__key__pointer__)
+       def getComment(self):
+               """returns the comment"""
+               return libpyelektra.keyGetComment(self.__key__pointer__)
+       def getName(self):
+               """returns the key name"""
+               return libpyelektra.keyGetName(self.__key__pointer__)
+       def dup(self):
+               """returns the duplicate"""
+               return Key(libpyelektra.keyDup(self.__key__pointer__))
+       def getType(self):
+               """return type of the key"""
+               return libpyelektra.keyGetType(self.__key__pointer__)
+       def setType(self,type):
+               """set the key type"""
+               libpyelektra.keySetType(self.__key__pointer__,type)
+       def getRecordSize(self,type):
+               """get the record size of the key"""
+               libpyelektra.keyGetRecordSize(self.__key__pointer__)
+       def getFullNameSize(self):
+               """get the fullname size"""
+               return libpyelektra.keyGetFullNameSize(self.__key__pointer__)
+       def name(self):
+               """returns the name of the key"""
+               return libpyelektra.keyName(self.__key__pointer__)
+       def comment(self):
+               """returns the comment of the key"""
+               return libpyelektra.keyComment(self.__key__pointer__)
+       def setComment(self,s):
+               """set the key comment"""
+               libpyelektra.keySetComment(self.__key__pointer__,s)
+       def getUID(self):
+               """return the uid"""
+               return libpyelektra.keyGetUID(self.__key__pointer__)
+       def setUID(self,uid):
+               """set the uid of the key"""
+               return libpyelektra.keySetUID(self.__key__pointer__,uid)
+       def getGID(self):
+               """get the GID of the key"""
+               return libpyelektra.getGID(self.__key__pointer__)
+       def setGID(self,gid):
+               """set the GID of the key"""
+               return libpyelektra.setGID(self.__key__pointer__,gid)
+       def getMode(self):
+               """get the mode of the key"""
+               return libpyelektra.keyGetMode(self.__key__pointer__)
+       def setMode(self,a):
+               """set the mode of the key"""
+               return libpyelektra.keySetMode(self.__key__pointer__,a)
+       def getOwnerSize(self):
+               """returns the owner size"""
+               return libpyelektra.keyGetOwnerSize(self.__key__pointer__)
+       def owner(self):
+               """returns the owner"""
+               return libpyelektra.keyOwner(self.__key__pointer__)
+       def setOwner(self):
+               """sets a new owner"""
+               return libpyelektra.keySetOwner(self.__key__pointer__)
+       def getLink(self):
+               """returns the link"""
+               return libpyelektra.keyGetLink(self.__key__pointer__)
+       def setLink(self,value):
+               """sets the link"""
+               return libpyelektra.keySetLink(self.__key__pointer__)
+       def getMTime(self):
+               """returns the MTime"""
+               return libpyelektra.keyGetMTime(self.__key__pointer__)
+       def getATime(self):
+               """returns the ATime"""
+               return libpyelektra.keyGetATime(self.__key__pointer__)
+       def getCTime(self):
+               """returns the CTime"""
+               return libpyelektra.keyGetCTime(self.__key__pointer__)
+       def isSystem(self):
+               """check whether the key is under the system namespace or not"""
+               if libpyelektra.keyIsSystem(self.__key__pointer__)==0: return False
+               return True
+       def isUser(self):
+               """check whether the key is under the user namespace or not"""
+               if libpyelektra.keyIsUser(self.__key__pointer__)==0: return False
+               return True
+       def isDir(self):
+               """check if the key is a folder key or not"""
+               if libpyelektra.keyIsDir(self.__key__pointer__)==0: return False
+               return True
+       def isLink(self):
+               """check if the key is a Link key or not"""
+               if libpyelektra.keyIsLink(self.__key__pointer__)==0: return False
+               return True
+       def isBin(self):
+               """check if the key is a binary type"""
+               if libpyelektra.keyIsBin(self.__key__pointer__)==0: return False
+               return True
+       def isString(self):
+               """check if the key is a string type"""
+               if libpyelektra.keyIsBin(self.__key__pointer__)==0: return False
+               return True
+       #def close(self):
+       #       libpyelektra.keyClose(self.__key__pointer__)
+       def __del__(self):
+               libpyelektra.keyClose(self.__key__pointer__)
+
+class KeySet:
+       """Methods to manipulate KeySets. A KeySet is a linked list to group a number of Keys. 
+KeySets have an internal cursor  to help in the Key navigation."""
+       def __init__(self,*args):
+               if len(args)==1:
+                       self.__keyset__pointer__=args[0]
+       def new(self):
+               """new() creates a new keyset"""
+               self.__keyset__pointer__=libpyelektra.ksNew()
+       def del_(self):
+               """del_() deletes the keyset"""
+               libpyelektra.ksDel(self.__keyset__pointer__)
+       def rewind(self):
+               """resets the cursor"""
+               libpyelektra.ksRewind(self.__keyset__pointer__)
+       def next(self):
+               """returns the next keyset in the keyset"""
+               libpyelektra.ksNext(self.__keyset__pointer__)
+       def getSize(self):
+               """returns the number of elements in the keyset"""
+               libpyelektra.ksGetSize(self.__keyset__pointer__)
+       def insert(self,key):
+               """inserts a new Key in the beginning of the KeySet."""
+               return libpyelektra.ksInsert(self.__keyset__pointer__,key.__key__pointer__)
+       def append(self,key):
+               """appends a new Key at the end of the KeySet."""
+               return libpyelektra.ksInsert(self.__keyset__pointer__,key.__key__pointer__)
+       def pop(self):
+               """Remove and return the last key of ks"""
+               return libpyelektra.ksPop(self.__keyset__pointer__)
+       def insertKeys(self,toInsert):
+               """ks.insertKeys(toInsert) transfers all keys from toInsert to the begining of ks.
+After this call, toInsert will be empty and can be deleted with toInsert.del()
+Returns: the size of the KeySet after transfer."""
+               return libpyelektra.ksInsertKeys(self.__keyset__pointer__,toInsert.__keyset__pointer__)
+       def appendKeys(self,toAppend):
+               """ks.insertKeys(toAppend) transfers all keys from toInsert to the begining of ks.
+After this call, toInsert will be empty and can be deleted with toInsert.del()
+Returns: the size of the KeySet after transfer."""
+               return libpyelektra.ksInsertKeys(self.__keyset__pointer__,toAppend.__keyset__pointer__)
+       def sort(self):
+               """sort the keys in the keyset"""
+               libpyelektra.ksSort(self.__keyset__pointer__)
+       def lookupByString(self,value,*args,**kargs):
+               """Look for a Key contained in ks that matches name, starting from ks' ksNext() position.
+Options: nocase\tignore case"""
+               options=0
+               if 'nocase' in kargs and kargs['nocase']: options+=32768
+               t=libpyelektra.ksLookupByString(self.__keyset__pointer__,value,options)
+               if t==0: return None
+               return Key(t)
+
+       def lookupByName(self,value,*args,**kargs):
+               """Look for a Key contained in ks that matches name, starting from ks' ksNext() position.
+Options: nocase\tignore case"""
+               options=0
+               if 'nocase' in kargs and kargs['nocase']: options+=32768
+               if 'all' in kargs and kargs['all']: options+=131072
+               t=libpyelektra.ksLookupByName(self.__keyset__pointer__,value,options)
+               if t==0: return None
+               return Key(t)
+
+       def lookupByBinary(self,value,options):
+               """Look for a Key contained in ks that matches name, starting from ks' ksNext() position.
+Options: nocase\tignore case"""
+               options=0
+               if 'nocase' in kargs and kargs['nocase']: options+=32768
+               t=libpyelektra.ksLookupByBinary(self.__keyset__pointer__,value,options)
+               if t==0: return None
+               return Key(t)
+       def current(self):
+               """returns the current Key or None if you reached the end"""
+               t=libpyelektra.ksCurrent(self.__keyset__pointer__)
+               if t==0: return None
+               return Key(t)
+       def head(self):
+               """returns the first key in the KeySet, without changing the KeySet's internal cursor."""
+               return Key(libpyelektra.ksHead(self.__keyset__pointer__))
+       def tail(self):
+               """returns the last key in the KeySet, without changing the KeySet's internal cursor."""
+               return Key(libpyelektra.ksTail(self.__keyset__pointer__))
diff --git a/src/bindings/python/examples/elektra/getset.py b/src/bindings/python/examples/elektra/getset.py
new file mode 100644 (file)
index 0000000..4c41449
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+# try: 
+# > python hello.py
+
+import elektra
+import sys
+
+kdb=elektra.Kdb()
+kdb.open()
+
+k=elektra.Key()
+k.new("user/key")
+k.setName("/key")
+k.setString("hi")
+
+kdb.setKey(k)
+
+ks=kdb.getChildKeys("user",norecursive=True)
+
+key=ks.lookupByName("/key")
+print key.getString()
+
+ks.del_()
+kdb.close()
diff --git a/src/bindings/python/examples/elektra/hello.py b/src/bindings/python/examples/elektra/hello.py
new file mode 100644 (file)
index 0000000..407c4cd
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+# try: 
+# > kdb set user/hello "Hello World!"
+# > python hello.py
+
+import elektra
+import sys
+
+#ks=elektra.KeySet()
+
+kdb=elektra.Kdb()
+kdb.open()
+ks=kdb.getChildKeys("user",norecursive=True)
+
+key=ks.lookupByName("/hello",0)
+print key.getString()
+
+ks.del_()
+kdb.close()
diff --git a/src/bindings/python/examples/elektra/remove.py b/src/bindings/python/examples/elektra/remove.py
new file mode 100644 (file)
index 0000000..a2be667
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+# try: 
+# > kdb set user/key "to remove"
+# > python remove.py
+# > kdb get user/key "to remove"
+
+from elektra import *
+import sys
+
+kdb=Kdb()
+kdb.open()
+kdb.remove("user/key")
+
+kdb.close()
diff --git a/src/bindings/python/examples/libelektra/getset.py b/src/bindings/python/examples/libelektra/getset.py
new file mode 100644 (file)
index 0000000..4f70f43
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+# try: 
+# > python getset.py
+
+from libelektra import *
+import sys
+
+ks=ksNew()
+h=kdbOpen()
+
+k=keyNew("user/key")
+keySetName(k,"key")
+keySetString(k,"hi")
+
+kdbSetKey(h,k)
+
+kdbGetChildKeys(h,"user",ks,1)
+
+k=ksLookupByName(ks,"/key",0)
+print keyGetString(k)
+
+kdbClose(h)
+keyClose(k)
diff --git a/src/bindings/python/examples/libelektra/hello.py b/src/bindings/python/examples/libelektra/hello.py
new file mode 100644 (file)
index 0000000..07dee18
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+# try: 
+# > kdb set user/hello "Hello World!"
+# > python hello.py
+
+from libelektra import *
+import sys
+
+def BailOut(msg):
+       print msg
+       print kdbStrError(kdbhGetError(0))
+       sys.exit(1)
+
+ks=ksNew()
+
+h=kdbOpen()
+if h==0:
+       BailOut('Could not open key database')
+
+if kdbGetChildKeys(h,"user",ks,1)==-1:
+       BailOut('Could not get keys')
+
+k=ksLookupByName(ks,"/hello",0);
+if k==0:
+       BailOut('Could not lookup keys')
+
+#print keyStealValue(k)
+print keyGetString(k)
+ksDel(ks)
+kdbClose(h)
diff --git a/src/bindings/python/examples/libelektra/remove.py b/src/bindings/python/examples/libelektra/remove.py
new file mode 100644 (file)
index 0000000..4c3d449
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+# try: 
+# > kdb set user/key "to remove"
+# > python remove.py
+# > kdb get user/key "to remove"
+
+from libelektra import *
+import sys
+
+h=kdbOpen()
+kdbRemove(h,"user/key")
+
+kdbClose(h)
diff --git a/src/bindings/python/libpyelektra.c b/src/bindings/python/libpyelektra.c
new file mode 100644 (file)
index 0000000..2540f4e
--- /dev/null
@@ -0,0 +1,1713 @@
+#include <Python.h>
+#include <string.h>
+#include <stdlib.h>
+#include <kdb.h>
+//#include <kdbprivate.h>
+
+static PyObject* py_kdbOpen (PyObject *self, PyObject *args)
+{
+       KDB *handle;
+       PyObject *LongPtr;
+       if (!PyArg_Parse(args, ""))
+               return NULL;
+       else {
+               handle=kdbOpen();
+               LongPtr=PyLong_FromVoidPtr(handle);
+               return LongPtr;
+       }
+
+}
+
+static PyObject* py_kdbGetChildKeys(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       PyObject *py_h;
+       char *s;
+       long opt;
+       unsigned long options;
+       ssize_t ret;
+       int r;
+       KDB *h;
+       KeySet *ks;
+
+       if (!PyArg_Parse(args,"(OsOl)",&py_h,&s,&py_ks,&opt))
+               return NULL;
+       else {
+               options=(unsigned long) opt;
+               h=PyLong_AsVoidPtr(py_h);
+               ks=PyLong_AsVoidPtr(py_ks);
+               
+               ret=kdbGetChildKeys(h,s,ks,options);
+               r=(int) ret;
+               return Py_BuildValue("i",r);
+       }
+}
+
+static PyObject* py_kdbGetKeyChildKeys(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       PyObject *py_k;
+       PyObject *py_h;
+       long opt;
+       unsigned long options;
+       ssize_t ret;
+       int r;
+       KDB *h;
+       KeySet *ks;
+       Key *k;
+
+       if (!PyArg_Parse(args,"(OOOl)",&py_h,&py_k,&py_ks,&opt))
+               return NULL;
+       else {
+               options=(unsigned long) opt;
+               h=PyLong_AsVoidPtr(py_h);
+               k=PyLong_AsVoidPtr(py_k);
+               ks=PyLong_AsVoidPtr(py_ks);
+               
+               ret=kdbGetKeyChildKeys(h,k,ks,options);
+               r=(int) ret;
+               return Py_BuildValue("i",r);
+       }
+}
+
+static PyObject* py_ksNew(PyObject *self, PyObject *args) {
+       KeySet *ks;
+       PyObject* LongPtr;
+       if (!PyArg_Parse(args, ""))
+               return NULL;
+       else {
+               ks=ksNew();
+               LongPtr=PyLong_FromVoidPtr(ks);
+               return LongPtr;
+       }
+}
+
+static PyObject* py_ksLookupByName(PyObject *self, PyObject *args) {
+       KeySet *ks;
+       PyObject *py_ks, *LongPtr;
+       char *s;
+       long l;
+       unsigned long ul;
+       Key *ret;
+       if (!PyArg_Parse(args, "(Osi)",&py_ks,&s,&l))
+               return NULL;
+       else {
+               ul=(unsigned long) l;
+               ks=PyLong_AsVoidPtr(py_ks);
+               ret=ksLookupByName(ks,s,ul);
+               LongPtr=PyLong_FromVoidPtr(ret);
+               //return Py_BuildValue("O", LongPtr);
+               return LongPtr;
+       }
+}
+
+/*
+static PyObject* py_keyStealValue(PyObject *self, PyObject *args) {
+       Key *k;
+       PyObject *py_k;
+       char *s;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else {
+               k=PyLong_AsVoidPtr(py_k);
+               s=keyStealValue(k);
+               return Py_BuildValue("s", s);
+       }
+}
+*/
+
+static PyObject* py_kdbClose(PyObject *self, PyObject *args)
+{
+       PyObject *py_h;
+       KDB *h;
+       if (!PyArg_Parse(args, "(O)",&py_h))
+               return NULL;
+       else {
+               h=PyLong_AsVoidPtr(py_h);
+               return Py_BuildValue("i", kdbClose(h));
+       }
+}
+
+static PyObject* py_ksDel(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else {
+               ks=PyLong_AsVoidPtr(py_ks);
+               return Py_BuildValue("i", ksDel(ks));
+       }
+}
+
+/* keyNew is only implement without parameters. Options can be set with
+ * the correspondending functions */
+static PyObject* py_keyNew(PyObject *self, PyObject *args)
+{
+       PyObject *LongPtr;
+       Key *k;
+       char *s;
+       if (!PyArg_Parse(args, "(s)",&s))
+               return NULL;
+       else  {
+               k=keyNew(s,KEY_SWITCH_END);
+               LongPtr=PyLong_FromVoidPtr(k);
+               //return Py_BuildValue("O",LongPtr);
+               return LongPtr;
+       }
+}
+
+static PyObject* py_keySetName(PyObject *self, PyObject *args)
+{
+       PyObject *py_k;
+       Key *k;
+       char *s;
+       ssize_t ret;
+       int r;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret=keySetName(k,s);
+               r=(int) ret;
+               return Py_BuildValue("i",r);
+       }
+}
+
+static PyObject* py_keySetBinary(PyObject *self, PyObject *args)
+{
+       PyObject *py_k;
+       Key *k;
+       char *s;
+       ssize_t ret;
+       int r;
+       int i;
+       if (!PyArg_Parse(args, "(Os#)",&py_k,&s,&i))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret=keySetBinary(k,s,(size_t)i);
+               r=(int) ret;
+               return Py_BuildValue("i",r);
+       }
+}
+
+static PyObject* py_kdbSetKey(PyObject *self, PyObject *args)
+{
+       PyObject *py_k;
+       PyObject *py_h;
+       Key *k;
+       KDB *h;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_h,&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               h=PyLong_AsVoidPtr(py_h);
+               ret=kdbSetKey(h,k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksRewind(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               ret=ksRewind(ks);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksNext(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       Key *k;
+       PyObject *py_k;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k=ksNext(ks);
+               py_k=PyLong_FromVoidPtr(k);
+               //return Py_BuildValue("O",py_k);
+               return py_k;
+       }
+}
+
+static PyObject* py_ksGetSize(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               ret=(int)ksGetSize(ks);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetFullName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetFullName(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+       
+}
+
+static PyObject* py_keyGetString(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetString(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+       
+}
+
+static PyObject* py_keySetString(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char *s;
+       int ret;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret=(int)keySetString(k,s);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_kdbSetKeys(PyObject *self, PyObject *args)
+{
+       KeySet *ks;
+       KDB *h;
+       PyObject *py_ks;
+       PyObject *py_h;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_h,&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               h=PyLong_AsVoidPtr(py_h);
+               ret=kdbSetKeys(h,ks);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetNameSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret=(int)keyGetNameSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+/*
+static PyObject* py_keyGetDataSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret=(int)keyGetDataSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}*/
+
+static PyObject* py_keyGetCommentSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       ssize_t ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret=keyGetCommentSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetName(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+       
+}
+
+static PyObject* py_keyGetComment(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       result[0]='\0';
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetComment(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+       
+}
+
+static PyObject* py_kdbGetValue(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       char result[1024];
+       char *s;
+       if (!PyArg_Parse(args, "(Os)",&py_h,&s))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               (void) kdbGetValue(h,s,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+}
+
+static PyObject* py_kdbGetKeyByParent(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       char *s1;
+       char *s2;
+       int ret;
+       if (!PyArg_Parse(args, "(OssO)",&py_h,&s1,&s2,&py_k))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               k=PyLong_AsVoidPtr(py_k);
+               ret = kdbGetKeyByParent(h,s1,s2,k); 
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_kdbGetKeyByParentKey(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       Key *k1;
+       PyObject *py_k1;
+       Key *k2;
+       PyObject *py_k2;
+       char *s;
+       int ret;
+       if (!PyArg_Parse(args, "(OssO)",&py_h,&py_k1,&s,&py_k2))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               k1=PyLong_AsVoidPtr(py_k1);
+               k2=PyLong_AsVoidPtr(py_k2);
+               ret = kdbGetKeyByParentKey(h,k1,s,k2);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_kdbGetValueByParent(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       char result[1024];
+       char *s1;
+       char *s2;
+       if (!PyArg_Parse(args, "(Oss)",&py_h,&s1,&s2))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               (void) kdbGetValueByParent(h,s1,s2,result,1023);  // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+}
+
+static PyObject* py_kdbSetValue(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       char *s1;
+       char *s2;
+       int ret;
+       if (!PyArg_Parse(args, "(Oss)",&py_h,&s1,&s2))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               ret = kdbSetValue(h,s1,s2);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_kdbSetValueByParent(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       char *s1;
+       char *s2;
+       char *s3;
+       int ret;
+       if (!PyArg_Parse(args, "(Osss)",&py_h,&s1,&s2,&s3))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               ret = kdbSetValueByParent(h,s1,s2,s3);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_kdbGetKey(PyObject *self, PyObject *args)
+{
+       KDB *h;
+       PyObject *py_h;
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_h,&py_k))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               k=PyLong_AsVoidPtr(py_k);
+               ret = kdbGetKey(h,k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_kdbStrErr(PyObject *self, PyObject *args)
+{
+       int no;
+       char *s;
+       if (!PyArg_Parse(args, "(i)",&no))
+               return NULL;
+       else  {
+               s = kdbStrError(no);
+               return Py_BuildValue("s",s);
+       }
+}
+
+static PyObject* py_kdbGetErrno(PyObject *self, PyObject *args)
+{
+       int no;
+       KDB *h;
+       PyObject *py_h;
+       if (!PyArg_Parse(args, "(O)",&py_h))
+               return NULL;
+       else  {
+               h=PyLong_AsVoidPtr(py_h);
+               no = kdbGetErrno();
+               return Py_BuildValue("i",no);
+       }
+}
+
+static PyObject* py_keyInit(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyInit(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyClose(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyClose(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+/*
+static PyObject* py_keyIsInitialized(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsInitialized(k);
+               return Py_BuildValue("i",ret);
+       }
+}*/
+
+static PyObject* py_keyNeedSync(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyNeedSync(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyDup(PyObject *self, PyObject *args)
+{
+       Key *k1;
+       Key *k2;
+       PyObject *py_k1;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k1))
+               return NULL;
+       else  {
+               k1=PyLong_AsVoidPtr(py_k1);
+               k2 = keyDup(k1);
+               return PyLong_FromVoidPtr(k2);
+       }
+}
+
+static PyObject* py_keyGetType(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetType(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keySetType(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int type;
+       int ret;
+       if (!PyArg_Parse(args, "(Oi)",&py_k,&type))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetType(k,type);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+/* TODO
+static PyObject* py_keySetDir(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyNeedSync(k);
+               return Py_BuildValue("i",ret);
+       }
+}*/
+
+/*
+static PyObject* py_keySetFlag(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetFlag(k);
+               return Py_BuildValue("i",ret);
+       }
+}*/
+
+/*
+static PyObject* py_keyClearFlag(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyClearFlag(k);
+               return Py_BuildValue("i",ret);
+       }
+}*/
+
+/*
+static PyObject* py_keyGetFlag(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetFlag(k);
+               return Py_BuildValue("i",ret);
+       }
+}*/
+
+static PyObject* py_keyGetRecordSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetRecordSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetFullNameSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetFullNameSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       const char *ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyName(k);
+               return Py_BuildValue("s",ret);
+       }
+}
+
+static PyObject* py_keyGetRootName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetRootName(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+       
+}
+
+static PyObject* py_keyGetFullRootName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetFullRootName(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+       
+}
+
+static PyObject* py_keyBaseName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       const char *s;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               s = keyBaseName(k);
+               return Py_BuildValue("s",s);
+       }
+}
+
+static PyObject* py_keyNameGetBaseNameSize(PyObject *self, PyObject *args)
+{
+       char *s;
+       int size;
+       if (!PyArg_Parse(args, "(s)",&s))
+               return NULL;
+       else  {
+               size = keyNameGetBaseNameSize(s);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keyGetBaseNameSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int size;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keyGetBaseNameSize(k);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keyAddBaseName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char *s;
+       int size;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keyAddBaseName(k,s);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keySetBaseName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char *s;
+       int size;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keySetBaseName(k,s);
+               return Py_BuildValue("i",size);
+       }
+}
+
+/* TODO
+static PyObject* py_keyNameGetOneLevel(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char *s;
+       char *name;
+       int size;
+       if (!PyArg_Parse(args, "(si)",&name,&size))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               s = keyNameGetOneLevel(name,&size);
+               return Py_BuildValue("s",s);
+       }
+} */
+/*
+static PyObject* py_keyGetParentName(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyNameGetParentName(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+} 
+
+static PyObject* py_keyGetParentNameSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int size;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keyGetParentNameSize(k);
+               return Py_BuildValue("i",size);
+       }
+} */
+
+static PyObject* py_keyNameGetRootNameSize(PyObject *self, PyObject *args)
+{
+       char *s;
+       int size;
+       if (!PyArg_Parse(args, "(s)",&s))
+               return NULL;
+       else  {
+               size = keyNameGetRootNameSize(s);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keyGetRootNameSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int size;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keyGetRootNameSize(k);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keyComment(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       const char *s;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               s = keyComment(k);
+               return Py_BuildValue("s",s);
+       }
+}
+
+static PyObject* py_keySetComment(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int size;
+       char *s;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keySetComment(k,s);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keyGetUID(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int size;
+       char *s;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keyGetUID(k);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keySetUID(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       char *s;
+       int uid;
+       if (!PyArg_Parse(args, "(Oi)",&py_k,&uid))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetUID(k,uid);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetGID(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int size;
+       char *s;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               size = keyGetGID(k);
+               return Py_BuildValue("i",size);
+       }
+}
+
+static PyObject* py_keySetGID(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       int gid;
+       if (!PyArg_Parse(args, "(Oi)",&py_k,&gid))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetGID(k,gid);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetMode(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int mode;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               mode = keyGetMode(k);
+               return Py_BuildValue("i",mode);
+       }
+}
+
+static PyObject* py_keySetMode(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       int mode;
+       if (!PyArg_Parse(args, "(Oi)",&py_k,&mode))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetMode(k,mode);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetOwnerSize(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetOwnerSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyOwner(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       char *s;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               s = keyOwner(k);
+               return Py_BuildValue("s",s);
+       }
+}
+
+static PyObject* py_keySetOwner(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       char *s;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetOwner(k,s);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetValueSize(PyObject *self, PyObject *args) 
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetValueSize(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetLink(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int mode;
+       char result[1024];
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               (void) keyGetLink(k,result,1023); // TODO return value not used
+               return Py_BuildValue("s",result);
+       }
+}
+
+static PyObject* py_keySetLink(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       char *s;
+       if (!PyArg_Parse(args, "(Os)",&py_k,&s))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keySetLink(k,s);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetMTime(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetMTime(k);
+               return Py_BuildValue("l",ret);
+       }
+}
+
+static PyObject* py_keyGetATime(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetATime(k);
+               return Py_BuildValue("l",ret);
+       }
+}
+
+static PyObject* py_keyGetCTime(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetCTime(k);
+               return Py_BuildValue("l",ret);
+       }
+}
+
+static PyObject* py_keyIsSystem(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsSystem(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyIsUser(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsUser(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyGetNamespace(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyGetNamespace(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyIsDir(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsDir(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyIsLink(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsLink(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyIsBin(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsBin(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_keyIsString(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_k))
+               return NULL;
+       else  {
+               k=PyLong_AsVoidPtr(py_k);
+               ret = keyIsString(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksInsert(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       KeySet *ks;
+       PyObject *py_ks;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_ks,&py_k))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k=PyLong_AsVoidPtr(py_k);
+               ret = ksInsert(ks,k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksAppendKey(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       KeySet *ks;
+       PyObject *py_ks;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_ks,&py_k))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k=PyLong_AsVoidPtr(py_k);
+               ret = ksAppendKey(ks,k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksPop(PyObject *self, PyObject *args)
+{
+       Key *k;
+       PyObject *py_k;
+       KeySet *ks;
+       PyObject *py_ks;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k = ksPop(ks);
+               py_k=PyLong_FromVoidPtr(k);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksInsertKeys(PyObject *self, PyObject *args)
+{
+       KeySet *ks1;
+       PyObject *py_ks1;
+       KeySet *ks2;
+       PyObject *py_ks2;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_ks1,&py_ks2))
+               return NULL;
+       else  {
+               ks1=PyLong_AsVoidPtr(py_ks1);
+               ks2=PyLong_AsVoidPtr(py_ks2);
+               ret = ksInsertKeys(ks1,ks2);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksAppend(PyObject *self, PyObject *args)
+{
+       KeySet *ks1;
+       PyObject *py_ks1;
+       KeySet *ks2;
+       PyObject *py_ks2;
+       int ret;
+       if (!PyArg_Parse(args, "(OO)",&py_ks1,&py_ks2))
+               return NULL;
+       else  {
+               ks1=PyLong_AsVoidPtr(py_ks1);
+               ks2=PyLong_AsVoidPtr(py_ks2);
+               ret = ksInsertKeys(ks1,ks2);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static PyObject* py_ksSort(PyObject *self, PyObject *args)
+{
+       KeySet *ks;
+       PyObject *py_ks;
+       int ret;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               ksSort(ks);
+               return Py_BuildValue("");
+       }
+}
+
+static PyObject* py_ksLookupByString(PyObject *self, PyObject *args) 
+{
+       KeySet *ks;
+       PyObject *py_ks, *LongPtr;
+       char *s;
+       long l;
+       unsigned long ul;
+       Key *ret;
+       if (!PyArg_Parse(args, "(Osi)",&py_ks,&s,&l))
+               return NULL;
+       else {
+               ul=(unsigned long) l;
+               ks=PyLong_AsVoidPtr(py_ks);
+               ret=ksLookupByString(ks,s,ul);
+               LongPtr=PyLong_FromVoidPtr(ret);
+               return LongPtr;
+       }
+}
+
+static PyObject* py_ksLookupByBinary(PyObject *self, PyObject *args) 
+{
+       KeySet *ks;
+       PyObject *py_ks, *LongPtr;
+       char *s;
+       int len;
+       long l;
+       unsigned long ul;
+       Key *ret;
+       if (!PyArg_Parse(args, "(Os#i)",&py_ks,&s,&len,&l))
+               return NULL;
+       else {
+               ul=(unsigned long) l;
+               ks=PyLong_AsVoidPtr(py_ks);
+               ret=ksLookupByBinary(ks,s,len,ul);
+               LongPtr=PyLong_FromVoidPtr(ret);
+               return LongPtr;
+       }
+}
+
+static PyObject* py_ksCurrent(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       Key *k;
+       PyObject *py_k;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k=ksCurrent(ks);
+               py_k=PyLong_FromVoidPtr(k);
+               return py_k;
+       }
+}
+
+static PyObject* py_ksHead(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       Key *k;
+       PyObject *py_k;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k=ksHead(ks);
+               py_k=PyLong_FromVoidPtr(k);
+               return py_k;
+       }
+}
+
+static PyObject* py_ksTail(PyObject *self, PyObject *args)
+{
+       PyObject *py_ks;
+       KeySet *ks;
+       Key *k;
+       PyObject *py_k;
+       if (!PyArg_Parse(args, "(O)",&py_ks))
+               return NULL;
+       else  {
+               ks=PyLong_AsVoidPtr(py_ks);
+               k=ksTail(ks);
+               py_k=PyLong_FromVoidPtr(k);
+               return py_k;
+       }
+}
+
+static PyObject* py_kdbRemove(PyObject *self, PyObject *args)
+{
+       PyObject *py_kdb;
+       KDB *kdb;
+       char *s;
+       int ret;
+       if (!PyArg_Parse(args, "(Os)",&py_kdb,&s))
+               return NULL;
+       else  {
+               kdb=PyLong_AsVoidPtr(py_kdb);
+               ret=kdbRemove(kdb,s);
+               return Py_BuildValue("i",ret);
+       }
+}
+
+static struct PyMethodDef elektra_methods[] = {
+       // Tested
+       {"kdbGetChildKeys",py_kdbGetChildKeys,1,"kdbGetChildKeys(handle,parentName,keySet,options) is similar and calls kdbGetKeyChildKeys().\n"
+                                               "It is provided for convenience."
+                                               "Instead of passing the parentName with a key it directly uses a string.\n"
+                                               "Parameters: \n"
+                                               "\thandle\t key Database handle\n"
+                                               "\tparentName\t Name of the parent key\n"
+                                               "\tkeySet\t key Set handle, the result is stored in this ks\n"
+                                               "\toptions\t options bits\n"
+                                               "Return: handle to the key, 0 otherwise"},
+       {"kdbGetKeyChildKeys",py_kdbGetKeyChildKeys,1,  "kdbGetKeyChildKeys(handle,parentKey,ks,options) Retrieve a number of inter-related keys\n"
+                                                       "at once. This is one of the most practical methods of the library, and you should use\n"
+                                                       "it to retrieve in one shot all keys needed by your application.\n"
+                                                       "Parameters: \n"
+                                                       "\thandle\t key Database handle\n"
+                                                       "\tparentName\t Name of the parent key\n"
+                                                       "\tkeySet\t key Set handle, the result is stored in this ks\n"
+                                                       "\toptions\t options bits\n"
+                                                       "Return: handle to the key, 0 otherwise"},
+       {"kdbOpen",py_kdbOpen,0,"kdbOpen() opens the session to the key database and returns a handle."},
+       {"ksNew",py_ksNew,0,"ksNew() creates a new keyset."},
+       {"ksDel",py_ksDel,1,"ksDel(ks) deletes a keyset."},
+       {"kdbClose",py_kdbClose,1,"kdbClose() closes the session to the key database"},
+       {"ksLookupByName",py_ksLookupByName,1,
+               "ksLookupByName(ks,name,options)\nLook for a Key contained in ks that matches name, starting from ks' ksNext() position.\n"
+               "Parameters: \n"
+               "\tks\t where to look for\n"
+               "\tname\t key name you are looking for\n"
+               "\toptions\t options bits\n"
+               "Return: handle to the key, 0 otherwise"
+               },
+//     {"keyStealValue",py_keyStealValue,1},
+       {"keyNew",py_keyNew,1,"keyNew(name) creates a new key with name <name>. Returns a key handle."},
+       {"keySetName",py_keySetName,0,  "keySetName(key,name) sets a new name to the key.\n"
+                                       "Parameters: \n"
+                                       "\tkey\t the key handle\n"
+                                       "\tname\t new key name\n"
+                                       "Return: size in bytes of this new key"
+                                       },
+       {"keySetBinary",py_keySetBinary,1,      "keySetBinary(key,value) sets the binary value of a key.\n"
+                                       "Parameters: \n"
+                                       "\tkey\t the key handle\n"
+                                       "\tvalue\t new key value\n"
+                                       "Return: size in bytes of this new key"
+                                       },
+       {"kdbSetKey",py_kdbSetKey,1,"kdbSetKey(handle,key) sets key in the backend storage.\n"
+                                       "Parameters: \n"
+                                       "\thandle\t kdb handle\n"
+                                       "\tkey\t key to set\n"
+                                       "Return: 0 on succes, -1 on failure"
+                                       },
+       {"ksRewind",py_ksRewind,1,      "ksRewind(ks) Resets a keySet internal cursor.\n"
+                                       "Parameter:\n"
+                                       "\tks\tKeySet\n"
+                                       "Returns: always 0"},
+       {"ksNext",py_ksNext,1,  "ksNext(ks) returns the next Key in a KeySet.\n"
+                                       "Parameter:\n"
+                                       "\tks\tKeySet\n"
+                                       "Returns: the new current key"},
+       {"keyGetFullName",py_keyGetFullName,1,  "keyGetFullName(key) returns the full name of the key.\n"
+                                       "Parameter:\n"
+                                       "\tkey\tthe key, which name should be requested\n"
+                                       "Returns: full name of the key"},
+       {"ksGetSize",py_ksGetSize,1,    "ksGetSize(ks) Return the number of keys that ks contains.\n"
+                                       "Parameter:\n"
+                                       "\tks\tKeySet\n"
+                                       "Returns: the number of keys that ks contains."},
+       {"keyGetString",py_keyGetString,1,      "keyGetString(key) returns the string value of the key.\n"
+                                               "Parameter:\n"
+                                               "\tkey\tthe key\n"
+                                               "Returns: the string value of the key"},
+       {"keySetString",py_keySetString,1,"keySetString(key,string) sets the string value of the key.\n"
+                                               "Parameter:\n"
+                                               "\tkey\tthe key\n"
+                                               "\tstring\tthe string to be set\n"
+                                               "Returns: number of characters of the string"},
+       {"kdbSetKeys",py_kdbSetKeys,1,  "kdbSetKeys(handle,ks) commits the ks KeySet to the backend storage, starting from ks's current "
+                                       "position until its end.\n"
+                                       "Parameters: \n"
+                                       "\thandle\t kdb handle\n"
+                                       "\tks\t keySet handle\n"
+                                       "Return: 0 on succes, -1 on failure"
+                                       },
+
+       {"keyGetNameSize",py_keyGetNameSize,1,"keyGetNameSize(key) returns the length of the key name"},
+       {"keyGetCommentSize",py_keyGetCommentSize,1,"keyGetCommentSize(key) returns the length of the comment"},
+       {"keyGetName",py_keyGetName,1,"keyGetName(key) returns the key name."},
+       {"keyGetComment",py_keyGetComment,1,"keyGetComment(key) returns the key comment."},
+       {"keyClose",py_keyClose,1,"keyClose free the key."}, 
+
+       // Not tested
+
+       // The following commented functions are not essential
+       /*{"kdbGetValue",py_kdbGetValue,1,"kdbGetValue help message"},
+       //{"keyGetDataSize",py_keyGetDataSize,1,"keyGetDataSize(key) returns the length of the key data"},
+       {"kdbGetKeyByParent",py_kdbGetKeyByParent,1,"kdbGetKeyByParent help message"},
+       {"kdbGetKeyByParentKey",py_kdbGetKeyByParentKey,1,"kdbGetKeyByParentKey help message"},
+       {"kdbGetValueByParent",py_kdbGetValueByParent,1,"kdbGetValueByParent help message"},
+       {"kdbSetValue",py_kdbSetValue,1,"kdbSetValue help message"},
+       {"kdbSetValueByParent",py_kdbSetValueByParent,1,"kdbSetValueByParent help message"}, 
+       {"keyInit",py_keyInit,1,"keyInit help message"},
+       {"keyIsInitialized",py_keyIsInitialized,1," help message"},
+       {"keyNeedSync",py_keyNeedSync,1,"keyNeedSync help message"},
+       {"keySetFlag",py_keySetFlag,1,"keySetFlag help message"},
+       {"keyClearFlag",py_keyClearFlag,1,"keyClearFlag help message"},
+       {"keyGetFlag",py_keyGetFlag,1,"keyGetFlag help message"},
+       {"keyGetRootName",py_keyGetRootName,1,"keyGetRootName help message"},
+       {"keyGetFullRootName",py_keyGetFullRootName,1,"keyGetFullRootName help message"},
+       {"keyNameGetRootNameSize",py_keyNameGetRootNameSize,1,"keyNameGetRootNameSize help message"},
+       {"keyGetRootNameSize",py_keyNameGetRootNameSize,1,"keyNameGetRootNameSize help message"},
+       {"keyBaseName",py_keyBaseName,1,"keyBaseName help message"},
+       {"keyNameGetBaseNameSize",py_keyNameGetBaseNameSize,1,"keyNameGetBaseNameSize help message"},
+       {"keyGetBaseNameSize",py_keyGetBaseNameSize,1,"keyGetBaseNameSize help message"},
+       {"keyAddBaseName",py_keyAddBaseName,1,"keyAddBaseName help message"},
+       {"keySetBaseName",py_keySetBaseName,1,"keySetBaseName help message"},
+       */
+
+       {"kdbGetKey",py_kdbGetKey,1,    "kdbGetKey(handle,key) Fully retrieves the passed key from the backend storage.\n"
+                                       "The backend will try to get the key, identified through its name.\n"
+                                       "Parameters: \n"
+                                       "\thandle\t kdb handle\n"
+                                       "\tkey\t a key that has the name set\n"
+                                       "Return: 0 on succes, -1 on failure"},
+
+       {"kdbStrError",py_kdbStrErr,1,"kdbStrError returns the string for the errno"},
+       {"kdbGetErrno",py_kdbGetErrno,1,"kdbGetErrno returns the last errno"},
+       {"kdbRemove",py_kdbRemove,1,"kdbRemove(kdb_handle,name) removes a key from the database"},
+
+
+       {"keyDup",py_keyDup,1,"keyDup(src,dest) creates a duplicate of src in dest. src and dest are key handles."},
+       {"keyGetType",py_keyGetType,1,"keyGetType(key) -> returns the type of the key"},
+       {"keySetType",py_keySetType,1,"keySetType(key,type) sets the key type to type."},
+       //{"keySetDir",py_keySetDir,1,"keySetDir help message"},
+
+       {"keyGetRecordSize",py_keyGetRecordSize,1,"keyGetRecordSize(key) -> int \nreturns the record size of the key."},
+       {"keyGetFullNameSize",py_keyGetFullNameSize,1,"keyGetFullNameSize(key) -> int\nreturns the fullname size of the key "},
+
+       {"keyName",py_keyName,1,"keyName(key) -> String\nreturns the name of the key."},
+
+       //{"keyNameGetOneLevel",py_keyNameGetOneLevel,1,"keyNameGetOneLevel help message"}, // TODO
+       //{"keyGetParentName",py_keyGetParentName,1,"keyGetParentName help message"},
+       //{"keyGetParentNameSize",py_keyGetParentNameSize,1,"keyGetParentNameSize help message"},
+
+       {"keyComment",py_keyComment,1,"keyComment(key) -> String returns the comment of key."},
+       {"keySetComment",py_keySetComment,1,"keySetComment(key,value) sets the comment with value."},
+
+       {"keyGetUID",py_keyGetUID,1,"keyGetUID(key) -> int\nreturns the UID."},
+       {"keySetUID",py_keySetUID,1,"keySetUID(key,uid) \nsets the UID."},
+       {"keyGetGID",py_keyGetGID,1,"keyGetGID(key) -> int\nreturns the GID."},
+       {"keySetGID",py_keySetGID,1,"keySetGID(key,gid) sets the gid."},
+
+       {"keyGetMode",py_keyGetMode,1,"keyGetMode(key) -> int returns the mode for the key."},
+       {"keySetMode",py_keySetMode,1,"keySetMode(key,int) sets mode."},
+
+       {"keyGetOwnerSize",py_keyGetOwnerSize,1,"keyGetOwnerSize(key) -> int\n returns the size of the owner."},
+       {"keyOwner",py_keyOwner,1,"keyOwner(key) -> string\nreturns the owner of the key."},
+       {"keySetOwner",py_keySetOwner,1,"keySetOwner(key,owner) sets the key owner to owner."},
+
+       // The following methods are not supported in python binding
+       // {"keyGetValueSize",py_keyGetValueSize,1,"keyGetValueSize(key) returns the size of the value."},
+       // {"keyValue",py_keyValue,1,"keyValue help message"},
+       // {"keyGetBinary",py_keyGetBinary,1,"keyGetBinary help message"},
+
+       {"keyGetLink",py_keyGetLink,1,"keyGetLink(key) -> string\nreturns the link"},
+       {"keySetLink",py_keySetLink,1,"keySetLink(key,value)\n sets the link value"},
+
+       {"keyGetMTime",py_keyGetMTime,1,"keyGetMTime(key) -> int\nreturns the MTime."},
+       {"keyGetATime",py_keyGetATime,1,"keyGetATime(key) -> int\nreturns the ATime."},
+       {"keyGetCTime",py_keyGetCTime,1,"keyGetCTime(key) -> int\nreturns the CTime."},
+
+       {"keyIsSystem",py_keyIsSystem,1,"keyIsSystem(key) -> int\nCheck whether a key is under the system namespace or not.\n"
+                                       "Returns: 1 if key name begins with system, 0 otherwise."
+                                       },
+       {"keyIsUser",py_keyIsUser,1,"keyIsUser(key) -> int\nCheck whether a key is under the user namespace or not.\n"
+                                   "Returns: 1 if key name begins with user, 0 otherwise."
+                                   },
+       //{"keyGetNamespace",py_keyGetNamespace,1,"keyGetNamespace help message"}, //redundant
+       {"keyIsDir",py_keyIsDir,1,      "keyIsDir(key) -> int\nCheck if a key is folder key.\n"
+                                       "Returns: 1 if key is a folder, 0 otherwise."
+                                       },
+       {"keyIsLink",py_keyIsLink,1,    "keyIsLink(key) -> int\nCheck if a key is a link key.\n"
+                                       "Returns: 1 if key is a link, 0 otherwise."
+                                       },
+       {"keyIsBin",py_keyIsBin,1,      "keyIsBin(key) -> int\nCheck if a key is binary type.\n"
+                                       "Returns: 1 if it is binary."
+                                       },
+       {"keyIsString",py_keyIsString,1,"keyIsString(key) -> int\nCheck if a key is a string type.\n"
+                                       "Returns: 1 if it is not binary."
+                                       },
+
+       //{"keyToStream",py_keyToStream,1,"keyToStream help message"}, //TODO
+       //{"keyToStreamBasename",py_keyToStreamBasename,1,"keyToStreamBasename help message"}, //TODO
+
+       {"ksInsert",py_ksInsert,1,      "ksInsert(ks,key) inserts a new Key in the beginning of the KeySet. A reference to the key will be\n" 
+                                       "stored, and not a copy of the key. So a future ksClose() or ksDel() on ks will keyDel() the key object.\n"
+                                       "Parameters: \n"
+                                       "\tks\t the keyset\n"
+                                       "\tkey\t key that will be inserted in the keyset\n"
+                                       "Return: size of keyset after insertion."
+                                       },
+       {"ksAppend",py_ksAppend,1,      "ksAppendKey(ks,key) appends a new Key at the end of the KeySet. A reference to the key will be stored\n"
+                                       ", and not a copy of the key. So a future ksClose() or ksDel() on ks will keyDel() the key object.\n"
+                                       "Parameters: \n"
+                                       "\tks\t the keyset\n"
+                                       "\tkey\t key that will be appended in the keyset\n"
+                                       "Return: size of keyset after insertion."
+                                       },
+       {"ksPop",py_ksPop,1,    "ksPop(ks) Remove and return the first key of ks."
+                               "If ks' cursor was positioned in the poped key, ks will be ksRewind()ed. "
+                               },
+       {"ksPopLast",py_ksPopLast,1,"ksPop(ks) Remove and return the last key of ks."
+                               "If ks' cursor was positioned in the poped key, ks will be ksRewind()ed. "
+                               },
+       {"ksInsertKeys",py_ksInsertKeys,1,      "ksInsertKeys(ks,toInsert) transfers all keys from toInsert to the begining of ks.\n"
+                                               "After this call, toInsert will be empty and can be deleted with ksDel()\n"
+                                               "Returns: the size of the KeySet after transfer."
+                                               },
+       {"ksAppend",py_ksAppend,1,      "ksAppend(ks,toAppend) Transfers all toAppend contained keys to the end of the ks\n"
+                                               "After this call, toAppend will be empty and can be deleted with ksDel()\n"
+                                               "Returns: the size of the KeySet after transfer."
+                                               },
+       //{"ksToStream",py_ksToStream,1,"ksToStream help message"}, // TODO
+       {"ksSort",py_ksSort,1,"ksSort(ks) sorts a KeySet aphabetically by Key name, using qsort()."
+                               },
+       {"ksLookupByString",py_ksLookupByString,1,      "ksLookupByString(ks,value,options) lookups for a Key contained in ks KeySet that\n" 
+                                                       "matches value, starting from ks' ksNext() position.\n"
+                                                       "Parameters: \n"
+                                                       "\tks\t where to look for\n"
+                                                       "\tvalue\t the value which owner key you want to find\n"
+                                                       "\toptions\t option bits\n"
+                                                       "Returns: the key found, 0 otherwise."
+                                                       },
+       {"ksLookupByBinary",py_ksLookupByBinary,1,      "ksLookupByBinary(ks,value,options) lookups for a Key contained in ks KeySet that\n"
+                                                       "matches the binary value, starting from ks' ksNext() position.\n"
+                                                       "Parameters: \n"
+                                                       "\tks\t where to look for\n"
+                                                       "\tvalue\t the value which owner key you want to find\n"
+                                                       "\toptions\t option bits\n"
+                                                       "Returns: the key found, 0 otherwise."
+                                                       },
+       //{"ksLookupRE",py_ksLookupRE,1,"ksLookupRE help message"},
+       {"ksCurrent",py_ksCurrent,1,"ksCurrent(ks) returns the current Key or 0 if you reached the end"},
+       {"ksHead",py_ksHead,1,"ksHead(ks) returns the first key in the KeySet, without changing the KeySet's internal cursor."},
+       {"ksTail",py_ksTail,1,"ksTail(ks) returns the last key in the KeySet, without changing the KeySet's internal cursor."},
+       {NULL,NULL,0,NULL}
+};
+
+void initlibpyelektra()
+{
+       (void) Py_InitModule("libpyelektra", elektra_methods);
+}
diff --git a/src/bindings/python/test.py b/src/bindings/python/test.py
new file mode 100644 (file)
index 0000000..6a525fc
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/python
+# try:
+#     python test.py -v
+# You need the kdb tool installed
+
+
+"""
+>>> ###################### libelektra tests   ########################
+>>> # Set user/key using the kdbtool and then remove it using kdbRemove
+>>> import libelektra
+>>> import elektra
+>>> import os
+>>> import string
+>>> os.system("kdb set user/key 'text'")
+0
+>>> kdb=libelektra.kdbOpen()
+>>> libelektra.kdbRemove(kdb,"user/key")
+0
+>>> ks=libelektra.ksNew()
+>>> string.strip(os.popen("kdb get user/key").read())
+''
+>>> # Create a new key using libelektra and check the new key using kdb
+>>> k=libelektra.keyNew("user/key")
+>>> libelektra.keySetString(k,"text")!=0
+True
+>>> libelektra.kdbSetKey(kdb,k)
+0
+>>> string.strip(os.popen("kdb get user/key").read())
+'text'
+>>> libelektra.kdbGetChildKeys(kdb,"user",ks,1)!=-1
+True
+>>> k=libelektra.ksLookupByName(ks,"/key",0)
+>>> libelektra.keyGetString(k)
+'text'
+>>> # Close the handles
+>>> libelektra.keyClose(k)
+0
+>>> libelektra.kdbClose(kdb)
+0
+>>> #######################  elektra tests  #######################
+>>> kdb=elektra.Kdb()
+>>> kdb.open()
+>>> os.system("kdb set user/key 'text'")
+0
+>>> # Remove key
+>>> kdb.remove("user/key")
+0
+>>> # Set Key
+>>> ks=elektra.KeySet()
+>>> ks.new()
+>>> k=elektra.Key()
+>>> k.new("user/key")
+>>> k.setString("text")
+5
+>>> kdb.setKey(k)
+0
+>>> string.strip(os.popen("kdb get user/key").read())
+'text'
+>>> ks=kdb.getChildKeys("user",norecursive=True)
+>>> key=ks.lookupByName("/key")
+>>> key.getString()
+'text'
+>>> k.close()
+>>> kdb.close()
+"""
+
+def _test():
+    import doctest
+    doctest.testmod()
+
+if __name__ == "__main__":
+    _test()
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
new file mode 100644 (file)
index 0000000..7b22258
--- /dev/null
@@ -0,0 +1,19 @@
+
+# $Id$
+
+nobase_include_HEADERS = kdb.h kdbloader.h kdbbackend.h kdbtools.h kdbprivate.h kdbos.h
+
+EXTRA_DIST = kdbprivate.h.in kdb.h.in
+
+kdbprivate.h: kdbprivate.h.in ../../config.status
+       sed -e "s:[@]KDB_DB_SYSTEM[@]:/opt/var/kdb:" "$(srcdir)/kdbprivate.h.in" |\
+       sed -e "s:[@]KDB_DB_USER[@]:/opt/var/kdb:" |\
+       sed -e "s:[@]KDB_DB_MEMORY[@]:/var/run:" > kdbprivate.h
+
+kdb.h: kdb.h.in ../../config.status
+       sed -e "s:[@]KDB_VERSION[@]:\"$(KDB_VERSION)\":" "$(srcdir)/kdb.h.in" |\
+       sed -e "s:[@]KDB_VERSION_MAJOR[@]:$(KDB_VERSION_MAJOR):" |\
+       sed -e "s:[@]KDB_VERSION_MINOR[@]:$(KDB_VERSION_MINOR):" |\
+       sed -e "s:[@]KDB_VERSION_MICRO[@]:$(KDB_VERSION_MICRO):" > kdb.h
+
+DISTCLEANFILES = kdbprivate.h kdb.h
diff --git a/src/include/kdb.h.in b/src/include/kdb.h.in
new file mode 100755 (executable)
index 0000000..b424dfb
--- /dev/null
@@ -0,0 +1,255 @@
+/***************************************************************************
+                kdb.h  -  Exported methods of the Library
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    copyright            : (C) 2008 by Markus Raab
+    email                : avi@unix.sh, elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef KDB_H
+#define KDB_H
+
+#include "kdbos.h"
+
+#define KDB_VERSION @KDB_VERSION@
+#define KDB_VERSION_MAJOR @KDB_VERSION_MAJOR@
+#define KDB_VERSION_MINOR @KDB_VERSION_MINOR@
+#define KDB_VERSION_MICRO @KDB_VERSION_MICRO@
+
+#define KS_END ((Key*)0)
+
+enum {
+       KEY_TYPE_UNDEFINED=0,
+       KEY_TYPE_BINARY=20,
+       KEY_TYPE_STRING=40,
+       KEY_TYPE_MAX=256
+};
+
+enum {
+       KEY_END=0,
+       KEY_NAME=1,
+       KEY_VALUE=1<<1,
+       KEY_OWNER=1<<2,
+       KEY_COMMENT=1<<3,
+       KEY_TYPE=1<<4,
+       KEY_UID=1<<5,
+       KEY_GID=1<<6,
+       KEY_MODE=1<<7,
+       KEY_ATIME=1<<8,
+       KEY_MTIME=1<<9,
+       KEY_CTIME=1<<10,
+       KEY_SIZE=1<<11,
+       KEY_REMOVE=1<<12,
+       KEY_STAT=1<<13,
+       KEY_DIR=1<<14
+};
+
+enum {
+       KDB_O_NONE=0,
+       KDB_O_DEL=1,
+       KDB_O_POP=1<<1,
+       KDB_O_NODIR=1<<2,
+       KDB_O_DIRONLY=1<<3,
+       KDB_O_NOSTAT=1<<4,
+       KDB_O_STATONLY=1<<5,
+       KDB_O_NOREMOVE=1<<6,
+       KDB_O_REMOVEONLY=1<<7,
+       KDB_O_INACTIVE=1<<8,
+       KDB_O_SYNC=1<<9,
+       KDB_O_SORT=1<<10,
+       KDB_O_NORECURSIVE=1<<11,
+       KDB_O_NOCASE=1<<12,
+       KDB_O_WITHOWNER=1<<13,
+       KDB_O_NOALL=1<<14
+};
+
+
+#ifdef __cplusplus
+namespace ckdb {
+extern "C" {
+#endif
+
+
+typedef struct _KDB    KDB;
+typedef struct _Key    Key;
+typedef struct _KeySet KeySet;
+
+
+/**************************************
+ *
+ * KDB methods
+ *
+ **************************************/
+
+KDB * kdbOpen(void);
+int kdbClose(KDB *handle);
+
+ssize_t kdbGet(KDB *handle, KeySet *returned,
+       Key * parentKey, option_t options);
+ssize_t kdbSet(KDB *handle, KeySet *returned,
+       Key * parentKey, option_t options);
+
+ssize_t kdbGetByName(KDB *handle, KeySet *returned,
+       const char *parentName, option_t options);
+
+int kdbGetKey(KDB *handle, Key *key);
+int kdbSetKey(KDB *handle, const Key *key);
+
+int kdbGetString(KDB *handle, const char *keyname, char *returned,
+       size_t maxSize);
+int kdbSetString(KDB *handle, const char *keyname, const char *value);
+int kdbRemove(KDB *handle, const char *keyname);
+
+
+/**************************************
+ *
+ * Key methods
+ *
+ **************************************/
+
+/* Basic Methods */
+Key *keyNew(const char *keyname, ...);
+Key *keyVNew(const char *keyname, va_list ap);
+
+Key *keyDup(const Key *source);
+int keyCopy(Key *dest, const Key *source);
+
+int keyDel(Key *key);
+
+ssize_t keyIncRef(Key *key);
+ssize_t keyDecRef(Key *key);
+ssize_t keyGetRef(const Key *key);
+
+/* Meta Info */
+int keyStat(Key *key);
+int keyRemove(Key *key);
+
+uid_t keyGetUID(const Key *key);
+int keySetUID(Key *key, uid_t uid);
+
+gid_t keyGetGID(const Key *key);
+int keySetGID(Key *key, gid_t gid);
+
+int keySetDir(Key *key);
+mode_t keyGetMode(const Key *key);
+int keySetMode(Key *key, mode_t mode);
+
+type_t keyGetType(const Key *key);
+int keySetType(Key *key, type_t type);
+
+time_t keyGetATime(const Key *key);
+int keySetATime(Key *key, time_t atime);
+
+time_t keyGetMTime(const Key *key);
+int keySetMTime(Key *key, time_t mtime);
+
+time_t keyGetCTime(const Key *key);
+int keySetCTime(Key *key, time_t ctime);
+
+/* Methods for Making Tests */
+int keyNeedStat(const Key *key);
+int keyNeedSync(const Key *key);
+int keyNeedRemove(const Key *key);
+
+int keyIsSystem(const Key *key);
+int keyIsUser(const Key *key);
+int keyIsMemory(const Key *key);
+
+int keyIsBelow(const Key *key, const Key *check);
+int keyIsDirectBelow(const Key *key, const Key *check);
+
+int keyIsInactive(const Key *key);
+
+int keyIsDir(const Key *key);
+int keyIsBinary(const Key *key);
+int keyIsString(const Key *key);
+
+/* Name Manipulation Methods */
+const char *keyName(const Key *key);
+const void *takeout_keyValue(Key *key);
+ssize_t keyGetNameSize(const Key *key);
+ssize_t keyGetName(const Key *key, char *returnedName, size_t maxSize);
+ssize_t keySetName(Key *key, const char *newname);
+
+ssize_t keyGetFullNameSize(const Key *key);
+ssize_t keyGetFullName(const Key *key, char *returnedName, size_t maxSize);
+
+const char *keyBaseName(const Key *key);
+ssize_t keyGetBaseNameSize(const Key *key);
+ssize_t keyGetBaseName(const Key *key, char *returned, size_t maxSize);
+ssize_t keyAddBaseName(Key *key,const char *baseName);
+ssize_t keySetBaseName(Key *key,const char *baseName);
+
+const char *keyOwner(const Key *key);
+ssize_t keyGetOwnerSize(const Key *key);
+ssize_t keyGetOwner(const Key *key, char *returned, size_t maxSize);
+ssize_t keySetOwner(Key *key, const char *owner);
+
+/* Value Manipulation Methods */
+const void *keyValue(const Key *key);
+ssize_t keyGetValueSize(const Key *key);
+
+ssize_t keyGetString(const Key *key, char *returnedString, size_t maxSize);
+ssize_t keySetString(Key *key, const char *newString);
+
+ssize_t keyGetBinary(const Key *key, void *returnedBinary, size_t maxSize);
+ssize_t keySetBinary(Key *key, const void *newBinary, size_t dataSize);
+
+const char *keyComment(const Key *key);
+ssize_t keyGetCommentSize(const Key *key);
+ssize_t keyGetComment(const Key *key, char *returnedDesc, size_t maxSize);
+ssize_t keySetComment(Key *key, const char *newDesc);
+
+
+
+/**************************************
+ *
+ * KeySet methods
+ *
+ **************************************/
+
+KeySet *ksNew(size_t alloc, ...);
+KeySet *ksVNew(size_t alloc, va_list ap);
+
+KeySet *ksDup(const KeySet * source);
+int ksCopy(KeySet *dest, const KeySet *source);
+
+int ksDel(KeySet *ks);
+
+void ksSort(KeySet *ks);
+
+ssize_t ksGetSize(const KeySet *ks);
+
+ssize_t ksAppendKey(KeySet *ks, Key *toAppend);
+ssize_t ksAppend(KeySet *ks, const KeySet *toAppend);
+
+Key *ksPop(KeySet *ks);
+
+int ksRewind(KeySet *ks);
+Key *ksNext(KeySet *ks);
+Key *ksCurrent(const KeySet *ks);
+
+Key *ksHead(const KeySet *ks);
+Key *ksTail(const KeySet *ks);
+
+cursor_t ksGetCursor(const KeySet *ks);
+int ksSetCursor(KeySet *ks, cursor_t cursor);
+
+Key *ksLookup(KeySet *ks, Key *k, option_t options);
+Key *ksLookupByName(KeySet *ks, const char *name, option_t options);
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+
+#endif /* KDB_H */
diff --git a/src/include/kdbbackend.h b/src/include/kdbbackend.h
new file mode 100644 (file)
index 0000000..c1addbb
--- /dev/null
@@ -0,0 +1,158 @@
+/***************************************************************************
+                kdbbackend.h  -  Methods for backend programing
+                             -------------------
+    begin                : Mon Dec 25 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/*You have to include this file in order to write backends or for internal
+ *source files for elektra. You do not need this functions to use elektra!*/
+
+
+#ifndef KDBBACKEND_H
+#define KDBBACKEND_H
+
+
+#include <kdb.h>
+#include <kdbprivate.h>
+
+#ifdef ELEKTRA_STATIC
+        #define KDBEXPORT(module) KDB *libelektra_##module##_LTX_kdbBackendFactory(void)       
+#else
+        #define KDBEXPORT(module) KDB *kdbBackendFactory(void)
+#endif
+
+
+
+/**
+ * Switches to denote the backend methods. Used in calls to kdbBackendExport().
+ *
+ * @ingroup backend
+ */
+typedef enum {
+       KDB_BE_OPEN=1,          /*!< Next arg is backend for kdbOpen() */
+       KDB_BE_CLOSE=1<<1,      /*!< Next arg is backend for kdbClose() */
+       KDB_BE_GET=1<<2,        /*!< Next arg is backend for kdbGet() */
+       KDB_BE_SET=1<<3,        /*!< Next arg is backend for kdbSet() */
+       KDB_BE_VERSION=1<<4,    /*!< Next arg is char * for Version */
+       KDB_BE_DESCRIPTION=1<<5,/*!< Next arg is char * for Description */
+       KDB_BE_AUTHOR=1<<6,     /*!< Next arg is char * for Author*/
+       KDB_BE_LICENCE=1<<7,    /*!< Next arg is char * for Licence*/
+       KDB_BE_END=0            /*!< End of arguments */
+} backend_t;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+KDB *kdbBackendExport(const char *backendName, ...);
+
+/* Mounting API */
+int kdbMount(KDB *handle, const Key *mountpoint, const KeySet *config);
+int kdbUnmount(KDB *handle, const Key *mountpoint);
+Key *kdbGetMountpoint(KDB *handle, const Key *where);
+
+/* Idea for new api...
+unsigned long kdbGetCapability(KDB *handle, const Key *where, unsigned long mask);
+const char *kdbGetString(KDB *handle, const Key *where, unsigned long which);
+*/
+
+/* Old capability API */
+KDBCap *kdbGetCapability(KDB *handle, const Key *where);
+int kdbhGetErrno(const KDB *handle);
+
+/* Some internal methods */
+int kdbiRealloc (void ** buffer, size_t size);
+void* kdbiMalloc (size_t size);
+void kdbiFree (void *ptr);
+char *kdbiStrDup (const char *s);
+size_t kdbiStrLen(const char *s);
+
+ssize_t kdbbEncode(void *kdbbDecoded, size_t size, char *returned);
+ssize_t kdbbDecode(char *kdbbEncoded,void *returned);
+
+int kdbbNeedsUTF8Conversion(void);
+int kdbbkdbbUTF8Engine(int direction, char **string, size_t *inputOutputByteSize);
+
+int kdbbEncodeChar(char c, char *buffer, size_t bufSize);
+int kdbbDecodeChar(const char *from, char *into);
+
+int kdbbFilenameToKeyName(const char *string, char *buffer, int bufSize);
+int kdbbKeyNameToRelativeFilename(const char *string, char *buffer, size_t bufSize);
+ssize_t kdbbKeyCalcRelativeFilename(const Key *key,char *relativeFilename,size_t maxSize);
+
+ssize_t kdbbGetFullKeyName (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned);
+ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize);
+
+
+/* Some handle manipulation methods */
+void *kdbhGetBackendData(const KDB *handle);
+void *kdbhSetBackendData(KDB *handle, void *data);
+
+KDBCap* kdbhSetCapability(KDB *handle, KDBCap *cap);
+KDBCap* kdbhGetCapability(const KDB *handle);
+
+Trie *kdbhGetTrie(const KDB *handle);
+void kdbhSetTrie(KDB *handle, Trie *trie);
+
+const Key *kdbhGetMountpoint(KDB *handle);
+void kdbhSetMountpoint(KDB *handle, const Key* mountpoint);
+
+KeySet *kdbhGetConfig(KDB *handle);
+
+/* Capability methods */
+KDBCap *capNew (void);
+void capDel (KDBCap *cap);
+
+const char *kdbcGetName (const KDBCap *cap);
+const char *kdbcGetVersion (const KDBCap *cap);
+const char *kdbcGetDescription (const KDBCap *cap);
+const char *kdbcGetAuthor (const KDBCap *cap);
+const char *kdbcGetLicence (const KDBCap *cap);
+
+/* too many functions, use flags */
+unsigned int kdbcGetonlyFullGet (const KDBCap *cap);
+unsigned int kdbcGetonlyFullSet (const KDBCap *cap);
+unsigned int kdbcGetonlyRemoveAll (const KDBCap *cap);
+unsigned int kdbcGetonlyAddKeys (const KDBCap *cap);
+unsigned int kdbcGetonlySystem (const KDBCap *cap);
+unsigned int kdbcGetonlyUser (const KDBCap *cap);
+unsigned int kdbcGetonlyFullSync (const KDBCap *cap);
+unsigned int kdbcGetnoOwner (const KDBCap *cap);
+unsigned int kdbcGetnoValue (const KDBCap *cap);
+unsigned int kdbcGetnoComment (const KDBCap *cap);
+unsigned int kdbcGetnoUID (const KDBCap *cap);
+unsigned int kdbcGetnoGID (const KDBCap *cap);
+unsigned int kdbcGetnoMode (const KDBCap *cap);
+unsigned int kdbcGetnoDir (const KDBCap *cap);
+unsigned int kdbcGetnoATime (const KDBCap *cap);
+unsigned int kdbcGetnoMTime (const KDBCap *cap);
+unsigned int kdbcGetnoCTime (const KDBCap *cap);
+unsigned int kdbcGetnoRemove (const KDBCap *cap);
+unsigned int kdbcGetnoStat (const KDBCap *cap);
+unsigned int kdbcGetnoMount (const KDBCap *cap);
+unsigned int kdbcGetnoBinary (const KDBCap *cap);
+unsigned int kdbcGetnoString (const KDBCap *cap);
+unsigned int kdbcGetnoTypes (const KDBCap *cap);
+unsigned int kdbcGetnoError (const KDBCap *cap);
+unsigned int kdbcGetnoLock (const KDBCap *cap);
+unsigned int kdbcGetnoThread (const KDBCap *cap);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* KDBBACKEND_H */
+
diff --git a/src/include/kdbloader.h b/src/include/kdbloader.h
new file mode 100644 (file)
index 0000000..0c46794
--- /dev/null
@@ -0,0 +1,57 @@
+/* $Id$ */
+
+#ifndef KDBLIBLOADER_H
+#define KDBLIBLOADER_H
+
+#ifdef ELEKTRA_STATIC
+
+/* The static case
+ *
+ * Struct which contain export symbols
+ *  Format :
+ *  --------
+ *
+ *  filename, NULL
+ *  symbol1, &func1,
+ *  symbol2, &func2,
+ *  filename2, NULL
+ *  symbol3, &func3,
+ *  symboln, &funcn,
+ *  NULL, NULL
+ */
+typedef struct {
+       const char *name;
+       void (*function)(void);
+} kdblib_symbol;
+typedef kdblib_symbol* kdbLibHandle;
+
+extern kdblib_symbol kdb_exported_syms[];
+
+#else
+# ifdef WIN32
+
+/* Windows case, non static */
+# include <windows.h>
+typedef HMODULE kdbLibHandle;
+
+# else
+
+/* Default case */
+#  include <ltdl.h>
+typedef lt_dlhandle kdbLibHandle;
+
+# endif
+#endif
+
+
+/* General pointer to kdbLib Functions and pointer to kdbLibBackend function */
+typedef void (*kdbLibFunc)(void);
+
+/* Functions */
+int kdbLibInit(void);
+kdbLibHandle kdbLibLoad(const char *backendName);
+kdbLibFunc kdbLibSym(kdbLibHandle handle, const char *symbol);
+int kdbLibClose(kdbLibHandle handle);
+const char *kdbLibError(void);
+
+#endif /* KDBLIBLOADER_H */
diff --git a/src/include/kdbos.h b/src/include/kdbos.h
new file mode 100644 (file)
index 0000000..b88d28f
--- /dev/null
@@ -0,0 +1,173 @@
+/***************************************************************************
+             kdbos.h  -  operating system specific workarounds
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/* This header purpose is that afterwards following types are defined:
+ * .. means don't care, just enough for your system
+ * For more information on that types read POSIX documentation.
+ *
+ * Type     Purpose                 Limits
+ * size_t   size of array or string 0, SIZE_MAX
+ * ssize_t  size with error cond.   -1, SSIZE_MAX(<SIZE_MAX)
+ * time_t   Seconds since 1970      0,.. recommended: 64 bit
+ *
+ * Integer Types must be at least 32bit:
+ *
+ * int      Integral Fast Type      INT_MIN, INT_MAX
+ * uid_t    User identification     0,..
+ * gid_t    Group identification    0,..
+ * type_t   Typing information      -1,..
+ * keyswitch_t For keyNew
+ * option_t    For kdbGet, kdbSet and ksLookup*
+ *
+ *
+ * Following elektra specific types are defined:
+ *
+ * Type     Purpose
+ * cursor_t stores information to find a position in a keyset
+ *
+ * Following constants must be defined:
+ *
+ * KEY_SEPARATOR   how to delimit keynames
+ * PATH_SEPARATOR  how to delimit pathnames
+ * KEY_DEF_MODE    the standard mode for keys
+ * KEY_DIR_MODE    the mode to add (|=) for key directories
+ *
+ * Following limits must be defined (in addition to limits mentioned
+ * above for types):
+ *
+ * MAX_UCHAR       the maximum for unsigned char
+ * MAX_KEY_LENGTH  the maximum length for a keyname
+ * MAX_PATH_LENGTH the maximum length for a pathname
+ *
+ * In addition to the types the ... or va_list must be supported,
+ * this is ISO C and should happen by including <stdarg.h>.
+ *
+ * Go ahead and write a #ifdef block for your operating system
+ * when the POSIX defaults are not ok.
+ */
+
+
+#ifndef KDBOS_H
+#define KDBOS_H
+
+
+/* Include essential headers used in kdb.h */
+#include <stdarg.h>
+
+#ifndef WIN32
+
+/***************************************************
+ *               Posix Compatible
+ ***************************************************/
+
+#ifndef __USE_POSIX
+#define __USE_POSIX
+#endif
+#include <limits.h>
+
+/* Conforming C++ programs are not allowed to
+ * include inttypes.h*/
+#include <inttypes.h>
+#include <sys/types.h>
+
+/**Separator for Path names*/
+#define PATH_SEPARATOR '/'
+
+
+/**MAX_PATH_LENGTH will be the value for longest
+ * possible filenames on the system.*/
+
+/*Some systems have even longer pathnames*/
+#ifdef PATH_MAX
+#define MAX_PATH_LENGTH PATH_MAX
+/*This value is garanteed on any Posixsystem*/
+#elif defined __USE_POSIX
+#define MAX_PATH_LENGTH _POSIX_PATH_MAX
+#else
+#define MAX_PATH_LENGTH 4096
+#endif
+
+/*Type to point to every position within the keyset*/
+typedef ssize_t cursor_t;
+
+/*Integer types*/
+typedef int type_t;
+typedef int keyswitch_t;
+typedef int option_t;
+
+/**Separator for key names.
+ * This character will be used to separate key names*/
+#define KEY_SEPARATOR '/'
+/**Default Mode.
+ * This mode will be used for fresh keys*/
+#define KEY_DEF_MODE 0664
+/**Default directory mode.
+ * This mode will be ORed to have a directory key*/
+#define KEY_DEF_DIR 0111
+
+/**Lets assume that the namespace + relative path is shorter then the absolute path.
+ * So the maximum length of key is what the system allows for the path.*/
+#define MAX_KEY_LENGTH MAX_PATH_LENGTH
+
+
+
+
+#else /* WIN32 */
+
+/***************************************************
+ *                 Windows
+ ***************************************************/
+# include <windows.h>
+# include <limits.h>
+
+# define usleep(x) Sleep(x)
+
+# define ssize_t int
+# define snprintf _snprintf
+
+/**Separator for Path names*/
+#define PATH_SEPARATOR '\\'
+
+#define MAX_PATH_LENGTH 4096
+
+/*Type to point to every position within the keyset*/
+typedef ssize_t cursor_t;
+
+/*Integer types*/
+typedef int type_t;
+typedef int keyswitch_t;
+typedef int option_t;
+
+/**Separator for key names.
+ * This character will be used to separate key names*/
+#define KEY_SEPARATOR '/'
+/**Default Mode.
+ * This mode will be used for fresh keys*/
+#define KEY_DEF_MODE 0664
+/**Default directory mode.
+ * This mode will be ORed to have a directory key*/
+#define KEY_DEF_DIR 0111
+
+/**Lets assume that the namespace + relative path is shorter then the absolute path.
+ * So the maximum length of key is what the system allows for the path.*/
+#define MAX_KEY_LENGTH MAX_PATH_LENGTH
+
+
+
+
+#endif /* WIN32 */
+
+#endif /* KDBOS_H */
+
diff --git a/src/include/kdbprivate.h.in b/src/include/kdbprivate.h.in
new file mode 100755 (executable)
index 0000000..b96075b
--- /dev/null
@@ -0,0 +1,625 @@
+/***************************************************************************
+      kdbprivate.h  -  Private classes definition
+
+                           -------------------
+    begin                : Mon Apr 12 2004
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef KDB_H
+#error dont include kdbprivate.h directly, use kdbbackend.h instead
+#else
+
+#ifndef KDBPRIVATE_H
+#define KDBPRIVATE_H
+
+#include <limits.h>
+#include <kdbloader.h>
+
+#ifndef KDB_DB_MEMORY
+/**Below this directory the memory configuration (memory/) will be searched.*/
+#define KDB_DB_MEMORY            "@KDB_DB_MEMORY@"
+#endif
+
+#ifndef KDB_DB_SYSTEM
+/**Below this directory the system configuration (system/) will be searched.*/
+#define KDB_DB_SYSTEM            "@KDB_DB_SYSTEM@"
+#endif
+
+#ifndef KDB_DB_USER
+#ifdef TUNNING_ELEKTRA_0_7
+/**Below this directory the user configuration (user/) will be searched.*/
+#define KDB_DB_USER            "@KDB_DB_USER@"
+#else
+/**This directory will be concatenated with a prefix which will be searched
+ * at runtime inside kdbbGetFullFilename().
+ *@see kdbbGetFullFilename
+ */
+#define KDB_DB_USER              ".kdb"
+#endif
+#endif
+
+
+#ifndef KDB_DB_HOME
+/**This directory will be used as fallback when no other method of
+ * kdbbGetFullFilename() works.
+ *@see kdbbGetFullFilename
+ */
+#define KDB_DB_HOME              "/home"
+#endif
+
+#ifndef KDB_KEY_MOUNTPOINTS
+/**Backend information.
+ *
+ * This key directory tells you where each backend is mounted
+ * to which mountpoint. */
+#define KDB_KEY_MOUNTPOINTS      "system/elektra/mountpoints"
+#endif
+
+#define KDB_KEY_MOUNTPOINTS_LEN  (sizeof (KDB_KEY_MOUNTPOINTS))
+
+#ifndef KDB_KEY_USERS
+/**Users information.
+ *
+ * This key directory tells you the users existing on the system. */
+#define KDB_KEY_USERS            "system/users"
+#endif
+
+#define KDB_KEY_USERS_LEN        (sizeof (KDB_KEY_USERS))
+
+#ifndef MAX_UCHAR
+#define MAX_UCHAR (UCHAR_MAX+1)
+#endif
+
+#ifndef KEYSET_SIZE
+#define KEYSET_SIZE 16
+#endif
+
+#ifndef APPROXIMATE_NR_OF_BACKENDS
+#define APPROXIMATE_NR_OF_BACKENDS 16
+#endif
+
+#ifndef ESCAPE_CHAR
+#define ESCAPE_CHAR '\\'
+#endif
+
+/**BUFFER_SIZE can be used as value for any I/O buffer
+ * on files.
+ *
+ * It may be used for optimization on various
+ * systems.*/
+#ifndef BUFFER_SIZE
+#define BUFFER_SIZE 256
+#endif
+
+#ifdef UT_NAMESIZE
+#define USER_NAME_SIZE UT_NAMESIZE
+#else
+#define USER_NAME_SIZE 100
+#endif
+
+
+#ifndef DEFFILEMODE
+#define DEFFILEMODE 0666
+#endif
+
+
+#define UTF8_TO   1
+#define UTF8_FROM 0
+
+
+typedef struct _Trie   Trie;
+typedef struct _Split  Split;
+typedef struct _KDBCap KDBCap;
+
+/* These define the type for pointers to all the kdb functions */
+typedef KDB*     (*kdbOpenPtr)();
+typedef int      (*kdbClosePtr)(KDB *);
+
+typedef ssize_t  (*kdbGetPtr)(KDB *handle, KeySet *returned, const Key *parentKey);
+typedef ssize_t  (*kdbSetPtr)(KDB *handle, KeySet *returned, const Key *parentKey);
+
+typedef KDB* (*OpenMapper)(const char *,const char *,KeySet *);
+typedef int (*CloseMapper)(KDB *);
+
+
+
+/*****************
+ * Key Flags
+ *****************/
+
+/**
+ * Key Flags
+ *
+ * Flags are used to communicate between backends and the library.
+ * The flags are for internal reasons only, you should not need to
+ * modify them.
+ *
+ * Backend <-> Library
+ *
+ * There are three possible ways to use the flag,
+ * to communicate:
+ * - from backend to library (referred as in)
+ * - from library to backend (referred as out)
+ * - two-way communication (referred as in/out)
+ *
+ * @ingroup backend
+ */
+typedef enum
+{
+       KEY_FLAG_SYNC=1,        /*!< Key need sync. (in/out)
+               If value, comment or metadata
+               are changed this flag will be set, so that the backend will sync
+               the key to database.*/
+       KEY_FLAG_REMOVE=1<<1,   /*!< Key will be removed. (out)
+               The key is marked to be removed.*/
+       KEY_FLAG_STAT=1<<2      /*!< Only stat the key. (in)
+               don't get value and comment, if the backend supports it. */
+} keyflag_t;
+
+
+
+/*****************
+ * KeySet Flags
+ *****************/
+
+/*
+ * KeySet Flags
+ *
+ * @ingroup ks
+ */
+typedef enum
+{
+       KS_FLAG_DIRTY=1<<1      /*!< KeySet is dirty.
+               * KeySet will be sorted before next kdbSet() and ksLookupByName().*/
+} ksflag_t;
+
+
+/**
+ * The private Key struct.
+ *
+ * Its internal private attributes should not be accessed directly by regular
+ * programs. Use the @ref key "Key access methods" instead.
+ * Only a backend writer needs to have access to the private attributes of the
+ * Key object which is defined as:
+ * @code
+typedef struct _Key Key;
+ * @endcode
+ *
+ * @ingroup backend
+ */
+struct _Key {
+       /**
+        * Type of the value, from #type_t.
+        * @see keyGetType(), keySetType(), keyIsBin()
+        */
+       type_t         type;
+
+       /**
+        * System UID of this key.
+        * @see keyGetUID(), keySetUID()
+        */
+       uid_t          uid;
+
+       /**
+        * System GID of this key.
+        * @see keyGetGID(), keySetGID()
+        */
+       gid_t          gid;
+
+       /**
+        * File-like mode control
+        * @see keyGetMode(), keySetMode()
+        */
+       mode_t         mode;
+
+       /**
+        * Time for last access(stat).
+        * @see keyGetATime()
+        */
+       time_t         atime;
+
+       /**
+        * Time for last modification.
+        * @see keyGetMTime()
+        */
+       time_t         mtime;
+
+       /**
+        * Time for last change (meta info)
+        * @see keyGetCTime()
+        */
+       time_t         ctime;
+
+       /**
+        * A comment about the key.
+        * @see keySetComment(), keyGetComment()
+        */
+       char *         comment;
+
+       /**
+        * Size of the comment of description string, including ending NULL.
+        * @see keyGetCommentSize(), keySetComment(), keyGetComment()
+        */
+       size_t         commentSize;
+
+       /**
+        * The value, which is a NULL terminated string or binary.
+        * @see keyGetString(), keyGetBinary(), keySetRaw() */
+       void *         data;
+
+       /**
+        * Size of the value, in bytes, including ending NULL.
+        * @see keyGetCommentSize(), keySetComment(), keyGetComment()
+        */
+       size_t         dataSize;
+
+       /**
+        * The name of the key.
+        * @see keySetName(), keySetName()
+        */
+       char *         key;
+
+       /**
+        * Size of the name, in bytes, including ending NULL.
+        * @see keyGetName(), keyGetNameSize(), keySetName()
+        */
+       size_t         keySize;
+
+       /**
+        * The user that owns the key.
+        * @see keySetOwner(), keyGetOwner()
+        */
+       char *         owner;
+
+       /**
+        * Size of the owner, in bytes, including ending NULL.
+        * @see keyGetCommentSize(), keySetComment(), keyGetComment()
+        */
+       size_t         ownerSize;
+
+       /**
+        * Some control and internal flags.
+        */
+       keyflag_t      flags;
+
+       /**
+        * In how many keysets the key resists.
+        * keySetName() and keyRemove() are only allowed if ksReference is 0.
+        * @see ksPop(), ksAppendKey(), ksAppend()
+        */
+       size_t        ksReference;
+};
+
+
+/**
+ * Macro to calculates the real size in bytes of the metainfo of the Key struct.
+ * The metainfo part of the struct is composed by _Key::type, _Key::uid,
+ * _Key::gid, _Key::mode, _Key::atime, _Key::mtime, _Key::ctime,
+ * _Key::commentSize, _Key::dataSize, _Key::keySize, _Key::ownerSize
+ *
+ * This macro is usefull in Key serialization and de-serialization methods
+ * of your backend.
+ *
+ * @ingroup backend
+ */
+#define KEY_METAINFO_SIZE(k) (sizeof (k->type) + sizeof (k->uid) + sizeof (k->gid) +\
+               sizeof (k->mode) + sizeof (k->atime) + sizeof (k->mtime) + sizeof (k->ctime) +\
+               sizeof (k->commentSize) + sizeof (k->dataSize) + sizeof (k->keySize) +\
+               sizeof (k->ownerSize) )
+
+
+
+
+/**
+ * The private KeySet structure.
+ *
+ * Its internal private attributes should not be accessed directly by regular
+ * programs. Use the @ref keyset "KeySet access methods" instead.
+ * Only a backend writer needs to have access to the private attributes of the
+ * KeySet object which is defined as:
+ * @code
+typedef struct _KeySet KeySet;
+ * @endcode
+ *
+ * @ingroup backend
+ */
+struct _KeySet {
+       struct _Key **array;    /**<Array which holds the keys */
+
+       size_t        size;     /**< Number of keys contained in the KeySet */
+       size_t        rsize;    /**< Number of removed keys contained in the KeySet */
+       size_t        alloc;    /**< Allocated size of array */
+
+       struct _Key  *cursor;   /**< Internal cursor */
+       size_t        current;  /**< Current position of cursor */
+       ksflag_t      flags;    /**< Some control and internal flags. */
+};
+
+
+/**
+ * The private Kdb Capability structure.
+ *
+ * Its internal private attributes should not be accessed directly by regular
+ * programs. Use the @ref capability "KDBCapability access methods" instead.
+ * Only a backend writer needs to have access to the private attributes of the
+ * KeySet object which is defined as:
+ * @code
+typedef struct _KDBCap KDBCap;
+ * @endcode
+ * 
+ * @see kdbGetCapabilty()
+ * @see commandInfo() of the 'kdb info' command to see it in action
+ * @ingroup capability
+ */
+struct _KDBCap {
+        /* we'll point only to static strings. We won't allocate anything for each member. */
+       const char *version;            /*!< Version of the library. */
+       const char *name;               /*!< Name of backend being or that will be used. */
+       const char *description;        /*!< Any text describing the backend. */
+       const char *author;             /*!< The author of the backend. */
+       const char *licence;            /*!< The licence of the backend,
+                                 because of BSD licence even commercial is allowed. */
+       unsigned int onlyFullGet:1;     /*!< You can't get specific keys. */
+       unsigned int onlyRemoveAll:1;   /*!< You can only remove all keys at once. */
+       unsigned int onlyAddKeys:1;     /*!< When setting keys, they will be added. */
+       unsigned int onlyFullSet:1;     /*!< All keys need to be in keyset when setting. */
+       unsigned int onlySystem:1;      /*!< Only system namespace supported. */
+       unsigned int onlyUser:1;        /*!< Only user namespace supported. */
+       unsigned int noOwner:1;         /*!< The backend does not support a owner of keys. */
+       unsigned int noValue:1;         /*!< No value is supported in the backend. */
+       unsigned int noComment:1;       /*!< No comment is supported in the backend. */
+       unsigned int noUID:1;           /*!< No uid is supported in the backend. */
+       unsigned int noGID:1;           /*!< No gid is supported in the backend. */
+       unsigned int noMode:1;          /*!< No mode is supported in the backend. */
+       unsigned int noDir:1;           /*!< Directories are not supported in the backend. */
+       unsigned int noATime:1;         /*!< Mode Time not supported. */
+       unsigned int noMTime:1;         /*!< Modification Time not supported. */
+       unsigned int noCTime:1;         /*!< Meta Info Change Time not supported. */
+       unsigned int noRemove:1;        /*!< The backend does not support removing keys. */
+       unsigned int noStat:1;          /*!< When getting keys they can't be stated. */
+       unsigned int noMount:1;         /*!< Mount type not supported. */
+       unsigned int noBinary:1;        /*!< Binary types not supported. */
+       unsigned int noString:1;        /*!< String types not supported. */
+       unsigned int noTypes:1;         /*!< Typing of keys is not supported. */
+       unsigned int noError:1;         /*!< Don't expect errno to be set correctly. */
+       unsigned int noLock:1;          /*!< Backend does not lock. */
+       unsigned int noThread:1;        /*!< Backend uses global variables and is not threadsafe. */
+};
+
+/**
+ * The structure which holds all information of a loaded backend.
+ *
+ * Its internal private attributes should not be accessed directly by regular
+ * programs. Use the @ref capability "KDBCapability access methods" instead.
+ * Only a backend writer needs to have access to the private attributes of the
+ * KeySet object which is defined as:
+ * @code
+typedef struct _KDB KDB;
+ * @endcode
+ *
+ * @see kdbOpen() and kdbClose() for external use
+ * @ingroup backend
+ */
+struct _KDB {
+       Trie *trie;             /*!< The pointer to the trie holding other backends.
+               @see kdbhGetTrie() */
+
+       KeySet *config;         /*!< This keyset contains configuration for the backend.
+               Don't care about the absolute path, it may change when dynamically
+               kdbMount() or because of another name under system/elektra/mountpoints. 
+               The keys inside contain information like /path which path should be used
+               to write configuration to or /host to which host packets should be send.
+               @see kdbhGetConfig() */
+
+       void *backendData;      /*!< A general pointer for any data backend needs.
+               Thus backends are not allowed to have global variables for thread saftey,
+               they must use this pointer.
+               @see kdbhGetBackendData() */
+
+       Key *mountpoint;        /*!< The mountpoint where the backend resides.
+               The keyName() is the point where the backend was mounted.
+               The keyValue() is the name of the backend without pre/postfix, e.g.
+               filesys. */
+
+       KDBCap *capability;     /*!< The capabilites this backend declares to have.
+               @see kdbhGetCapability() */
+
+       kdbLibHandle dlHandle;  /*!< The pointer to the datastructure to load a new backend. */
+
+       /* These are the interfaces that must be implemented */
+
+       kdbOpenPtr kdbOpen;     /*!< The pointer to kdbOpen_template() of the backend. */
+       kdbClosePtr kdbClose;   /*!< The pointer to kdbClose_template() of the backend. */
+
+       kdbGetPtr kdbGet;       /*!< The pointer to kdbGet_template() of the backend. */
+       kdbSetPtr kdbSet;       /*!< The pointer to kdbSet_template() of the backend. */
+};
+
+
+/** The private trie structure.
+ *
+ * A trie is a data structure which can handle the longest prefix matching very
+ * fast. This is exactly what needs to be done when using kdbGet() and kdbSet()
+ * in a hierachy where backends are mounted - you need the backend mounted
+ * closest to the parentKey.
+ */
+struct _Trie {
+       struct _Trie *childs[MAX_UCHAR];/*!<  */
+       char *text[MAX_UCHAR];          /*!<  */
+       unsigned int textlen[MAX_UCHAR];/*!<  */
+       void *value[MAX_UCHAR];         /*!<  */
+       void *empty_value;              /*!< value for the empty string "" */
+};
+
+
+/** The private split structure.
+ *
+ * kdbSet() splits keysets. This structure contains arrays for
+ * various information needed to process the keysets afterwards.
+ */
+struct _Split {
+       size_t no;              /*!< Number of keysets */
+       size_t alloc;           /*!< How large the arrays are allocated  */
+       KeySet **keysets;       /*!< The keysets */
+       KDB **handles;  /*!< The KDB for the keyset */
+       Key **parents;          /*!< The parentkey for the keyset */
+       int *syncbits;          /*!< Is there any key in there which need to be synced? */
+       int *belowparents;      /*!< Is there any key in there which is below the parent? */
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***************************************
+ *
+ * Functions which might be reintroduced
+ *
+ **************************************/
+
+/*****************
+ * Namespaces
+ *****************/
+
+/*
+ * Elektra currently supported Key namespaces.
+ *
+ * @ingroup key
+ * @see kdbGet(), keyGetNamespace(), keyNameGetNamespace()
+ */
+enum KeyNamespace {
+       KEY_NS_SYSTEM=1,       /*!< The @p system keys */
+       KEY_NS_USER=2,          /*!< The @p user keys */
+       KEY_NS_MEMORY=3          /*!< The @p volatile keys */
+};
+
+int keyGetNamespace(const Key *key);
+int keyNameGetNamespace(const char *keyname);
+
+
+/*****************
+ * Misc Functions
+ *****************/
+
+keyswitch_t keyCompare(const Key *key1, const Key *key2);
+ssize_t ksGetCommonParentName(const KeySet *ks,char *returnedCommonParent,
+       size_t maxSize);
+
+
+
+/*****************
+ * Serialization
+ *****************/
+
+int keySerialize(const Key *key, void *serialized, size_t maxSize);
+Key *keyCompose(const void *serialized);
+Key *keyUnserialize(const void *serialized); 
+size_t keyGetSerializedSize(const Key *key);
+
+/*****************
+ * Allocation
+ *****************/
+
+int ksNeedSort(const KeySet *ks);
+int ksResize(KeySet *ks, size_t size);
+size_t ksGetAlloc(const KeySet *ks);
+
+
+/***************************************
+ *
+ * Not exported functions, for internal use only
+ *
+ **************************************/
+
+KDB* kdbOpenBackend(const char *backendname, const char *mountpoint, KeySet *config);
+int kdbCloseBackend(KDB *handle);
+
+ssize_t keySetRaw(Key *key, const void *newBinary, size_t dataSize);
+
+char *keyNameGetOneLevel(const char *keyname, size_t *size);
+
+ssize_t keyNameGetRootNameSize(const char *keyname);
+ssize_t keyNameGetBaseNameSize(const char *keyname);
+ssize_t keyNameGetFullRootNameSize(const char *keyname);
+
+int keyNameIsSystem(const char *keyname);
+int keyNameIsUser(const char *keyname);
+int keyNameIsMemory(const char *keyname);
+
+ssize_t keyGetRootNameSize(const Key *key);
+ssize_t keyGetRootName(const Key *key, char *returned, size_t maxSize);
+
+ssize_t keyGetFullRootNameSize(const Key *key);
+ssize_t keyGetFullRootName(const Key *key, char *returned, size_t maxSize);
+
+ssize_t keyGetParentName(const Key *key, char *returned, size_t maxSize);
+ssize_t keyGetParentNameSize(const Key *key);
+
+KDB *kdbGetBackend(KDB *handle, const Key *key);
+
+int kdbCreateTrie(KDB *handle, KeySet *ks, OpenMapper mapper);
+int kdbDelTrie(Trie *trie,CloseMapper close_backend);
+
+Trie *createTrie(KeySet *ks, OpenMapper mapper);
+Trie *delete_trie(Trie *trie, char *name, CloseMapper close_mapper);
+Trie *insert_trie(Trie *trie, const char *name, const void *value);
+
+void free_splitted_keysets(Split *keysets);
+void init_splitted_keysets(Split *ret);
+void resize_splitted_keysets(Split *ret);
+Split *split_keyset(KDB *handle, KeySet *ks,
+       Key *parentKey, unsigned long options);
+
+int kdbiStrCaseCmp (const char *s1, const char *s2);
+size_t kdbiStrLen(const char *s);
+
+void *kdbiMalloc (size_t size);
+void  kdbiFree (void *ptr);
+char *kdbiStrDup (const char *s);
+int kdbiRealloc(void **buffer, size_t size);
+
+ssize_t kdbbEncode(void *kdbbDecoded, size_t size, char *returned);
+ssize_t kdbbDecode(char *kdbbEncoded, void *returned);
+
+int kdbbNeedsUTF8Conversion(void);
+int kdbbUTF8Engine(int direction, char **string, size_t *inputByteSize);
+
+int kdbbEncodeChar(char c, char *buffer, size_t bufSize);
+int kdbbDecodeChar(const char *from, char *into);
+
+int kdbbFilenameToKeyName(const char *string, char *buffer, int bufSize);
+ssize_t kdbbGetFullKeyName (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned);
+int kdbbKeyNameToRelativeFilename(const char *string, char *buffer, size_t bufSize);
+ssize_t kdbbKeyCalcRelativeFilename(const Key *key,char *relativeFilename,size_t maxSize);
+ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize);
+
+int keyInit(Key *key);
+int keyClose(Key *key);
+
+int ksInit(KeySet *ks);
+int ksClose(KeySet *ks);
+
+/** Test a bit. @see set_bit(), clear_bit() */
+#define test_bit(var,bit)            ((var) &   (bit))
+/** Set a bit. @see clear_bit() */
+#define set_bit(var,bit)             ((var) |=  (bit))
+/** Clear a bit. @see set_bit() */
+#define clear_bit(var,bit)           ((var) &= ~(bit))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KDBPRIVATE_H */
+
+#endif /* KDB_H first check */
diff --git a/src/include/kdbtools.h b/src/include/kdbtools.h
new file mode 100644 (file)
index 0000000..d32b8c2
--- /dev/null
@@ -0,0 +1,111 @@
+/***************************************************************************
+                kdbtools.h  -  Elektra High Level methods
+                             -------------------
+    begin                : Sat Jan 22 2005
+    copyright            : (C) 2005 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifndef KDBTOOLS_H
+#define KDBTOOLS_H
+
+
+#include "kdb.h"
+
+
+/**
+ * Options to change the default behavior of streaming.
+ *
+ * On default the streaming options only output the names of the given
+ * keysets. If you want more information, header, metainfo, compressed
+ * output, full names, values or comments you will find the appropriate
+ * options here.
+ *
+ * For full influence of value, comment and metadata shown, use these
+ * options together with #keyswitch_t. All bits of meta-information ORed
+ * together are KDB_O_SHOWMETA.
+ *
+ * For more information about the flags, consult the documentation of
+ * the streaming methods.
+ *
+ * These options can be ORed. That is the |-Operator in C.
+ *
+ * It uses the values defined in #keyswitch_t too, so it starts with 14.
+ *
+ * @ingroup stream
+ * @see kdbGetChildKeys()
+ * @see ksToStream()
+ * @see keyToStream()
+ */
+enum KDBStream {
+       KDB_O_SHOWMETA=0xF0,            /*!< Show all metadata (type, uid, gid, mode) */
+       KDB_O_SHOWFLAGS=1<<14,          /*!< Show all flags */
+       KDB_O_SHOWINDICES=1<<15,        /*!< Show the indices for the entries */
+       KDB_O_CONDENSED=1<<16,          /*!< Spare any whitespaces and do not group visually together.*/
+       KDB_O_NUMBER=1<<17,             /*!< Use a number intead of user and group name.*/
+       KDB_O_HEADER=1<<18,             /*!< Show also the header of the document. */
+       KDB_O_FULLNAME=1<<19,           /*!< Export @p user keys using full name.*/
+       KDB_O_HIER=1<<20                /*!< Export to the new hierarchical XML
+                                               representation using key basename.
+                                               See ksToStream(). */
+};
+
+
+typedef int (*KSFromXMLfile)(KeySet *ks,const char *filename);
+typedef int (*KSFromXML)(KeySet *ks,int fd);
+typedef ssize_t (*output) (const KeySet *ks, FILE* stream, option_t options);
+
+
+#define KDB_SCHEMA_PATH_KEY   "system/elektra/xml/schemapath"
+
+#ifndef DYN_LINK
+
+#ifdef __cplusplus
+namespace ckdb {
+extern "C" {
+#endif
+
+
+int ksFromXMLfile(KeySet *ks,const char *filename);
+int ksFromXML(KeySet *ks,int fd);
+
+ssize_t ksToStream(const KeySet *ks, FILE* stream, option_t options);
+int ksOutput (const KeySet *ks, FILE *stream, option_t options);
+int ksGenerate (const KeySet *ks, FILE *stream, option_t options);
+
+ssize_t keyToStream(const Key *key, FILE *stream, option_t options);
+ssize_t keyToStreamBasename(const Key *key, FILE *stream,
+       const char *parent, const size_t parentSize, option_t options);
+
+int keyOutput (const Key *key, FILE *stream, option_t options);
+int keyGenerate (const Key *key, FILE *stream, option_t options);
+
+
+/*
+uint32_t ksLookupRE(KeySet *ks, uint32_t where,
+       const regex_t *regexp, option_t options);
+int ksCompare(KeySet *ks1, KeySet *ks2, KeySet *removed);
+*/
+
+#ifdef __cplusplus
+}
+}
+#endif
+
+#endif /* DYN_LINK */
+
+#endif /* KDBTOOLS_H */
diff --git a/src/include/ltdl.h b/src/include/ltdl.h
new file mode 100644 (file)
index 0000000..8aaf342
--- /dev/null
@@ -0,0 +1,366 @@
+/* ltdl.h -- generic dlopen functions
+   Copyright (C) 1998-2000 Free Software Foundation, Inc.
+   Originally by Thomas Tanner <tanner@ffii.org>
+   This file is part of GNU Libtool.
+
+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 of the License, or (at your option) any later version.
+
+As a special exception to the GNU Lesser General Public License,
+if you distribute this file as part of a program or library that
+is built using GNU libtool, you may include it under the same
+distribution terms that you use for the rest of that program.
+
+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, write to the Free
+Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301  USA
+*/
+
+/* Only include this header file once. */
+#ifndef LTDL_H
+#define LTDL_H 1
+
+#include <sys/types.h>         /* for size_t declaration */
+
+\f
+/* --- MACROS FOR PORTABILITY --- */
+
+
+/* Saves on those hard to debug '\0' typos....  */
+#define LT_EOS_CHAR    '\0'
+
+/* LTDL_BEGIN_C_DECLS should be used at the beginning of your declarations,
+   so that C++ compilers don't mangle their names.  Use LTDL_END_C_DECLS at
+   the end of C declarations. */
+#ifdef __cplusplus
+# define LT_BEGIN_C_DECLS      extern "C" {
+# define LT_END_C_DECLS                }
+#else
+# define LT_BEGIN_C_DECLS      /* empty */
+# define LT_END_C_DECLS                /* empty */
+#endif
+
+LT_BEGIN_C_DECLS
+
+
+/* LT_PARAMS is a macro used to wrap function prototypes, so that compilers
+   that don't understand ANSI C prototypes still work, and ANSI C
+   compilers can issue warnings about type mismatches.  */
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) || defined(__cplusplus)
+# define LT_PARAMS(protos)     protos
+# define lt_ptr                void*
+#else
+# define LT_PARAMS(protos)     ()
+# define lt_ptr                char*
+#endif
+
+/* LT_STMT_START/END are used to create macros which expand to a
+   a single compound statement in a portable way.  */
+#if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus)
+#  define LT_STMT_START        (void)(
+#  define LT_STMT_END          )
+#else
+#  if (defined (sun) || defined (__sun__))
+#    define LT_STMT_START      if (1)
+#    define LT_STMT_END        else (void)0
+#  else
+#    define LT_STMT_START      do
+#    define LT_STMT_END        while (0)
+#  endif
+#endif
+
+/* LT_CONC creates a new concatenated symbol for the compiler
+   in a portable way.  */
+#if defined(__STDC__) || defined(__cplusplus) || defined(_MSC_VER)
+#  define LT_CONC(s,t) s##t
+#else
+#  define LT_CONC(s,t) s/**/t
+#endif
+
+/* LT_STRLEN can be used safely on NULL pointers.  */
+#define LT_STRLEN(s)   (((s) && (s)[0]) ? strlen (s) : 0)
+
+
+\f
+/* --- WINDOWS SUPPORT --- */
+
+
+/* Canonicalise Windows and Cygwin recognition macros.  */
+#ifdef __CYGWIN32__
+#  ifndef __CYGWIN__
+#    define __CYGWIN__ __CYGWIN32__
+#  endif
+#endif
+#if defined(_WIN32) || defined(WIN32)
+#  ifndef __WINDOWS__
+#    ifdef _WIN32
+#      define __WINDOWS__ _WIN32
+#    else
+#      ifdef WIN32
+#        define __WINDOWS__ WIN32
+#      endif
+#    endif
+#  endif
+#endif
+
+
+#ifdef __WINDOWS__
+#  ifndef __CYGWIN__
+/* LT_DIRSEP_CHAR is accepted *in addition* to '/' as a directory
+   separator when it is set. */
+#    define LT_DIRSEP_CHAR     '\\'
+#    define LT_PATHSEP_CHAR    ';'
+#  endif
+#endif
+#ifndef LT_PATHSEP_CHAR
+#  define LT_PATHSEP_CHAR      ':'
+#endif
+
+/* DLL building support on win32 hosts;  mostly to workaround their
+   ridiculous implementation of data symbol exporting. */
+#ifndef LT_SCOPE
+#  ifdef __WINDOWS__
+#    ifdef DLL_EXPORT          /* defined by libtool (if required) */
+#      define LT_SCOPE __declspec(dllexport)
+#    endif
+#    ifdef LIBLTDL_DLL_IMPORT  /* define if linking with this dll */
+#      define LT_SCOPE extern __declspec(dllimport)
+#    endif
+#  endif
+#  ifndef LT_SCOPE             /* static linking or !__WINDOWS__ */
+#    define LT_SCOPE   extern
+#  endif
+#endif
+
+
+#if defined(_MSC_VER) /* Visual Studio */
+#  define R_OK 4
+#endif
+
+
+\f
+/* --- DYNAMIC MODULE LOADING API --- */
+
+
+typedef        struct lt_dlhandle_struct *lt_dlhandle; /* A loaded module.  */
+
+/* Initialisation and finalisation functions for libltdl. */
+LT_SCOPE       int         lt_dlinit           LT_PARAMS((void));
+LT_SCOPE       int         lt_dlexit           LT_PARAMS((void));
+
+/* Module search path manipulation.  */
+LT_SCOPE       int         lt_dladdsearchdir    LT_PARAMS((const char *search_dir));
+LT_SCOPE       int         lt_dlinsertsearchdir LT_PARAMS((const char *before,
+                                                   const char *search_dir));
+LT_SCOPE       int         lt_dlsetsearchpath   LT_PARAMS((const char *search_path));
+LT_SCOPE       const char *lt_dlgetsearchpath   LT_PARAMS((void));
+LT_SCOPE       int         lt_dlforeachfile     LT_PARAMS((
+                       const char *search_path,
+                       int (*func) (const char *filename, lt_ptr data),
+                       lt_ptr data));
+
+/* Portable libltdl versions of the system dlopen() API. */
+LT_SCOPE       lt_dlhandle lt_dlopen           LT_PARAMS((const char *filename));
+LT_SCOPE       lt_dlhandle lt_dlopenext        LT_PARAMS((const char *filename));
+LT_SCOPE       lt_ptr      lt_dlsym            LT_PARAMS((lt_dlhandle handle,
+                                                    const char *name));
+LT_SCOPE       const char *lt_dlerror          LT_PARAMS((void));
+LT_SCOPE       int         lt_dlclose          LT_PARAMS((lt_dlhandle handle));
+
+/* Module residency management. */
+LT_SCOPE       int         lt_dlmakeresident   LT_PARAMS((lt_dlhandle handle));
+LT_SCOPE       int         lt_dlisresident     LT_PARAMS((lt_dlhandle handle));
+
+
+
+\f
+/* --- MUTEX LOCKING --- */
+
+
+typedef void   lt_dlmutex_lock         LT_PARAMS((void));
+typedef void   lt_dlmutex_unlock       LT_PARAMS((void));
+typedef void   lt_dlmutex_seterror     LT_PARAMS((const char *errmsg));
+typedef const char *lt_dlmutex_geterror        LT_PARAMS((void));
+
+LT_SCOPE       int     lt_dlmutex_register     LT_PARAMS((lt_dlmutex_lock *lock,
+                                           lt_dlmutex_unlock *unlock,
+                                           lt_dlmutex_seterror *seterror,
+                                           lt_dlmutex_geterror *geterror));
+
+
+
+\f
+/* --- MEMORY HANDLING --- */
+
+
+/* By default, the realloc function pointer is set to our internal
+   realloc implementation which iself uses lt_dlmalloc and lt_dlfree.
+   libltdl relies on a featureful realloc, but if you are sure yours
+   has the right semantics then you can assign it directly.  Generally,
+   it is safe to assign just a malloc() and a free() function.  */
+LT_SCOPE  lt_ptr   (*lt_dlmalloc)      LT_PARAMS((size_t size));
+LT_SCOPE  lt_ptr   (*lt_dlrealloc)     LT_PARAMS((lt_ptr ptr, size_t size));
+LT_SCOPE  void    (*lt_dlfree)         LT_PARAMS((lt_ptr ptr));
+
+
+
+\f
+/* --- PRELOADED MODULE SUPPORT --- */
+
+
+/* A preopened symbol. Arrays of this type comprise the exported
+   symbols for a dlpreopened module. */
+typedef struct {
+  const char *name;
+  lt_ptr      address;
+} lt_dlsymlist;
+
+LT_SCOPE       int     lt_dlpreload    LT_PARAMS((const lt_dlsymlist *preloaded));
+LT_SCOPE       int     lt_dlpreload_default
+                               LT_PARAMS((const lt_dlsymlist *preloaded));
+
+#define LTDL_SET_PRELOADED_SYMBOLS()           LT_STMT_START{  \
+       extern const lt_dlsymlist lt_preloaded_symbols[];               \
+       lt_dlpreload_default(lt_preloaded_symbols);                     \
+                                               }LT_STMT_END
+
+
+
+\f
+/* --- MODULE INFORMATION --- */
+
+
+/* Read only information pertaining to a loaded module. */
+typedef        struct {
+  char *filename;              /* file name */
+  char *name;                  /* module name */
+  int  ref_count;              /* number of times lt_dlopened minus
+                                  number of times lt_dlclosed. */
+} lt_dlinfo;
+
+LT_SCOPE       const lt_dlinfo *lt_dlgetinfo       LT_PARAMS((lt_dlhandle handle));
+LT_SCOPE       lt_dlhandle     lt_dlhandle_next    LT_PARAMS((lt_dlhandle place));
+LT_SCOPE       int             lt_dlforeach        LT_PARAMS((
+                               int (*func) (lt_dlhandle handle, lt_ptr data),
+                               lt_ptr data));
+
+/* Associating user data with loaded modules. */
+typedef unsigned lt_dlcaller_id;
+
+LT_SCOPE       lt_dlcaller_id  lt_dlcaller_register  LT_PARAMS((void));
+LT_SCOPE       lt_ptr          lt_dlcaller_set_data  LT_PARAMS((lt_dlcaller_id key,
+                                               lt_dlhandle handle,
+                                               lt_ptr data));
+LT_SCOPE       lt_ptr          lt_dlcaller_get_data  LT_PARAMS((lt_dlcaller_id key,
+                                               lt_dlhandle handle));
+
+
+\f
+/* --- USER MODULE LOADER API --- */
+
+
+typedef        struct lt_dlloader      lt_dlloader;
+typedef lt_ptr                 lt_user_data;
+typedef lt_ptr                 lt_module;
+
+/* Function pointer types for creating user defined module loaders. */
+typedef lt_module   lt_module_open     LT_PARAMS((lt_user_data loader_data,
+                                           const char *filename));
+typedef int        lt_module_close     LT_PARAMS((lt_user_data loader_data,
+                                           lt_module handle));
+typedef lt_ptr     lt_find_sym         LT_PARAMS((lt_user_data loader_data,
+                                           lt_module handle,
+                                           const char *symbol));
+typedef int        lt_dlloader_exit    LT_PARAMS((lt_user_data loader_data));
+
+struct lt_user_dlloader {
+  const char          *sym_prefix;
+  lt_module_open       *module_open;
+  lt_module_close      *module_close;
+  lt_find_sym         *find_sym;
+  lt_dlloader_exit     *dlloader_exit;
+  lt_user_data         dlloader_data;
+};
+
+LT_SCOPE       lt_dlloader    *lt_dlloader_next    LT_PARAMS((lt_dlloader *place));
+LT_SCOPE       lt_dlloader    *lt_dlloader_find    LT_PARAMS((
+                                               const char *loader_name));
+LT_SCOPE       const char     *lt_dlloader_name    LT_PARAMS((lt_dlloader *place));
+LT_SCOPE       lt_user_data   *lt_dlloader_data    LT_PARAMS((lt_dlloader *place));
+LT_SCOPE       int             lt_dlloader_add     LT_PARAMS((lt_dlloader *place,
+                               const struct lt_user_dlloader *dlloader,
+                               const char *loader_name));
+LT_SCOPE       int             lt_dlloader_remove  LT_PARAMS((
+                                               const char *loader_name));
+
+
+\f
+/* --- ERROR MESSAGE HANDLING --- */
+
+
+/* Defining error strings alongside their symbolic names in a macro in
+   this way allows us to expand the macro in different contexts with
+   confidence that the enumeration of symbolic names will map correctly
+   onto the table of error strings.  */
+#define lt_dlerror_table                                               \
+    LT_ERROR(UNKNOWN,              "unknown error")                    \
+    LT_ERROR(DLOPEN_NOT_SUPPORTED,  "dlopen support not available")    \
+    LT_ERROR(INVALID_LOADER,       "invalid loader")                   \
+    LT_ERROR(INIT_LOADER,          "loader initialization failed")     \
+    LT_ERROR(REMOVE_LOADER,        "loader removal failed")            \
+    LT_ERROR(FILE_NOT_FOUND,       "file not found")                   \
+    LT_ERROR(DEPLIB_NOT_FOUND,      "dependency library not found")    \
+    LT_ERROR(NO_SYMBOLS,           "no symbols defined")               \
+    LT_ERROR(CANNOT_OPEN,          "can't open the module")            \
+    LT_ERROR(CANNOT_CLOSE,         "can't close the module")           \
+    LT_ERROR(SYMBOL_NOT_FOUND,      "symbol not found")                        \
+    LT_ERROR(NO_MEMORY,                    "not enough memory")                \
+    LT_ERROR(INVALID_HANDLE,       "invalid module handle")            \
+    LT_ERROR(BUFFER_OVERFLOW,      "internal buffer overflow")         \
+    LT_ERROR(INVALID_ERRORCODE,     "invalid errorcode")               \
+    LT_ERROR(SHUTDOWN,             "library already shutdown")         \
+    LT_ERROR(CLOSE_RESIDENT_MODULE, "can't close resident module")     \
+    LT_ERROR(INVALID_MUTEX_ARGS,    "invalid mutex handler registration") \
+    LT_ERROR(INVALID_POSITION,     "invalid search path insert position")
+
+/* Enumerate the symbolic error names. */
+enum {
+#define LT_ERROR(name, diagnostic)     LT_CONC(LT_ERROR_, name),
+       lt_dlerror_table
+#undef LT_ERROR
+
+       LT_ERROR_MAX
+};
+
+/* These functions are only useful from inside custom module loaders. */
+LT_SCOPE       int     lt_dladderror   LT_PARAMS((const char *diagnostic));
+LT_SCOPE       int     lt_dlseterror   LT_PARAMS((int errorcode));
+
+
+
+\f
+/* --- SOURCE COMPATIBILITY WITH OLD LIBLTDL --- */
+
+
+#ifdef LT_NON_POSIX_NAMESPACE
+#  define lt_ptr_t             lt_ptr
+#  define lt_module_t          lt_module
+#  define lt_module_open_t     lt_module_open
+#  define lt_module_close_t    lt_module_close
+#  define lt_find_sym_t                lt_find_sym
+#  define lt_dlloader_exit_t   lt_dlloader_exit
+#  define lt_dlloader_t                lt_dlloader
+#  define lt_dlloader_data_t   lt_user_data
+#endif
+
+LT_END_C_DECLS
+
+#endif /* !LTDL_H */
diff --git a/src/kdb/.deps/kdb-BSDgetopt.Po b/src/kdb/.deps/kdb-BSDgetopt.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/kdb/.deps/kdb-help.Po b/src/kdb/.deps/kdb-help.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/kdb/.deps/kdb-kdb-tool.Po b/src/kdb/.deps/kdb-kdb-tool.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/kdb/.deps/kdb_static-BSDgetopt.Po b/src/kdb/.deps/kdb_static-BSDgetopt.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/kdb/.deps/kdb_static-help.Po b/src/kdb/.deps/kdb_static-help.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/kdb/.deps/kdb_static-kdb-tool.Po b/src/kdb/.deps/kdb_static-kdb-tool.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/kdb/BSDgetopt.c b/src/kdb/BSDgetopt.c
new file mode 100644 (file)
index 0000000..3e8e105
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "BSDgetopt.h"
+
+#ifndef HASGETOPT
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: getopt.c,v 1.5 2003/06/02 20:18:37 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define        BADCH   (int)'?'
+#define        BADARG  (int)':'
+#define        EMSG    ""
+
+int BSDopterr = 1;             /* if error message should be printed */
+int BSDoptind = 1;             /* index into parent argv vector */
+/*
+ * getopt --
+ *     Parse argc/argv argument vector.
+ */
+int
+BSDgetopt(nargc, nargv, ostr)
+       int nargc;
+       char * const *nargv;
+       const char *ostr;
+{
+       extern char *__progname;
+       static char *place = EMSG;              /* option letter processing */
+       char *oli;                              /* option letter list index */
+
+       if (ostr == NULL)
+               return (-1);
+
+       if (BSDoptreset || !*place) {           /* update scanning pointer */
+               BSDoptreset = 0;
+               if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') {
+                       place = EMSG;
+                       return (-1);
+               }
+               if (place[1] && *++place == '-') {      /* found "--" */
+                       ++BSDoptind;
+                       place = EMSG;
+                       return (-1);
+               }
+       }                                       /* option letter okay? */
+       if ((BSDoptopt = (int)*place++) == (int)':' ||
+           !(oli = strchr(ostr, BSDoptopt))) {
+               /*
+                * if the user didn't specify '-' as an option,
+                * assume it means -1.
+                */
+               if (BSDoptopt == (int)'-')
+                       return (-1);
+               if (!*place)
+                       ++BSDoptind;
+               if (BSDopterr && *ostr != ':')
+                       (void)fprintf(stderr,
+                           "%s: illegal option -- %c\n", __progname, BSDoptopt);
+               return (BADCH);
+       }
+       if (*++oli != ':') {                    /* don't need argument */
+               BSDoptarg = NULL;
+               if (!*place)
+                       ++BSDoptind;
+       }
+       else {                                  /* need an argument */
+               if (*place)                     /* no white space */
+                       BSDoptarg = place;
+               else if (nargc <= ++BSDoptind) {        /* no arg */
+                       place = EMSG;
+                       if (*ostr == ':')
+                               return (BADARG);
+                       if (BSDopterr)
+                               (void)fprintf(stderr,
+                                   "%s: option requires an argument -- %c\n",
+                                   __progname, BSDoptopt);
+                       return (BADCH);
+               }
+               else                            /* white space */
+                       BSDoptarg = nargv[BSDoptind];
+               place = EMSG;
+               ++BSDoptind;
+       }
+       return (BSDoptopt);                     /* dump back option letter */
+}
+
+#endif 
diff --git a/src/kdb/BSDgetopt.h b/src/kdb/BSDgetopt.h
new file mode 100644 (file)
index 0000000..04d6284
--- /dev/null
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */
+
+#ifndef KDB_BSDGETOPT_H
+#define KDB_BSDGETOPT_H
+
+# define getopt(ac, av, o)  BSDgetopt(ac, av, o)
+# define opterr             BSDopterr
+# define optind             BSDoptind
+# define optopt             BSDoptopt
+# define optreset           BSDoptreset
+# define optarg             BSDoptarg
+
+int BSDgetopt(int argc, char * const *argv, const char *opts);
+
+int    BSDopterr,              /* if error message should be printed */
+       BSDoptind,              /* index into parent argv vector */
+       BSDoptopt,              /* character checked for validity */
+       BSDoptreset;            /* reset getopt */
+char   *BSDoptarg;             /* argument associated with option */
+
+#endif
diff --git a/src/kdb/Makefile.am b/src/kdb/Makefile.am
new file mode 100644 (file)
index 0000000..ba443ca
--- /dev/null
@@ -0,0 +1,21 @@
+
+# $Id$
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LTDLINCL)
+
+kdb_source = kdb-tool.c kdb-tool.h help.c BSDgetopt.c BSDgetopt.h ../include/kdb.h ../include/kdbloader.h ../include/kdbtools.h
+
+bin_PROGRAMS = kdb kdb_static
+kdb_SOURCES = $(kdb_source)
+kdb_LDADD = ../libelektra/libelektra.la $(LIBLTDL) $(LIBICONV)
+kdb_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+kdb_static_SOURCES =  $(kdb_source)
+kdb_static_LDADD = $(privatelibs) ../libelektra/libelektra.a $(LIBICONV)
+kdb_static_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+../libelektra/libelektra.a:
+       cd ../libelektra && $(MAKE) libelektra.a
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
diff --git a/src/kdb/help.c b/src/kdb/help.c
new file mode 100755 (executable)
index 0000000..a4d36eb
--- /dev/null
@@ -0,0 +1,263 @@
+/***************************************************************************
+                          help.c  -  Functions used by 'kdb help' command
+                             -------------------
+    begin                : Sat Jul 22 2006
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <kdb-tool.h>
+
+
+/**
+ * Prints out help to the kdb (1) command.
+ * --help does not work
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb help
+ * bash$ kdb -h
+ * @endcode
+ */
+int commandHelp() {
+       printf("Usage: kdb [OPTION] <command> [<key> [value ...]]\n");
+       printf("Use kdb to manipulate the Key Database.\n");
+       printf("\n");
+
+       printf("libelektra " VERSION " is used\n");
+       printf("\n");
+
+       printf("ARGUMENTS\n");
+       printf("Commands are explained with kdb -h command\n");
+       printf("<key> is the name of the key. It can be prefixed\n");
+       printf(" with environment KDB_ROOT. The slash between will\n");
+       printf("be inserted.\n");
+       printf(" export KDB_ROOT=\"user/test/dir\"\n");
+       printf(" kdb get file/key ... will expand to user/test/dir/file/key\n");
+       printf("[value ...] hold the value which should be set\n");
+       printf("\n");
+       
+       printf("COMMANDS\n");
+       printf(" kdb get [-dlfs] key/name\n");
+       printf(" kdb set [-t type] [-c \"A comment about this key\"] [-m mode] [-u uid]\n");
+       printf("         [-g gid] key/name \"the value\"\n");
+#ifdef TUNNING_ELEKTRA_0_7
+       printf(" **<type> is string, int, double and bool(value of bool is [1 or 0]\n");
+#endif
+       printf(" kdb set [-t type] [-m mode] [-c \"A comment\"] key/name -- \"the value\"\n");
+       printf(" kdb set [-t type] [-b file] key/name\n");
+       printf(" kdb ls [-lRfvs] [key/dir | key/name]\n");
+       printf(" kdb ls [-lRfvx] [key/dir | key/name] > keys.xml\n");
+       printf(" kdb edit [-R] [key/dir | key/name]\n");
+       printf(" kdb rm key/name\n");
+       printf(" kdb mv key/src key/dest\n");
+       printf(" kdb ln key/src key/dest\n");
+       printf(" kdb export system/some/tree.root > file.xml\n");
+       printf(" kdb import < file.xml\n");
+       printf(" kdb import file.xml\n");
+       printf(" kdb monitor some/key/name\n");
+       printf(" kdb info\n");
+       printf("\n");
+       
+       return 0;
+}
+
+void optionr() {
+       printf("-R -r\n");
+       printf(" Causes to work recursively. In ls, will list recursively. \n");
+       printf("\n");
+}
+
+void optionx() {
+       printf("-x\n");
+       printf(" Makes ls output an XML representation of the keys, instead of an ls-compatible output. \n");
+       printf("\n");
+}
+
+void optionl() {
+       printf("-l\n");
+       printf(" Causes to display long results. With ls, will generate lists similar to ls -l. With get, will show also the key name. \n");
+       printf("\n");
+}
+
+void optiona() {
+       printf("-a\n");
+       printf(" Causes ls to display also inactive keys. Generate lists similar to ls -a. Inactive keys are keys which basename begins with a '.' (dot). An example of inactive key: system/sw/XFree/current/Monitor/.Monitor1 \n");
+       printf("\n");
+}
+
+void optionf() {
+       printf("-f\n");
+       printf(" Causes to work with full key names. A full key name makes sense only on user/* keys, and differentiate from the regular key names in specifying the owner user. If the current user is someuser, the user/some/key full name is user:someuser/some/key. Makes effect in ls, export and get subcommands. \n");
+       printf("\n");
+}
+
+void optiond() {
+       printf("-d\n");
+       printf(" Causes get to work descriptivelly. When requesting a key it will show the comment, key name and its value in a fancy format \n");
+       printf("\n");
+}
+
+void options() {
+       printf("-s\n");
+       printf(" Causes get and ls to be more friendly to Shell scripts. For example, when requesting user/env/env2/PATH, the output will be PATH=\"the value\", that is, only the basename of the key will be showed and the value will be surrounded by  \".\n");
+       printf("\n");
+}
+
+void optiont() {
+       printf("-t type\n");
+       printf("When setting a key's value, you can specify the type with this switch.\n");
+       printf("Currently accepted types are:\n");
+       printf("  string for plain text\n");
+       printf("  binary for binary as-is values, should be avoided\n");
+       printf("  link to create symbolic links between keys\n");
+       printf("  dir to create folder keys, is obsolete now, every key can contain subkeys\n");
+       printf("Plain text are always stored as UTF-8(7) in Elektra, regardeless of your current encoding ($LANG). Binary values should be avoided, because they are black boxes for system administrators. \n");
+       printf("\n");
+}
+
+void optionb() {
+       printf("-b filename\n");
+       printf(" Set the key value as the content of file filename. This option is more usefull when setting binary keys. \n");
+       printf("\n");
+}
+
+void optionm() {
+       printf("-m mode\n");
+       printf(" For the set command. Will set the key mode permission to mode, which must be an octal number as for chmod(1). \n");
+       printf("\n");
+}
+
+void optionu() {
+       printf("-u uid\n");
+       printf(" Create the key with uid user ID. It can be a user name or a uid number. \n");
+       printf("\n");
+}
+
+void optiong() {
+       printf("-g gid\n");
+       printf(" Create the key with gid group ID. It can be a group name or a gid number \n");
+       printf("\n");
+}
+
+void optionc() {
+       printf("-c comment\n");
+       printf(" When setting keys, you can use this argument to set a descriptive comment for it. This comment is exactly as a comment in a plain text configuration file. The comment is stored as UTF-8(7) regardeless of your current encoding ($LANG). \n");
+       printf("\n");
+}
+
+void optionv() {
+       printf("-v\n");
+       printf(" With the ls subcommand, will make it show also the value stored in the key. \n");
+       printf("\n");
+}
+
+void commandGetHelp () {
+       printf("get\n");
+       printf(" Get the value from the specified key. Accepts options: -d, -l, -f, -s \n");
+       printf("\n");
+       optiond();
+       optionl();
+       optionf();
+       options();
+}
+
+void commandSetHelp () {
+       printf("set\n");
+       printf(" Set the value to the specified key. Accepts options: -c, -t, -m, -b \n");
+       printf("\n");
+       optionc();      
+       optiont();      
+       optionm();      
+       optionb();      
+}
+
+void commandListHelp () {
+       printf("ls\n");
+       printf(" As the ls(1) command, list key names for the specified key, or children keys, if specified a folder key. The -v argument will make it show also the values of each key. The -d (descriptive) will make it show the comment, key name and its value, as you are watching a plain text file. Accepts options: -x, -d, -l, -f, -v, -R, -s \n");
+       printf("\n");
+       optionx();
+       optiond();
+       optionl();
+       optionf();
+       optionv();
+       optionr();
+}
+
+void commandLinkHelp () {
+       printf("ln\n");
+       printf("Creates a key that is a symbolic links to another key. \n");
+       printf("\n");
+}
+
+void commandMoveHelp() {
+       printf("mv\n");
+       printf("Move, or renames a key. Currently it can't move keys across different filesystems.\n");
+       printf("\n");
+}
+
+void commandRemoveHelp() {
+       printf("rm\n");
+       printf("As the rm(1) command, removes the key specified. \n");
+       printf("\n");
+}
+
+void commandEditHelp() {
+       printf("edit\n");
+       printf("A very powerfull subcommand that lets you edit an XML representation of the keys. The parameters it accepts is usually a parent key, so its child keys will be gathered. Can be used with the -R flag to work recursively. The editor used is the one set in the $EDITOR environment variable, or vi. After editing the keys, kdb edit will analyze them and commit only the changed keys, remove the keys removed, and add the keys added. \n");
+       printf("\n");
+}
+
+void commandExportHelp() {
+       printf("export, save \n");
+       printf("Export a subtree of keys to XML. If no subtree is defined right after the export command, system and current user trees will be exported. Output is written to standard output. The output encoding will always be UTF-8, regardeless of your system encoding. UTF-8 is the most universal charset you can get when exchanging data between multiple systems. Accepts -f. \n");
+       printf("\n");
+       optionf();      
+}
+
+void commandImportHelp() {
+       printf("import, load \n");
+       printf("Import an XML representation of keys and save it to the keys database. If no filename is passed right after the import command, standard input is used. \n");
+       printf("\n");
+}
+
+void commandInfoHelp() {
+       printf("info\n");
+       printf("Displays some information about the Elektra library being used, version, backends, etc.\n");
+       printf("\n");
+}
+
+void commandMonitorHelp() {
+       printf("monitor, mon, \n");
+       printf("Monitor a key for some value change. It will block your command line until a change in the key value is detected, then return its new value.\n");
+       printf("\n");
+}
+
+
+int helpCommand(int command) {
+       switch (command) {
+               case CMD_SET:             commandSetHelp();     break;
+               case CMD_LIST:            commandListHelp();    break;
+               case CMD_LINK:            commandLinkHelp();    break;
+               case CMD_GET:             commandGetHelp();     break;
+               case CMD_REMOVE:          commandRemoveHelp();  break;
+               case CMD_EDIT:            commandEditHelp();    break;
+               case CMD_LOAD:            commandImportHelp();  break;
+               case CMD_SAVE:            commandExportHelp();  break;
+               case CMD_MONITOR:         commandMonitorHelp(); break;
+               case CMD_MOVE:            commandMoveHelp();    break;
+               case CMD_INFO:            commandInfoHelp();    break;
+       }
+       exit (0);
+}
+
+/**
+ * @}
+ */
diff --git a/src/kdb/kdb-tool.c b/src/kdb/kdb-tool.c
new file mode 100755 (executable)
index 0000000..ec774f4
--- /dev/null
@@ -0,0 +1,1492 @@
+/***************************************************************************
+                kdb-tool.c  -  Tool for the kdb administration
+                             -------------------
+    begin                : Mon Mar 02 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <kdb-tool.h>
+
+/*
+ * @defgroup libexample  The kdb Command Source Code: Example of Full Library Utilization
+ * @{
+ */
+
+char *argComment=0;
+char *argFile=0;
+char *argData=0;
+char *argKeyName=0;
+char *argDomain=0;
+uid_t *argUID=0;
+uid_t *argGID=0;
+int argCommand=0;
+int argNoRecursive=KDB_O_NORECURSIVE;
+int argLong=0;
+int argValue=0;
+int argAll=0;
+int argSort=0;
+int argDescriptive=0;
+int argFullName=0;
+int argShell=0;
+int argGenerate=0;
+int argOutput=0;
+int argXML=0;
+int argDir=0;
+int argHelp=0;
+mode_t argMode=0;
+int argType=KEY_TYPE_UNDEFINED;
+
+
+/* We'll load this methods dynamically to avoid libxml dependencies */
+KSFromXMLfile ksFromXMLfile;
+KSFromXML ksFromXML;
+
+output ksToStream;
+output ksOutput;
+output ksGenerate;
+
+
+
+/*
+ * Prints an error message related to @c errno on standard error, prefixed by @p msg.
+ * @see kdbStrError()
+ * @ingroup kdb
+ */
+int kdbPrintError(const char * msg) {
+       fprintf (stderr, "%s\n", msg);
+       return 0;
+}
+
+/**
+ * Uses getopt to parse commandline options
+ **/
+int parseCommandLine(int argc, char *argv[]) {
+       char sargType[ARGSIZE],argUser[ARGSIZE],argGroup[ARGSIZE];
+       char sargMode[ARGSIZE],sargCommand[ARGSIZE];
+       char * keyEnv;
+       size_t keyEnvLength=0, keyOptLength=0, keyOldLength;
+
+       int opt;
+
+       *sargType=*argUser=*argGroup=*sargCommand=*sargMode=0;
+
+       while ((opt=getopt(argc,argv,"ab:c:dfg:Ghilm:nOrRst:u:vXx"))!=-1)
+       {
+               if (opt == EOF)
+                       break;
+               switch (opt)
+               {
+               case 'a':
+                       argAll=1;
+                       break;
+               case 'b':
+                       argFile=realloc(argFile,strlen(optarg)+1);
+                       assert(argFile!=NULL);
+                       strcpy(argFile,optarg);
+                       break;
+               case 'c':
+                       argComment=realloc(argComment,strlen(optarg)+1);
+                       assert(argComment!=NULL);
+                       strcpy(argComment,optarg);
+                       break;
+               case 'd':
+                       argDescriptive=1;
+                       argLong=1;
+                       argDir=1;
+                       break;
+               case 'f':
+                       argFullName=1;
+                       break;
+               case 'g':
+                       strncpy(argGroup,optarg,ARGSIZE);
+                       break;
+               case 'G':
+                       argGenerate=1;
+                       break;
+               case 'h':
+                       argHelp=1;
+                       break;
+               case 'l':
+                       argLong=1;
+                       break;
+               case 'm':
+                       strncpy(sargMode,optarg,ARGSIZE);
+                       break;
+               case 'n':
+                       argSort=1;
+                       break;
+               case 'O':
+                       argOutput=1;
+                       break;
+               case 'R':
+               case 'r':
+                       argNoRecursive=0;
+                       break;
+               case 's':
+                       argShell=1;
+                       break;
+               case 't':
+                       strncpy(sargType,optarg,ARGSIZE);
+                       break;
+               case 'u':
+                       strncpy(argUser,optarg,ARGSIZE);
+                       break;
+               case 'v':
+                       argValue=1;
+                       break;
+               case 'X':
+               case 'x':
+                       argXML=1;
+                       break;
+               default:
+                       fprintf(stderr, "Unknown error (%d %c) in parsing arguments\n",
+                                       opt,opt);
+                       break;
+               }
+       }
+
+       if (optind < argc) /*parse command*/
+       {
+               strncpy(sargCommand,argv[optind],ARGSIZE);
+               optind ++;
+       } else {
+               commandHelp();
+               exit(0);
+       }
+
+       keyEnv = getenv ("KDB_ROOT");
+       if (keyEnv) keyEnvLength = strlen (keyEnv) + 1;
+       else keyEnvLength = 0;
+       if (optind < argc) /*parse key name*/
+       {
+               keyOptLength = strlen (argv[optind]) + 1;
+               argKeyName=realloc(argKeyName,
+                       keyEnvLength + keyOptLength + 1);
+               assert(argKeyName!=NULL);
+               if (keyEnv) strncpy (argKeyName, keyEnv,   keyEnvLength);
+               strncpy(argKeyName + keyEnvLength, argv[optind], keyOptLength);
+               if (keyEnv) *(argKeyName+keyEnvLength-1) = '/';
+               optind ++;
+       } else if (keyEnv) {
+               argKeyName=realloc(argKeyName, keyEnvLength + 1);
+               assert(argKeyName!=NULL);
+               if (keyEnv) strncpy (argKeyName, keyEnv, keyEnvLength);
+       }
+
+       keyOptLength = 0;
+       keyOldLength = 0;
+       while (optind < argc) /* parse value (rest of arguments) */
+       {
+               keyOptLength += strlen(argv[optind]) + 1;
+               argData=realloc(argData,keyOptLength + 1);
+               assert(argData!=NULL);
+               if (keyOldLength > 0) *(argData+keyOldLength-1) = ' ';
+               strcpy(argData+keyOldLength, argv[optind]);
+               optind ++;
+               keyOldLength = keyOptLength;
+       }
+
+       /* see if parsing worked:
+       fprintf (stderr, "command: %s\n", sargCommand);
+       fprintf (stderr, "key name: %s\n", argKeyName);
+       fprintf (stderr, "value: %s\n", argData);
+       exit (0);
+       */
+               
+       /* End of command line argument reading. Now parse and finalize */
+
+       /* Check parsed command */
+       if (!strcmp(sargCommand,"ls"))           argCommand=CMD_LIST;
+       else if (!strcmp(sargCommand,"set"))     argCommand=CMD_SET;
+       else if (!strcmp(sargCommand,"get"))     argCommand=CMD_GET;
+       else if (!strcmp(sargCommand,"rm"))      argCommand=CMD_REMOVE;
+       else if (!strcmp(sargCommand,"vi"))      argCommand=CMD_EDIT;
+       else if (!strcmp(sargCommand,"edit"))    argCommand=CMD_EDIT;
+       else if (!strcmp(sargCommand,"load"))    argCommand=CMD_LOAD;
+       else if (!strcmp(sargCommand,"import"))  argCommand=CMD_LOAD;
+       else if (!strcmp(sargCommand,"save"))    argCommand=CMD_SAVE;
+       else if (!strcmp(sargCommand,"export"))  argCommand=CMD_SAVE;
+       else if (!strcmp(sargCommand,"mon"))     argCommand=CMD_MONITOR;
+       else if (!strcmp(sargCommand,"monitor")) argCommand=CMD_MONITOR;
+       else if (!strcmp(sargCommand,"mv"))      argCommand=CMD_MOVE;
+       else if (!strcmp(sargCommand,"info"))    argCommand=CMD_INFO;
+       else if (!strcmp(sargCommand,"help"))    argCommand=CMD_HELP;
+       else {
+               fprintf(stderr,"kdb: Invalid subcommand.\n");
+               return 1;
+       }
+
+       /* Parse type */
+       if (*sargType!=0) {
+               /* TODO: use regex */
+               if      (!strcmp(sargType,"string")) argType=KEY_TYPE_STRING;
+#ifdef TUNNING_ELEKTRA_0_7
+      else if (!strcmp(sargType,"int"))    argType=KEY_TYPE_STRING+1;
+      else if (!strcmp(sargType,"double")) argType=KEY_TYPE_STRING+2;
+      else if (!strcmp(sargType,"bool")) argType=KEY_TYPE_STRING+3;
+#endif
+               else if (!strcmp(sargType,"bin"))    argType=KEY_TYPE_BINARY;
+               else if (!strcmp(sargType,"binary")) argType=KEY_TYPE_BINARY;
+               else if (!strcmp(sargType,"dir"))    argDir=1; /* bkwrds compatibility */
+               else {
+                       argType=strtol(sargType,0,10);
+                       if (errno == ERANGE || errno == EINVAL)
+                               /* handle undefined later */
+                               argType=KEY_TYPE_UNDEFINED;
+               }
+       } else if (argCommand==CMD_SET) { /* We must have a type */
+               argType=KEY_TYPE_STRING;
+       }
+
+#ifdef HAVE_PWD_H
+       /* Parse UID */
+       if (*argUser) {
+               if (isdigit(*argUser)) {
+                       argUID=malloc(sizeof(uid_t));
+                       *argUID=atoi(argUser);
+               } else {
+                       struct passwd *pwd;
+                       pwd=getpwnam(argUser);
+                       if (pwd) {
+                               argUID=malloc(sizeof(uid_t));
+                               *argUID=pwd->pw_uid;
+                       } else {
+                               fprintf(stderr,"kdb: Invalid user \'%s\'. Ignoring\n", argUser);
+                       }
+               }
+       }
+#endif
+#ifdef HAVE_GRP_H
+       /* Parse GID */
+       if (*argGroup) {
+               if (isdigit(*argGroup)) {
+                       argGID=malloc(sizeof(gid_t));
+                       *argGID=atoi(argGroup);
+               } else {
+                       struct group *grp;
+                       grp=getgrnam(argGroup);
+                       if (grp) {
+                               argGID=malloc(sizeof(gid_t));
+                               *argGID=grp->gr_gid;
+                       } else {
+                               fprintf(stderr,"kdb: Invalid group \'%s\'. Ignoring\n",argGroup);
+                       }
+               }
+       }
+#endif
+
+
+       /* Parse permissions */
+       if (*sargMode!=0) argMode=strtol(sargMode,0,8);
+
+       return argCommand;
+}
+
+
+
+
+
+
+/*
+ * Helper for the 'kdb ls' command
+ *
+ */
+int listMode(Key *key,char *readable) {
+       mode_t mode=keyGetMode(key);
+
+       if (keyIsDir(key)) readable[0]='d';
+       else readable[0]='-';
+
+       readable[1] = mode & S_IRUSR ? 'r' : '-';
+       readable[2] = mode & S_IWUSR ? 'w' : '-';
+       readable[3] = mode & S_IXUSR ? 'x' : '-';
+       #ifdef HAVE_WIN32
+       return 3;
+       #else
+       readable[4] = mode & S_IRGRP ? 'r' : '-';
+       readable[5] = mode & S_IWGRP ? 'w' : '-';
+       readable[6] = mode & S_IXGRP ? 'x' : '-';
+       readable[7] = mode & S_IROTH ? 'r' : '-';
+       readable[8] = mode & S_IWOTH ? 'w' : '-';
+       readable[9] = mode & S_IXOTH ? 'x' : '-';
+       return 9;
+       #endif
+}
+
+
+
+
+/*
+ * Helper for the 'kdb ls' command
+ *
+ */
+int listTime(time_t when,char *readable) {
+       time_t current_time=time(0);
+       char buf[26];
+       #ifndef HAVE_CTIME_R
+       char *ctimep = NULL;
+       #endif
+       time_t six_months_ago;
+       int recent;
+
+       /* If the file appears to be in the future, update the current
+          time, in case the file happens to have been modified since
+          the last time we checked the clock.  */
+
+       /* Consider a time to be recent if it is within the past six
+          months.  A Gregorian year has 365.2425 * 24 * 60 * 60 ==
+          31556952 seconds on the average.  Write this value as an
+          integer constant to avoid floating point hassles.  */
+       six_months_ago = current_time - 31556952 / 2;
+       recent = (six_months_ago <= when) && (when <= current_time);
+#ifdef HAVE_CTIME_R
+       ctime_r(&when,buf);
+#else
+       ctimep = ctime(&when);
+       strncpy(buf, ctimep, sizeof(buf));
+#endif
+       /*
+       buf now is like "Wed Jun 30 21:49:08 1993\n"
+       fprintf (stderr, "(%d), recent: %d buf: %s", strlen(buf), recent, buf);
+       */
+
+       if (recent) {
+               memcpy(readable,buf+4,12); // copy "Jun 30 21:49"
+               /* in readable are now the parts "Jun 30 21:49" */
+       } else {
+               memcpy(readable,buf+4,7); // copy "Jun 30 "
+               /* in readable are now the parts "Jun 30 " */
+               readable[7]=' ';
+               memcpy(readable+8,buf+20,4); // copy date
+       }
+       if (readable[4] == 32) readable[4] = '0'; // prefix date
+       return 12;
+}
+
+
+/*
+ * Helper for the 'kdb ls' command
+ *
+ */
+void listSingleKey(Key *key) {
+       char buffer[400];
+       char *p=buffer;
+       char *unknown = "<unknown>";
+       int unknown_length= strlen (unknown);
+       char *binary = "<binary>";
+       int binary_length = strlen (binary);
+       struct passwd *pwd;
+
+
+       if (argLong) {
+               p+=listMode(key,p);
+               *p=' '; p++;
+               *p=' '; p++;
+               *p=' '; p++;
+#ifdef HAVE_PWD_H
+               if ( (pwd=getpwuid(keyGetUID(key))) != NULL ) {
+                       strcpy(p,pwd->pw_name);
+                       p+=strlen(pwd->pw_name);
+                       *p=' '; p++;
+                       *p=' '; p++;
+               } else {
+                       strcpy(p, unknown);
+                       p+=unknown_length;
+                       *p=' '; p++;
+                       *p=' '; p++;
+               }
+#endif
+#ifdef HAVE_GRP_H
+               if ( (grp=getgrgid(keyGetGID(key))) != NULL ) {
+                       strcpy(p,grp->gr_name);
+                       p+=strlen(grp->gr_name);
+                       *p=' '; p++;
+               } else {
+                       strcpy(p, unknown);
+                       p+=unknown_length;
+                       *p=' '; p++;
+                       *p=' '; p++;
+               }
+#endif
+               /*sprintf(p,"%*d ",5,keyGetRecordSize(key));
+               p+=strlen(p);*/
+
+               p+=listTime(keyGetMTime(key),p);
+               *p=' '; p++;
+       }
+
+       if (argFullName)
+               p+=keyGetFullName(key,p,sizeof(buffer)-(p-buffer))-1;
+       else
+               p+=keyGetName(key,p,sizeof(buffer)-(p-buffer))-1;
+
+       if (argValue && (keyGetValueSize(key)>1))
+       {
+               *p='='; p++;
+
+               if (keyIsString (key))
+               {
+                       p+=keyGetString(key,p,sizeof(buffer)-(p-buffer))-1;
+               } else if (keyIsBinary(key)) {
+                       strcpy (p, binary);
+                       p+=binary_length;
+               }
+       }
+
+       puts(buffer);
+}
+
+
+
+
+/*
+ * Helper for the 'kdb ls' command
+ *
+ */
+void listAllKeys (KeySet * ks) {
+       size_t listSize=ksGetSize(ks);
+       Key *walker;
+
+       if (listSize == 1) listSingleKey(ksHead(ks));
+       else if (listSize > 1) {
+               ksRewind(ks);
+               while ((walker=ksNext(ks)))
+                       listSingleKey(walker);
+       }
+}
+
+
+
+/*
+ * Helper for the 'kdb ls -s' command
+ *
+ */
+void listAllKeysForShell(KeySet *ks) {
+       Key *walker=0;
+       char *isParent=0;
+       size_t parentNameSize=strlen(argKeyName);
+       const char *keyname=0;
+       const void *keyvalue=0;
+       char *child=0;
+       
+       ksRewind(ks);
+       while ((walker=ksNext(ks))) {
+               keyname=keyName(walker);
+               isParent=strstr(keyname,argKeyName);
+               
+               if (isParent) {
+                       keyname+=parentNameSize;
+                       while (*keyname && *keyname == '/') keyname++;
+               } else {
+                       keyname=0;
+               }
+               
+               /* At this point keyname points to the begining of the
+                  trailing key name after stripping argKeyName */
+               if (keyname) {
+                       /* output key name */
+                       while ((child=strchr(keyname,'/'))) {
+                               fwrite(keyname,child-keyname,1,stdout);
+                               fputc('_',stdout);
+                               keyname=child+1;
+                       }
+                       fputs(keyname,stdout); /* writes remaining stuff */
+                       
+                       /* now output the value surrounded by '"' */
+                       fputs("=\"",stdout);
+                       keyvalue=keyValue(walker);
+                       if (keyvalue) fputs(keyvalue,stdout);
+                       fputs("\";\n",stdout);
+               }
+       }
+}
+
+
+
+
+/**
+ * The business logic behind 'kdb rm' command
+ * @par Example:
+ * @code
+ * bash$ kdb rm user/env/alias/ls   # get rid to the ls alias
+ * @endcode
+ *
+ * @see kdbRemove()
+ * @param argKeyName name of the key that will be removed
+ */
+int commandRemove(KDB *handle)
+{
+       if (!argKeyName) {
+               fprintf(stderr,"kdb rm: No key name\n");
+               return -1;
+       }
+
+       if (argNoRecursive)
+       {
+               if (kdbRemove(handle,argKeyName) == -1)
+               {
+                       char error[300];
+
+                       sprintf(error,"kdb rm: \'%s\'",argKeyName);
+                       kdbPrintError(error);
+                       return -1;
+               }
+       } else { /* -R set */
+               Key *key;
+               KeySet *ks = ksNew(0);
+
+               key=keyNew(argKeyName,KEY_END);
+
+               if (!key) {
+                       fprintf(stderr,"kdb rm: No valid key name\n");
+                       return -1;
+               }
+
+               if (kdbGet(handle, ks, key, KDB_O_INACTIVE) == -1)
+               {
+                       char error[500];
+
+                       if (ksCurrent(ks))
+                               sprintf(error,"kdb rm: Could not get keys, problem appeared at \'%s\'",
+                                               keyName(ksCurrent(ks)));
+                       else
+                               sprintf(error,"kdb rm: Could not get keys below \'%s\'", argKeyName);
+                       kdbPrintError(error);
+                       keyDel (key);
+                       ksDel (ks);
+                       return -1;
+               }
+               if (kdbSet(handle, ks, key, KDB_O_REMOVEONLY) == -1)
+               {
+                       char error[500];
+
+                       if (ksCurrent(ks))
+                               sprintf(error,"kdb rm: Could not rm keys, problem appeared at \'%s\'", keyName(ksCurrent(ks)));
+                       else
+                               sprintf(error,"kdb rm: Could not remove keys below \'%s\'", argKeyName);
+                       kdbPrintError(error);
+                       keyDel (key);
+                       ksDel (ks);
+                       return -1;
+               }
+               ksDel (ks);
+               keyDel (key);
+       }
+
+       return 0;
+}
+
+
+/*
+ * Renames the key to a given newname in backend in an atomic way.
+ *
+ * @note the key will not exist afterwards in database
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param key an initialized Key retrieved from backend
+ * @param newname the new name which the name should have in backend
+ * @return 0 on success
+ * @return -1 on failure and @c errno is propagated
+ * @see kdbSet()
+ * @ingroup kdb
+ */
+int kdbRename(KDB *handle, const Key *key, const char *newname) {
+       KeySet * ks = ksNew(0);
+       Key * toRename = keyDup(key);
+       Key * toRemove = keyDup(key);
+       keyRemove (toRemove);
+       ksAppendKey(ks, toRemove);
+
+       if (keySetName (toRename, newname) == -1)
+       {
+               ksDel (ks);
+               return -1;
+       }
+       ksAppendKey(ks, toRename);
+
+       if (kdbSet (handle, ks,0,0) == -1)
+       {
+#if DEBUG
+               printf ("kdbRename: kdbSet failed\n");
+#endif
+               ksDel (ks);
+               return -1;
+       }
+
+       ksDel (ks);
+       return 0;
+}
+
+
+
+/**
+ * The business logic behind 'kdb mv' command.
+ * The central method used is kdbRename() but this function is
+ * way more robust, and is an example on how to handle errors.
+ * @par Example:
+ * @code
+ * bash# kdb mv user/env  user:tatiana/env
+ * @endcode
+ *
+ * @see kdbRename()
+ * @param argKeyName name of the source key
+ * @param argData name of the target key
+ */
+int commandMove(KDB *handle) {
+       Key *key;
+       size_t size=0;
+       int rc;
+       
+       /* Consistency */
+       if (!argKeyName) {
+               fprintf(stderr,"kdb mv: No target specified\n");
+               return -1;
+       }
+
+       if (!argData) {
+               fprintf(stderr,"kdb mv: \'%s\': No destination specified\n",argKeyName);
+               return -1;
+       }
+       
+       key=keyNew(argKeyName,KEY_END);
+       size=keyGetNameSize(key);
+       
+       if (size == 0) {
+               char error[100];
+               
+               sprintf(error,"kdb mv: \'%s\'", argKeyName);
+               kdbPrintError(error);
+               
+               keyDel(key);
+               return 1;
+       }
+       
+       rc=kdbRename(handle,key,argData);
+       if (rc != 0) {
+               /* Handle a non-zero rc, with same behavior of Unix mv command */
+               switch (errno) {
+                       
+               }
+       }
+       
+       keyDel(key);
+       
+       return rc;
+}
+
+
+
+/**
+ * The business logic behind 'kdb set' command.
+ * Sets value to a single key.
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb set -c "My shell prompt" user/env/env1/PS1 '\h:\w\$'
+ * @endcode
+ *
+ * @param argKeyName name of the key that will be set
+ * @param argComment comment to be set to key (-c)
+ * @param argType type of the key (-t)
+ * @param argMode mode permissions that will be set to sey (-m)
+ * @param argUID UID to be set to sey
+ * @param argGID GID to be set to sey
+ * @param argData the value to the key
+ * @param argFile a filename to use as the input for the value
+ * @see kdbSetKey()
+ */
+int commandSet(KDB *handle) {
+       Key *key=0;
+       int ret=0;
+       char error[200];
+       size_t offset=0;
+
+
+       /* Consistency */
+       if (!argKeyName) {
+               fprintf(stderr,"kdb set: No key name\n");
+               return -1;
+       }
+
+       key=keyNew(argKeyName,KEY_END);
+
+       if (!key) {
+               fprintf(stderr,"kdb set: No key name\n");
+               return -1;
+       }
+       ret=kdbGetKey(handle,key);
+       if (ret == 0) { /* Key already exists. Good. */
+               /* Use existed key type if user didn't give us one */
+               if (argType==KEY_TYPE_UNDEFINED) argType=keyGetType(key);
+       }
+
+       /* Set or overwrite everything else... */
+       
+       if (argUID) keySetUID(key,*argUID);
+       if (argGID) keySetGID(key,*argGID);
+       if (argMode) keySetMode(key,argMode);
+       if (argDir) {
+               keySetDir(key);
+       }
+
+       if (argComment) keySetComment(key,argComment);
+       
+       if (argFile) {
+               FILE *f;
+               int end=0;
+               
+               if (argData) free(argData);
+               argData=0;
+               f=fopen(argFile,"r");
+               
+               if (!f) {
+                       sprintf(error,"kdb set: \'%s\'",argFile);
+                       kdbPrintError(error);
+                       return -1;
+               }
+               while (! end) {
+                       char buffer[100];
+                       ssize_t r;
+                       
+                       r=read(fileno(f),buffer,sizeof(buffer));
+                       switch (r) {
+                               case 0:
+                                       r=lseek(fileno(f),0,SEEK_END)-offset;
+                                       end=1;
+                                       break;
+                               case -1:
+                                       /* those bizarre errors */
+                                       fprintf(stderr,"kdb set: \'%s\': problem reading file\n",argFile);
+                                       fclose(f);
+                                       return -1;
+                       }
+                       argData=realloc(argData,offset+r);
+                       assert(argData!=NULL);
+                       memcpy(argData+offset,buffer,r);
+                       offset+=r;
+               }
+               fclose(f);
+       }
+
+
+       /* Set key value . . . */
+       if (argType == KEY_TYPE_UNDEFINED)
+               keySetString(key,argData); /* the most common here */
+       else if (argData) { /* Handle special type values . . . */
+       
+               /* set raw data */
+               if (offset) keySetRaw(key,argData,offset);
+               else if (KEY_TYPE_BINARY <= argType && argType < KEY_TYPE_STRING)
+                        /* command-line-passed bin values have unwanted \0 in the end */
+                        keySetRaw(key,argData,strlen(argData));
+               else keySetRaw(key,argData,strlen(argData)+1);
+               
+               /* set type explicitly */
+               keySetType(key,argType);
+       }
+
+
+       ret=kdbSetKey(handle,key);
+       if (ret) {
+               sprintf(error,"kdb set: \'%s\'",argKeyName);
+               kdbPrintError(error);
+       }
+       
+       keyDel(key);
+       
+       return ret;
+}
+
+
+
+
+/**
+ * The business logic behind 'kdb ls' command.
+ * @param argKeyName key name to be listed
+ * @param argNoRecursive whether to act recursively (-R)
+ * @param argValue whether to show key values or not (-v)
+ * @param argAll whether to list also inactive keys (-a)
+ * @param argShell operate in a shell script friendly mode (-s)
+ * @param argXML whether to create XML output (-x)
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb ls -R   # list all keys from system and user trees
+ * bash$ kdb ls -Ra  # list them all plus the hidden/inactive keys
+ * bash$ kdb ls -Rav # list all showing value
+ * bash# kdb ls -Rxv # equivalent to 'kdb export'
+ * bash$ kdb ls -Rv user/env # list my aliases and environment vars
+ * @endcode
+ *
+ * @see keyToStream(), ksToStream()
+ * @see commandExport() for the 'kdb export' command
+ */
+int commandList(KDB *handle) {
+       KeySet *ks; /* this is the container for all keys we'll collect bellow */
+       ssize_t ret;
+       unsigned long options=0;
+
+       /* Build our option set */
+       
+       if (argSort)                options |= KDB_O_SORT;
+       if (argNoRecursive)         options |= KDB_O_NORECURSIVE;
+       if (argAll)                 options |= KDB_O_INACTIVE;
+       if (!argValue)              options |= KDB_O_STATONLY;
+       
+       /* these options make sense only to ksToStream() */
+       if (argFullName)            options |= KDB_O_FULLNAME;
+       /* ksToStream() defaults */ options |= KDB_O_HEADER | KDB_O_HIER;
+
+       ks=ksNew(0);
+
+       if (!argKeyName || strcmp(argKeyName, "/") == 0)
+       {
+               Key *walker=0;
+               KeySet *tmp = ksNew(0);
+
+               /* User don't want a specific key, so list the root keys */
+               kdbGet(handle,tmp,0,0);
+
+               if (! argNoRecursive) {
+                       
+                       while ((walker=ksPop(tmp))) {
+                               /* walk root by root, retrieve entire subtree
+                                * and append it to ks
+                                */
+                               KeySet *thisRoot=ksNew(0);
+                               
+                               ret=kdbGet(handle,thisRoot,walker,options | KDB_O_POP);
+                               
+                               /* A hack to transfer a key from a keyset to another.
+                                * Don't do this at home.
+                                */
+                               ksAppendKey(ks,walker);
+                               ksAppend(ks,thisRoot);
+                               ksDel(thisRoot); /* we don't need the container anymore */
+                       }
+               } else ksAppend(ks,tmp);
+               ksDel(tmp);
+       } else {
+               /* User gave us a specific key to start with */
+
+               ret=kdbGetByName (handle,ks,argKeyName,options);
+
+               if (ret<0) {
+                       /* We got an error. Check if it is because its not a folder key */
+                       if (errno==ENOTDIR) {
+                               /* We still have a chance, since there is something there */
+                               Key *key=keyNew(argKeyName,KEY_END);
+
+                               if (!key)
+                               {
+                                       char error[200];
+
+                                       keyDel(key);
+                                       ksDel(ks);
+                                       
+                                       sprintf(error,"kdb ls: %s",argKeyName);
+                                       kdbPrintError(error);
+                                       return ret;
+                               }
+
+                               if (argValue)
+                                       ret=kdbGetKey(handle,key);
+                               else ret=kdbStatKey(handle,key);
+                               
+                               if (ret == 0) ksAppendKey(ks,key);
+                               else {
+                                       /* There is absolutely nothing there */
+                                       char error[200];
+
+                                       keyDel(key);
+                                       ksDel(ks);
+                                       
+                                       sprintf(error,"kdb ls: %s",argKeyName);
+                                       kdbPrintError(error);
+                                       return ret;
+                               }
+                               keyDel (key);
+                       } else { /* A real error */
+                               char error[200];
+
+                               ksDel(ks);
+
+                               sprintf(error,"kdb ls: %s",argKeyName);
+                               kdbPrintError(error);
+                               return ret;
+                       }
+               }
+       }
+
+       /* Better give it a sort */
+       if (ksNeedSort (ks)) ksSort (ks);
+
+       if (argXML) ksToStream(ks,stdout,options);
+       else if (argShell) listAllKeysForShell(ks);
+       else if (argGenerate) ksGenerate(ks,stdout, 0);
+       else if (argOutput) ksOutput(ks,stdout, KEY_VALUE|KEY_COMMENT);
+       else listAllKeys(ks);
+
+       ksDel(ks);
+       return 0;
+}
+
+
+
+
+
+
+
+/**
+ * Business logic behind the 'kdb get' command.
+ * Get a key and return its value to you.
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb get user/env/alias/ls
+ * ls -Fh --color=tty
+ * @endcode
+ *
+ * @param argKeyName key to get value
+ * @param argDescriptive show also the key comment (-d)
+ * @param argShell output suitable for shell scripts (-s)
+ * @param argLong show also the key name (-l)
+ * @param argFullName with @p argLong, show the user domain too (-f)
+ *
+ * @see kdbGetKey(), kdbGetBaseName(), keyGetComment(), keyGetString()
+ *
+ */
+int commandGet(KDB *handle) {
+       int ret;
+       Key *key = 0;
+       char *buffer = 0; // used two times
+       char *p;
+       size_t size,cs=0;
+       type_t keyType;
+       char error[200];
+
+
+       if (!argKeyName) {
+               fprintf(stderr,"kdb get: No key name\n");
+               fprintf(stderr,"run kdb get -h for more info\n");
+               return -1;
+       }
+       
+       key=keyNew(argKeyName,KEY_END);
+
+       if (argKeyName[0] == '/')
+       {
+               buffer = malloc (strlen (argKeyName)+sizeof("system\0"));
+               
+               strcpy (buffer, "user\0");
+               keySetName(key, strcat (buffer, argKeyName));
+               ret=kdbGetKey(handle,key);
+               if (ret == 0) goto done;
+               
+               strcpy (buffer, "system\0");
+               keySetName(key, strcat (buffer, argKeyName));
+               ret=kdbGetKey(handle,key);
+               if (ret == 0) goto done;
+       } else {        
+               ret=kdbGetKey(handle,key);
+       }
+
+       if (ret) {
+               sprintf(error,"kdb get: %s",argKeyName);
+               kdbPrintError(error);
+               goto cleanup;
+       }
+done:
+       size=keyGetValueSize(key);
+       if (argDescriptive) {
+               cs=keyGetCommentSize(key);
+               if (cs) size+=cs+3;
+       }
+       if (argShell) {
+               size+=keyGetBaseNameSize(key);
+               size+=2; /* for 2 '"' to wrap the value */
+       } else if (argLong) {
+               if (argFullName) size+=keyGetFullNameSize(key);
+               else size+=keyGetNameSize(key);
+       }
+
+
+       if (buffer) free (buffer);
+       p=buffer=malloc(size);
+
+
+       if (argDescriptive) {
+               if (cs>1) {
+                       p+=sprintf(p,"# ");
+                       p+=keyGetComment(key,p,size-(p-buffer));
+                       *--p='\n'; p++;
+               }
+       }
+       if (argShell) {
+               p+=keyGetBaseName(key,p,size-(p-buffer));
+               *--p='='; p++;
+               *p='\"'; p++;
+       } else if (argLong) {
+               if (argFullName) p+=keyGetFullName(key,p,size-(p-buffer));
+               else p+=keyGetName(key,p,size-(p-buffer));
+               *--p='='; p++;
+       }
+       
+       keyType=keyGetType(key);
+
+       if (keyIsBinary(key)) p+=keyGetBinary(key,p,size-(p-buffer));
+       else p+=keyGetString(key,p,size-(p-buffer));
+       if (argShell) {
+               *--p='\"'; p++;
+               *p=0;
+       }
+       if (keyIsBinary(key)) fwrite(buffer,size,1,stdout);
+       else printf("%s\n",buffer);
+
+       ret = 0;
+
+cleanup:
+       free(buffer);
+       keyDel(key);
+
+       return ret;
+}
+
+
+
+
+
+
+/**
+ * Opens an editor to edit an XML representation of the keys.
+ * This is one of the most complex commands of the kdb program.
+ * It will
+ * -# retrieve the desired keys
+ * -# put them inside an editor in an XML format to let the user change them
+ * -# wait for the editor to finish
+ * -# reread the edited XML, converting to an internal KeySet
+ * -# compare original and edited KeySets, with ksCompare(), to detect
+ *    differences
+ * -# remove removed keys
+ * -# update updated keys
+ * -# add added keys
+ * -# leave untouched the not-changed keys
+ *
+ * @par Example:
+ * @code
+ * bash$ EDITOR=kedit kdb edit -R user/env # edit with kedit
+ * bash# kdb edit -R system/sw/MyApp       # defaults to vi editor
+ * @endcode
+ *
+ * @param argKeyName the parent key name (and children) that will be edited
+ * @param argRecursive whether to act recursivelly or not
+ * @param argAll whether to edit inactive keys or not
+ * @param EDITOR environment var that defines editor to use, or @p vi
+ * @see keyCompare(), ksCompare(), kdbGetByName()
+ *     ksToStream(), kdbRemoveKey()
+ */
+int commandEdit(KDB *handle) {
+       KeySet *ks;
+       KeySet *ksEdited;
+       KeySet *toRemove;
+       Key *current;
+       int ret;
+       char filename[]="/var/tmp/kdbeditXXXXXX";
+       char command[300];
+       //FILE *xmlfile=0;
+       char choice[5];
+
+       if (!ksFromXMLfile) return 1;
+       
+       ks=ksNew(0);
+
+       kdbGetByName(handle, ks, argKeyName, (argAll?KDB_O_INACTIVE:0) | (argNoRecursive?KDB_O_NORECURSIVE:0));
+
+       if (! ksGetSize(ks)) {
+               /* Maybe the user parameter is not a parent key, but a single key */
+               current=keyNew(argKeyName,KEY_END);
+               if (kdbGetKey(handle,current)) {
+                       /* Failed. Cleanup */
+                       keyDel(current);
+                       current=0;
+               } else {
+                       /* We have something. */
+                       ksAppendKey(ks,current);
+                       current=0;
+               }
+       }
+
+/*
+       for (current=ks.start; current; current=current->next) {
+               if (keyNeedSync(current)) {
+                       printf("%s needs sync\n",current->key);
+               }
+       }
+*/
+
+       /*
+       TODO
+       xmlfile=fdopen(mkstemp(filename),"rw+");
+
+       ksToStream(ks,xmlfile,KDB_O_XMLHEADERS | KDB_O_HIER |
+               KDB_O_FULLNAME | KDB_O_FULLUGID);
+       fclose(xmlfile);
+       */
+
+       do {
+               /* execute the editor and wait for it to finish */
+               sprintf(command,"[ -z \"$EDITOR\" ] && EDITOR=vi; $EDITOR %s",filename);
+               system(command);
+
+               toRemove=ksNew(0);
+               ksEdited=ksNew(0);
+
+               /* ksFromXML is not a library function.
+                * It is implemented in and for this program only.
+                * It is pretty reusable code, though.
+                */
+               ret=ksFromXMLfile(ksEdited,filename);
+               if (ret!=0) {
+                       printf("kdb cannot import this file, because it is not valid !\n");
+                       strcpy(choice,"");
+                       while (choice[0]!='E' && choice[0]!='C') {
+                               printf("Do you want to edit it again or to cancel ? (E/C) : ");
+                               fgets(choice,4, stdin );
+                       }
+               }
+       } while (ret!=0 && choice[0]=='E');
+       remove(filename);
+       
+       if (ret==0) {
+               /*TODO ksCompare
+               ksCompare(ks,ksEdited,toRemove);
+               */
+       
+               /* Discard ksEdited because there is nothing else here
+               * after keyCompare() */
+               ksDel(ksEdited);
+               
+               /* Commit changed keys */
+               ksRewind(ks);
+               while ((ret=kdbSet(handle,ks,0,0))) {
+                       /* We got an error. Warn user. */
+                       Key *problem;
+                       char error[500];
+                       char keyname[300]="";
+                       
+                       problem=ksCurrent(ks);
+                       if (problem) keyGetFullName(problem,keyname,sizeof(keyname));
+                       sprintf(error,"kdb edit: while setting/updating %s", keyname);
+                       kdbPrintError(error);
+                       
+                       /* And try to set keys again starting from the next key,
+                       * unless we reached the end of the KeySet */
+                       if (ksNext(ks) == 0) break;
+               }
+               
+               ksDel(ks); /* Finished with this KeySet */
+       
+               /* Remove removed keys */
+               ksRewind(toRemove);
+               while ((current=ksNext(toRemove))) {
+                       char keyname[800];
+       
+                       keyGetFullName(current,keyname,sizeof(keyname));
+                       ret=kdbRemove(handle,keyname);
+                       if (ret != 0) {
+                               char error[850];
+                               
+                               sprintf(error,"kdb edit: while removing %s",keyname);
+                               kdbPrintError(error);
+                       }
+               }
+       
+               /* Finished with this KeySet too */
+               ksDel(toRemove);
+               }
+       
+       return 0;
+}
+
+ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize);
+
+/**
+ * Business logic behind the 'kdb info' command.
+ * Displays some information about the Elektra library, version, backend, etc.
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb info
+ * @endcode
+ * 
+ * @see kdbGetInfo(), kdbInfoToString(), kdbFreeInfo()
+ */
+int commandInfo(KDB *handle)
+{
+       char buffer [MAX_PATH_LENGTH];
+       Key *k;
+       int rc;
+
+       if (!argKeyName)
+       {
+               printf ("Supply a keyname as second argument to deduce where the key will be stored on disk.\n");
+               return 1;
+       }
+
+       k = keyNew (argKeyName, KEY_END);
+
+       if (!k)
+       {
+               printf ("Supply a valid keyname.\n");
+               return 1;
+       }
+
+       rc = kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
+
+       if (rc == -1)
+       {
+               printf ("failure\n");
+               return 1;
+       } else {
+               printf ("%s\n", buffer);
+               return 0;
+       }
+}
+
+
+/**
+ * Business logic behind the 'kdb import' command.
+ * Import an XML file (or standard input) into the key database.
+ * This is usefull to import full application's keys, or restore backups.
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb import myAppDefaultKeys.xml
+ * bash$ generateKeys | kdb import
+ * @endcode
+ * 
+ * @see commandExport()
+ */
+int commandImport(KDB *handle) {
+       KeySet *ks;
+       int ret;
+
+       if (!ksFromXMLfile || !ksFromXML) return 1;
+       
+       
+       ks=ksNew(0);
+       /* The command line parsing function will put the XML filename
+          in the argKeyName global. */
+       if (argKeyName) ksFromXMLfile(ks,argKeyName);
+       else ksFromXML(ks,fileno(stdin) /* more elegant then just '0' */);
+
+       ksRewind(ks);
+       while ((ret=kdbSet(handle,ks,0,0))==-1) {
+               /* We got an error. Warn user. */
+               Key *problem;
+               char error[500]="";
+               char keyname[300]="";
+
+               problem=ksCurrent(ks);
+               if (problem) keyGetFullName(problem,keyname,sizeof(keyname));
+               sprintf(error,"kdb import: while importing %s", keyname);
+               kdbPrintError(error);
+               
+               /* And try to set keys again starting from the next key,
+                *  unless we reached the end of KeySet */
+               if (ksNext(ks) == 0) break;
+       }
+       
+       ksDel(ks);
+       
+       return ret;
+}
+
+
+
+
+
+/**
+ * Business logic behind the 'kdb export' command.
+ * Export a set of keys to an XML format. Usefull to make backups or copy
+ * keys to other machine or user.
+ * Equivalent to 'kdb ls -xRv base/key/name'
+ *
+ * @par Example:
+ * @code
+ * bash# kdb export system > systemConfigurationBackup.xml
+ * bash# kdb export system/sw/MyApp > myAppConfiguration.xml
+ * bash$ kdb export system/sw/MyApp | sed -e 's|system/sw|user/sw|g' | kdb import
+ * @endcode
+ *
+ * @see commandList(), commandImport()
+ *
+ */
+int commandExport(KDB *handle)
+{
+       KeySet *ks;
+       ssize_t ret;
+
+       if (!argKeyName)
+       {
+               printf ("You have to pass a keyname as argument\n");
+               return 1;
+       }
+
+       ks = ksNew (0);
+       ret = kdbGetByName(handle, ks, argKeyName, KDB_O_INACTIVE);
+
+       if (ret == 0)
+       {
+               fprintf (stderr, "You try to print in the middle of a backend which does not support\n");
+               fprintf (stderr, "getting only some keys.\n");
+               return 1;
+       }
+       else if (ret == -1)
+       {
+               kdbPrintError ("Could not get keys, reason:");
+               ksDel (ks);
+               return 2;
+       }
+
+       else if (argGenerate) ksGenerate(ks,stdout, 0);
+       else if (argOutput) ksOutput(ks,stdout, KEY_VALUE|KEY_COMMENT);
+       else ksToStream(ks,stdout,0);
+
+       ksDel (ks);
+
+       return 0;
+}
+
+
+/**
+ * Business logic behind 'kdb mon' command.
+ *
+ * Will block your command line until some change happens to the
+ * interested key.
+ *
+ * @par Example:
+ * @code
+ * bash$ kdb mon system/sw/MyApp/someKey
+ * @endcode
+ *
+ * @see kdbMonitorKey(), kdbMonitorKeys()
+ */
+int commandMonitor(KDB *handle) {
+#if 0
+       Key *toMonitor;
+       uint32_t diff;
+       
+
+       toMonitor=keyNew(argKeyName,KEY_NEEDSYNC,handle,KEY_END);
+       
+       diff=kdbMonitorKey(handle,
+               toMonitor,           /* key to monitor */
+               KEY_VALUE,    /* key info we are interested in */
+               0,                   /* how many times to poll. 0 = ad-infinitum */
+               500                  /* usecs between polls. 0 defaults to 1 second */);
+
+       /*
+        * Since in our case we'll hang completelly until we get a key's
+        * value change, we don't have to check diff.
+        * So if method returned, the value has changed, and toMonitor has it.
+        */
+       printf("New value is %s\n",(char *)keyValue(toMonitor));
+       
+       keyDel(toMonitor);
+
+#endif
+
+       return 0;
+}
+
+kdbLibHandle dlhandle=0;
+
+void closeToolsLib(void)
+{
+       kdbLibClose (dlhandle);
+}
+
+int loadToolsLib(void)
+{
+
+       kdbLibInit();
+
+       dlhandle=kdbLibLoad("libelektratools");
+       if (dlhandle == 0) {
+               return 1;
+       }
+
+       ksFromXMLfile=(KSFromXMLfile)kdbLibSym(dlhandle,"ksFromXMLfile");
+       ksFromXML=(KSFromXML)kdbLibSym(dlhandle,"ksFromXML");
+
+       ksToStream = (output) kdbLibSym (dlhandle, "ksToStream");
+       ksOutput   = (output) kdbLibSym (dlhandle, "ksOutput");
+       ksGenerate = (output) kdbLibSym (dlhandle, "ksGenerate");
+
+       atexit(closeToolsLib);
+
+       return 0;
+}
+
+
+int doCommand(int command, KDB *handle) {
+       switch (command) {
+               case CMD_SET:             return commandSet(handle);
+               case CMD_LIST:            return commandList(handle);
+               case CMD_GET:             return commandGet(handle);
+               case CMD_REMOVE:          return commandRemove(handle);
+               case CMD_EDIT:            return commandEdit(handle);
+               case CMD_LOAD:            return commandImport(handle);
+               case CMD_SAVE:            return commandExport(handle);
+               case CMD_MONITOR:         return commandMonitor(handle);
+               case CMD_MOVE:            return commandMove(handle);
+               case CMD_INFO:            return commandInfo(handle);
+               case CMD_HELP:            return commandHelp(handle);
+       }
+       return 0;
+}
+
+
+int main(int argc, char **argv) {
+       KDB *handle=0;
+       int command=0;
+       int ret=0;
+
+
+       if (loadToolsLib())
+               fprintf(stderr,"kdb: XML importing and editing disabled\n");
+       
+       /* Parse the command line */
+       command=parseCommandLine(argc,argv);
+
+       /* Check if user only wants some help (kdb -h {command})*/
+       if (argHelp) helpCommand(command);
+
+       /* Open key database */
+       handle = kdbOpen();
+
+       if (handle == 0) {
+               fprintf (stderr, "kdb: could not open key database\n");
+               exit (1);
+       }
+
+       /* Execute command with parameters from command line and exit
+        * program afterwards.*/
+       ret = doCommand(command,handle);
+
+       kdbClose(handle);
+       return ret;
+}
+
+/*
+ * @}
+ */
diff --git a/src/kdb/kdb-tool.h b/src/kdb/kdb-tool.h
new file mode 100644 (file)
index 0000000..d986c17
--- /dev/null
@@ -0,0 +1,77 @@
+/***************************************************************************
+                          kdb.h  -  Tool for the kdb administration
+                             -------------------
+    begin                : Fri Feb 22 2008
+    copyright            : (C) 2008 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HASGETOPT
+#include "BSDgetopt.h"
+#endif
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <time.h>
+#include <locale.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <ctype.h>
+#include <ltdl.h>
+#include <assert.h>
+/* We need fcntl.h for open and related constants used in our definition of mkstemp */
+#ifdef HAVE_WIN32
+#include <fcntl.h>
+#endif
+
+#define DYN_LINK
+#include <kdbtools.h>
+#include <kdb.h>
+#include <kdbloader.h>
+
+#define CMD_GET       1
+#define CMD_SET       2
+#define CMD_REMOVE    3
+#define CMD_LIST      4
+#define CMD_LINK      5
+#define CMD_EDIT      6
+#define CMD_LOAD      7
+#define CMD_SAVE      8
+#define CMD_MONITOR   9
+#define CMD_MOVE      10
+#define CMD_INFO      25
+#define CMD_HELP      30
+
+#define ARGSIZE      30
+
+
+#ifdef HAVE_WIN32
+#define mkstemp(m) open(mktemp(m), O_RDWR)
+#endif
+
+/* Exported functions from help.c */
+int commandHelp();
+int helpCommand(int command);
+
+
diff --git a/src/libelektra/Makefile.am b/src/libelektra/Makefile.am
new file mode 100644 (file)
index 0000000..fba0f1e
--- /dev/null
@@ -0,0 +1,108 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LTDLINCL)
+
+elektra_sources = kdb.c kdbhandle.c key.c keyset.c trie.c \
+                 kdbhighlevel.c kdbcapability.c keyhelpers.c \
+                 keymeta.c keyname.c keytest.c keyvalue.c \
+                 serialize.c split.c
+
+elektra_dependencies = ../include/kdbprivate.h \
+       ../include/kdb.h \
+       ../include/kdbos.h \
+       ../include/kdbbackend.h \
+       ../include/kdbloader.h
+
+lib_LIBRARIES = libelektra-static.a
+libelektra_static_a_SOURCES = $(elektra_sources) \
+               exported_symbols.c
+libelektra_static_a_DEPENDENCIES = $(elektra_dependencies) objects
+libelektra_static_a_CFLAGS = -DELEKTRA_STATIC -DDEFAULT_BACKEND=\"$(default_backend)\" $(CSTDFLAGS) $(COPTFLAGS) $(CDBGFLAGS)
+libelektra_static_a_LIBADD = `cat objects`
+BUILT_SOURCES = exported_symbols.c
+
+lib_LTLIBRARIES = libelektra.la
+libelektra_la_SOURCES = $(elektra_sources)
+libelektra_la_DEPENDENCIES = $(elektra_dependencies) ../libloader/libloader-dynamic.la ../libhelper/libhelper-dynamic.la
+libelektra_la_CFLAGS = -DDEFAULT_BACKEND=\"default\" $(CSTDFLAGS) $(COPTFLAGS) $(CDBGFLAGS)
+libelektra_la_LDFLAGS = -version-info $(ELEKTRA_VERSION_API)
+libelektra_la_LIBADD = ../libloader/libloader-dynamic.la ../libhelper/libhelper-dynamic.la $(LIBICONV)
+
+EXTRA_DIST = exportsymbols.sh
+
+../include/kdbprivate.h:
+       cd ../include && $(MAKE) kdbprivate.h
+
+../libloader/libloader-static.a:
+       cd ../libloader && $(MAKE) libloader-static.a
+
+../libloader/libloader-dynamic.la:
+       cd ../libloader && $(MAKE) libloader-dynamic.la
+
+../libhelper/libhelper-static.a:
+       cd ../libhelper && $(MAKE) libhelper-static.a
+
+../libhelper/libhelper-dynamic.la:
+       cd ../libhelper && $(MAKE) libhelper-dynamic.la
+
+#if HAVE_XML
+#../libelektratools/libelektratools.a:
+#      cd ../libelektratools && $(MAKE) libelektratools.a
+#endif
+
+#all_backends:
+#      cd ../backends && $(MAKE) all
+
+../backends/filesys/libelektra-filesys.a:
+       cd ../backends/filesys && $(MAKE) libelektra-filesys.a
+
+../backends/ini/libelektra-ini.a:
+       cd ../backends/ini && $(MAKE) libelektra-ini.a
+
+../backends/fstab/libelektra-fstab.a:
+       cd ../backends/fstab && $(MAKE) libelektra-fstab.a
+
+../backends/passwd/libelektra-passwd.a:
+       cd ../backends/passwd && $(MAKE) libelektra-passwd.a
+
+../backends/hosts/libelektra-hosts.a:
+       cd ../backends/hosts && $(MAKE) libelektra-hosts.a
+
+../backends/template/libelektra-template.a:
+       cd ../backends/template && $(MAKE) libelektra-template.a
+
+../backends/daemon/libelektra-daemon.a:
+       cd ../backends/daemon && $(MAKE) libelektra-daemon.a
+
+../backends/berkeleydb/libelektra-berkeleydb.a:
+       cd ../backends/berkeleydb && $(MAKE) libelektra-berkeleydb.a
+
+../backends/gconf/libelektra-gconf.a:
+       cd ../backends/gconf && $(MAKE) libelektra-gconf.a
+
+#objects: ../libloader/libloader-static.a all_backends ../libelektratools/libelektratools.a
+#      @list='$(backends)'; for backend in $$list; do \
+#        bck_list=$$bck_list" ../backends/$$backend/libelektra-$$backend.a "; \
+#      done; \
+#      $(SHELL) exportobjects.sh ../libloader/libloader-static.a $$bck_list ../libelektratools/libelektratools.a
+
+
+object_deps = ../libloader/libloader-static.a ../libhelper/libhelper-static.a $(backend_static_libs)
+
+#if HAVE_XML
+#object_deps += ../libelektratools/libelektratools.a
+#endif
+
+objects: $(object_deps)
+       $(SHELL) exportobjects.sh $(object_deps)
+
+exported_symbols.c: ../../config.status
+       $(SHELL) $(srcdir)/exportsymbols.sh $(default_backend) $(BACKENDS)
+
+#exported_symbols.c: all_backends ../libelektratools/libelektratools.a
+#      @list='$(backends)'; for backend in $$list; do \
+#        bck_list=$$bck_list" ../backends/$$backend/libelektra-$$backend.a"; \
+#      done; \
+#      $(SHELL) exportsymbols.sh $$bck_list ../libelektratools/libelektratools.a
+clean-local:
+       rm -f objects
+       rm -f exported_symbols.c exported_symbols.h
+       rm -f *.gcno *.gcda *.gcno
diff --git a/src/libelektra/exportobjects.sh.in b/src/libelektra/exportobjects.sh.in
new file mode 100644 (file)
index 0000000..7846608
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh -x
+
+# exportobjects.sh
+# ----------------
+#
+# Export the list of all backend's objects files
+# to be included in the static version libelektra
+#
+# (c) 2006 Yannick Lecaillez
+
+for lib in "$@"
+do
+       if [ -r $lib ]; then
+               echo "Get objects files for $lib ..."
+               @AR@ t $lib | @AWK@ -v "dir=`dirname $lib`" '/^[^_]/ {printf "%s/%s ", dir,$0}' >> objects
+       else
+               echo "SKIPED: $lib doesn't exist."
+       fi
+done
+
+echo "" >> objects
+
diff --git a/src/libelektra/exportsymbols.sh b/src/libelektra/exportsymbols.sh
new file mode 100644 (file)
index 0000000..098fd26
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh -x
+
+# exportsymbols.sh
+# ----------------
+#
+# Export kdbBackendFactory entry point symbols
+# for each compiled backend
+#
+# (c) 2006 Yannick Lecaillez
+
+func=kdbBackendFactory
+toolsfuncs='ksFromXMLfile ksFromXML ksToStream ksOutput ksGenerate keyToStream keyOutput keyGenerate'
+
+echo '/* exported_symbols.h generated by exportsymbols.sh */
+
+#include <kdbloader.h>
+
+' > exported_symbols.h
+
+for lib in "$@"
+do
+       if test $lib = libelektratools; then
+               for toolsfunc in $toolsfuncs; do
+                       symbol=${lib}_LTX_${toolsfunc}
+                       echo "extern void $symbol (void);" >> exported_symbols.h
+               done
+       else
+               symbol=libelektra_${lib}_LTX_${func}
+               echo "extern void $symbol (void);" >> exported_symbols.h
+       fi
+done
+
+echo '/* exported_symbols.c generated by exportsymbols.sh */
+
+#include <exported_symbols.h>
+
+kdblib_symbol kdb_exported_syms[] =
+{
+' > exported_symbols.c
+
+lib="$1"
+echo "Exporting symbols for default ..."
+fname="libelektra-default"
+symbol=libelektra_${lib}_LTX_${func}
+echo " {\"$fname\", 0 }," >> exported_symbols.c
+echo " {\"$func\", &$symbol }," >> exported_symbols.c
+echo "" >> exported_symbols.c
+shift
+
+for lib in "$@"
+do
+       if test $lib = libelektratools; then
+               fname=libelektratools
+               echo "  {\"$fname\", 0 }," >> exported_symbols.c
+               for toolsfunc in $toolsfuncs; do
+                       symbol=${fname}_LTX_${toolsfunc}
+                       echo "  {\"$toolsfunc\", &$symbol }," >> exported_symbols.c
+               done
+       else
+               echo "Exporting symbols for $lib ..."
+               fname="libelektra-${lib}"
+               symbol=libelektra_${lib}_LTX_${func}
+               echo "  {\"$fname\", 0 }," >> exported_symbols.c
+               echo "  {\"$func\", &$symbol }," >> exported_symbols.c
+       fi
+       echo "" >> exported_symbols.c
+done
+echo " { 0 , 0 }" >> exported_symbols.c
+echo "};" >> exported_symbols.c
diff --git a/src/libelektra/kdb.c b/src/libelektra/kdb.c
new file mode 100755 (executable)
index 0000000..32fbb9f
--- /dev/null
@@ -0,0 +1,1049 @@
+/***************************************************************************
+            kdb.c  -  Low level functions for access the Key Database
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/**
+ * @defgroup kdb KDB :: Low Level Methods
+ * @brief General methods to access the Key database.
+ *
+ * To use them:
+ * @code
+ * #include <kdb.h>
+ * @endcode
+ *
+ * The kdb*() class of methods are used to access the storage, to get and set
+ * @link key Keys @endlink or @link keyset KeySets @endlink.
+ *
+ * The most important functions are:
+ *  - kdbOpen()
+ *  - kdbClose()
+ *  - kdbGet()
+ *  - kdbSet()
+ *
+ * The two essential functions for dynamic information about backends are:
+ *  - kdbGetMountpoint()
+ *  - kdbGetCapability()
+ *
+ * They use some backend implementation to know the details about how to access
+ * the storage. Currently we have this backends:
+ * - @c berkeleydb: the keys are stored in a Berkeley DB database, providing
+ *   very small footprint, speed, and other advantages.
+ * - @c filesys: the key hierarchy and data are saved as plain text files in 
+ *   the filesystem.
+ * - @c ini: the key hierarchy are saved into configuration files.
+ *   @see http://www.libelektra.org/Ini
+ * - @c fstab: a reference backend used to interpret the @c /etc/fstab file as
+ *   a set of keys under @c system/filesystems .
+ * - @c gconf: makes Elektra use the GConf daemon to access keys. Only the
+ *   @c user/ tree is available since GConf is not system wide.
+ *
+ * Backends are physically a library named @c /lib/libelektra-{NAME}.so.
+ *
+ * See @link backend writing a new backend @endlink for information
+ * about how to write a backend.
+ *
+ * Language binding writers should follow the same rules:
+ * - You must relay completely on the backend-dependent methods.
+ * - You may use or reimplement the second set of methods.
+ * - You should completely reimplement in your language the higher
+ *   lever methods.
+ * - Many methods are just for comfort in C. These methods are marked
+ *   and need not to be implemented if the binding language has e.g. string
+ *   operators which can do the operation easily.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#include <kdbbackend.h>
+
+
+KDB* kdbOpenBackend(const char *backendname, const char *mountpoint, KeySet *config)
+{
+       KDB * handle;
+       char* backend_name;
+
+       kdbLibHandle dlhandle=0;
+       typedef KDB *(*KDBBackendFactory) (void);
+       KDBBackendFactory kdbBackendFactory=0;
+
+       backend_name = malloc(sizeof("libelektra-")+strlen(backendname));
+
+       strncpy(backend_name,"libelektra-",sizeof("libelektra-"));
+       strncat(backend_name,backendname,strlen(backendname));
+
+       dlhandle=kdbLibLoad(backend_name);
+       if (dlhandle == 0) {
+               /*errno=KDB_ERR_EBACKEND;*/
+               goto err_clup; /* error */
+       }
+
+       /* load the "kdbBackendFactory" symbol from backend */
+       kdbBackendFactory=(KDBBackendFactory)kdbLibSym(dlhandle, "kdbBackendFactory");
+       if (kdbBackendFactory == 0) {
+               /*errno=KDB_ERR_NOSYS;*/
+               goto err_clup; /* error */
+       }
+       
+       handle=kdbBackendFactory();
+       if (handle == 0)
+       {
+               /*errno=KDB_ERR_NOSYS;*/
+               goto err_clup; /* error */
+       }
+
+       /* save the libloader handle for future use */
+       handle->dlHandle=dlhandle;
+       handle->trie    = 0;
+
+       handle->mountpoint=keyNew(mountpoint,KEY_VALUE,backendname,0);
+
+       /* let the backend initialize itself */
+       if (handle->kdbOpen)
+       {
+               handle->config = config;
+               handle->kdbOpen(handle);
+       }
+       else {
+               /*errno=KDB_ERR_NOSYS;*/
+               goto err_clup;
+       }
+
+#if DEBUG && VERBOSE
+       printf("Finished loading Backend %s\n", backend_name);
+#endif
+       free(backend_name);
+       return handle;
+
+err_clup:
+#if DEBUG
+       fprintf(stderr,"Failed to load backend %s\n", backend_name);
+#endif
+       free(backend_name);
+       return 0;
+}
+
+int kdbCloseBackend(KDB *handle)
+{
+       int rc=0;
+
+       if (handle->kdbClose)
+               rc=handle->kdbClose(handle);
+       
+       if (rc == 0) {
+               kdbLibClose(handle->dlHandle);
+               capDel (handle->capability);
+               keyDel(handle->mountpoint);
+               if (handle->config) ksDel(handle->config);
+               free(handle);
+       }
+       
+       return rc;
+}
+
+
+/**
+ * Dynamically mount a single backend.
+ *
+ * Maps the mountpoint, defined through its name and value, into the global elektra
+ * hierachy. If successfull, under the mountpoint another backend will reside.
+ *
+ * This only works for a single KDB, that means a single thread in a single process.
+ * You may want statically mounting by editing system/elektra/mountpoints.
+ *
+ * If you allocated mountpoint and config first, make sure that you free it!
+ * It is ok to free it immediately afterwards.
+ *
+ * @param handle handle to the kdb data structure 
+ * @param mountpoint the keyName() of this key is the mountpoint, keyValue() the backend
+ * @param config the configuration passed for that backend
+ * @return 0 on success, -1 if an error occurred
+ * @ingroup kdb
+*/
+int kdbMount(KDB *handle, const Key *mountpoint, const KeySet *config)
+{
+       char *mountpoint_slash;
+       const char *key_name;
+       const char *backend;
+       size_t size;
+       KDB *h;
+       Trie *trie;
+       KeySet *c;
+
+       key_name=keyName(mountpoint);
+       backend=keyValue(mountpoint);
+
+       size = kdbiStrLen(key_name);
+       mountpoint_slash = malloc (size + 1);
+       strcpy (mountpoint_slash, key_name);
+       mountpoint_slash [size-1] = '/';
+       mountpoint_slash [size] = 0;
+
+       h=kdbOpenBackend(backend,mountpoint_slash,c=ksDup(config));
+       if (!h) {
+               free(mountpoint_slash);
+               ksDel(c);
+               return -1;
+       }
+       trie=insert_trie(kdbhGetTrie(handle), mountpoint_slash, h);
+       kdbhSetTrie(handle,trie);
+
+       free(mountpoint_slash);
+       return 0;
+}
+
+
+/**
+ * Dynamically unmount a single backend.
+ *
+ * Unmount a backend that was mounted with kdbMount() before.
+ *
+ * @param handle handle to the kdb data structure 
+ * @param mountpoint directory where backend is mounted to, that should be unmounted
+ * @return 0 on success, -1 if an error ocurred.
+ * @ingroup kdb
+ */
+
+int kdbUnmount(KDB *handle, const Key *mountpoint)
+{
+       size_t size;
+       const char *key_name;
+       char *mountpoint_slash;
+
+       if (mountpoint==NULL) {
+               return -1;
+       }
+       if (kdbGetBackend(handle,mountpoint)==NULL) {
+               return -1;
+       }
+       key_name=keyName(mountpoint);
+
+       size = kdbiStrLen(key_name);
+       mountpoint_slash = malloc (size + 1);
+       strcpy (mountpoint_slash, key_name);
+       mountpoint_slash [size-1] = '/';
+       mountpoint_slash [size] = 0;
+
+       delete_trie(kdbhGetTrie(handle), mountpoint_slash,kdbCloseBackend);
+       free(mountpoint_slash);
+       return 0;
+}
+
+
+
+/**
+ * Lookup a mountpoint in a handle for a specific key.
+ *
+ * Will return a key representing the mountpoint or null
+ * if there is no appropriate mountpoint e.g. its the
+ * root mountpoint.
+ *
+ * Together with kdbGetCapability() the two essential
+ * informations about mounted backends.
+ *
+ * @par Example:
+ * @code
+Key * key = keyNew ("system/template");
+KDB * handle = kdbOpen();
+Key *mountpoint=0;
+mountpoint=kdbGetMountpoint(handle, key);
+
+printf("The library I am using is %s mounted in %s\n",
+       keyValue(mountpoint),
+       keyName(mountpoint));
+kdbClose (handle);
+keyDel (key);
+ * @endcode
+ *
+ *
+ * @param handle is the data structure, where the mounted directories are saved.
+ * @param where the key, that should be looked up.
+ * @return the mountpoint associated with the key
+ * @ingroup kdb
+ */
+Key* kdbGetMountpoint (KDB *handle, const Key *where)
+{
+       KDB *backend_handle;
+
+       backend_handle=kdbGetBackend(handle,where);
+       if (!backend_handle)
+       {
+               /*errno = KDB_ERR_EBACKEND;*/
+               return 0;
+       }
+
+       return backend_handle->mountpoint;
+}
+
+
+/*
+ * Returns a structure of information about the internals
+ * of the library and the backend used.
+ *
+ * This is mainly used to check the capabilities of a specific
+ * backend.
+ *
+ * @par Example:
+ * @code
+Key * key = keyNew ("system/template");
+KDB * handle = kdbOpen();
+KDBCap *capability=0;
+capability=kdbGetCapability(handle, key);
+
+printf("The library I am using is %s in version %s\n",
+       kdbcGetName(capability),
+       kdbcGetVersion(capability));
+if (kdbcGetnoError(capability)) printf ("Ohh! Error states are not supported\n");
+else printf ("Puhh! Error states are supported\n");
+kdbClose (handle);
+keyDel (key);
+ * @endcode
+ *
+ * Together with kdbGetMountpoint() the two essential
+ * informations about mounted backends.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param where lets you choose the position of where you want the information from
+ * @return info on sucess, 0 if @p info is NULL
+ *     errno will be set to KDB_ERR_EBACKEND if no backend is found
+ * @see commandInfo()
+ * @ingroup kdb
+ */
+KDBCap *kdbGetCapability(KDB *handle, const Key *where)
+{
+       KDB *backend_handle;
+
+       backend_handle=kdbGetBackend(handle,where);
+       if (!backend_handle)
+       {
+               /*errno = KDB_ERR_EBACKEND;*/
+               return 0;
+       }
+
+       return backend_handle->capability;
+}
+
+
+
+/**
+ * Opens the session with the Key database.
+ *
+ * The first step is to open the default backend. With it
+ * system/elektra/mountpoints will be loaded and all needed
+ * libraries and mountpoints will be determined.
+ * These libraries for backends will be loaded and with it the
+ * @p KDB datastructure will be initialized.
+ *
+ * You must always call this method before retrieving or commiting any
+ * keys to the database. In the end of the program,
+ * after using the key database, you must not forget to kdbClose().
+ * You can use the atexit () handler for it.
+ *
+ * The pointer to the @p KDB structure returned will be initialized
+ * like described above, and it must be passed along on any kdb*()
+ * method your application calls.
+ *
+ * Get a @p KDB handle for every thread using elektra. Don't share the
+ * handle across threads, and also not the pointer accessing it:
+ * @code
+thread1 {
+       KDB * h;
+       h = kdbOpen();
+       // fetch keys and work with them
+       kdbClose(h);
+}
+thread2 {
+       KDB * h;
+       h = kdbOpen();
+       // fetch keys and work with them
+       kdbClose(h);
+}
+ * @endcode
+ *
+ * You don't need to use the kdbOpen() if you only want to
+ * manipulate plain in-memory Key or KeySet objects without any affairs with
+ * the backend key database,
+ *
+ * @see kdbClose() to end all affairs to the key database.
+ * @return a KDB pointer on success
+ * @return NULL on failure
+ * @ingroup kdb
+ */
+KDB * kdbOpen()
+{
+       KDB * handle;
+       KeySet *keys;
+       Trie *trie;
+
+#if DEBUG && VERBOSE
+       fprintf (stderr, "open elektra " VERSION "\n");
+#endif
+
+       if (kdbLibInit()) {
+               /*errno=KDB_ERR_NOSYS;*/
+               return 0;
+       }
+
+       /* Open default backend */
+       handle=kdbOpenBackend("default",0,0);
+       if (!handle)
+       {
+#if DEBUG
+               printf ("failed to open default backend");
+#endif
+               return 0;
+       }
+
+       /* get mount config from root backend */
+       keys=ksNew(0);
+
+       kdbGet(handle,keys,keyNew(KDB_KEY_MOUNTPOINTS,KEY_END),KDB_O_DEL);
+
+#if DEBUG && VERBOSE
+       ksRewind(keys);
+       for (key=ksNext(keys);key;key=ksNext(keys)) {
+               printf("mount %s as %s\n",keyName(key),(char*) keyValue(key));
+       }
+#endif
+       trie=createTrie(keys,kdbOpenBackend);
+       kdbhSetTrie(handle, trie);
+       ksDel(keys);
+
+       return handle;
+}
+
+/**
+ * Closes the session with the Key database.
+ *
+ * You should call this method when you finished your affairs with the key
+ * database. You can manipulate Key and KeySet objects also after kdbClose().
+ * You must not use any kdb* call afterwards. You can implement kdbClose()
+ * in the atexit() handler.
+ *
+ * This is the counterpart of kdbOpen().
+ *
+ * The @p handle parameter will be finalized and all resources associated to it
+ * will be freed. After a kdbClose(), this @p handle can't be used anymore,
+ * unless it gets initialized again with another call to kdbOpen().
+ *
+ * @see kdbOpen()
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @return 0 on success
+ * @return -1 on NULL pointer
+ * @ingroup kdb
+ */
+int kdbClose(KDB *handle)
+{
+       if (!handle)
+       {
+               /*errno=KDB_ERR_NOSYS;*/
+               return -1;
+       }
+       if (handle->trie)
+               kdbDelTrie (handle->trie,kdbCloseBackend);
+
+       kdbCloseBackend (handle);
+
+       return 0;
+}
+
+
+
+/**
+ * Retrieve keys in an atomic and universal way, all other kdbGet Functions
+ * rely on that one.
+ *
+ * The @p returned KeySet must be initialized or may already contain some
+ * keys. The new retrieved keys will be appended using ksAppendKey().
+ *
+ * In default behaviour (@p options = 0) it will fully retrieve all keys
+ * under the @p parentKey folder, with all subfolders and their children
+ * but not inactive keys or folders.
+ *
+ * The keyset will not be sorted at first place, but will be marked dirty and
+ * sorted afterwards when needed. That could be a subsequent ksLookup(),
+ * ksLookupByName() or kdbSet(). See ksSort() on that issue.
+ *
+ * The behaviour can be fine-tuned with options in various ways to make kdbGet() more
+ * comfortable.
+ *
+ * @section kdbgetoption Options
+ *
+ * The @p option is an array of the following ORed flags:
+ *
+ * - @p option_t::KDB_O_DEL \n
+ *   Its often useful to keyDel() the parentKey in the line after kdbGet().
+ *   Using this flag, you can just pass a key allocated with keyNew(),
+ *   kdbGet() will free it for you in the end.
+ * - @p option_t::KDB_O_POP \n
+ *   The @p parentKey itself will always be added to @p returned.
+ *   If you only want the children
+ *   of the parentKey in @p returned, but not the parentKey itself, use this flag.
+ *   This is only valid for the first parentKey, the one you passed.
+ *   The other recursive parentKeys will stay in the keyset.
+ *   To get only the leaves of the tree, without any parentKey,
+ *   see @ref option_t::KDB_O_NODIR below.
+ * - @p option_t::KDB_O_NODIR \n
+ *   Don't include folders in the @p returned KeySet, so only keys without
+ *   subkeys. You can picture it best that you only get the leaves of the
+ *   tree of keys.
+ * - @p option_t::KDB_O_DIRONLY \n
+ *   Put in @p returned only the folder keys. The resulting 
+ *   KeySet will be only the skeleton of the tree. This option must not be
+ *   ORed together with KDB_O_DIR.
+ * - @p option_t::KDB_O_NOSTAT \n
+ *   Don't stat they keys, whatever keyNeedStat() says.
+ *   That means that also the key value and comment will be retrieved.
+ *   The flag will result in that all keys in @p returned don't have
+ *   keyNeedStat() set.
+ * - @p option_t::KDB_O_STATONLY \n
+ *   Only stat the keys. It means that key value and comment will
+ *   not be retrieved. The resulting keys will contain only meta info such
+ *   as user and group IDs, owner, mode permissions and modification times.
+ *   You don't need that flag if the keys already have keyNeedStat() set.
+ *   The flag will result in that all keys in @p returned have
+ *   keyNeedStat() set.
+ * - @p option_t::KDB_O_INACTIVE \n
+ *   Will make it not ignore inactive keys, so @p returned will contain also
+ *   inactive keys. Inactive keys are those that have names
+ *   begining with '.' (dot).
+ *   Please be sure that you know what you are doing, inactive keys must not
+ *   have any semantics to the application. This flag should only be set in
+ *   key browsers after explicit user request.
+ *   You might also get inactive keys when you plan to remove a whole
+ *   hierarchy.
+ * - @p option_t::KDB_O_SORT \n
+ *   Force @p returned to be ksSort()ed. Normally you don't want that the
+ *   @p returned is sorted immediately because you might add other keys or
+ *   go for another kdbGet(). Sorting will
+ *   take place automatically when needed by ksLookup() or kdbSet(),
+ *   also without this option set.
+ *   But you need to sort the keyset for yourself, when you just iterate
+ *   over it. If you want to do that, pass this flag at the last kdbGet().
+ * - @p option_t::KDB_O_NORECURSIVE \n
+ *   Dont get the keys recursive. Only receive keys from one folder.
+ *   This might not work if the backend does not support it. Be prepared
+ *   for more keys and use ksLookup() and avoid static assumptions
+ *   on how many keys you get.
+ *
+ * @par Example:
+ * @code
+KDB *handle;
+KeySet *myConfig;
+Key *key;
+
+myConfig=ksNew(0);
+
+handle = kdbOpen();
+
+key=keyNew("system/sw/MyApp",KEY_END);
+rc=kdbGet(handle,key, myConfig, 0);
+keyDel(key);
+
+key=keyNew("user/sw/MyApp",KEY_END);
+rc=kdbGet(handle,key, myConfig, 0);
+keyDel(key);
+
+// will sort keyset here
+key=ksLookupByName(myConfig,"/sw/MyApp/key", 0);
+// check if key is not 0 and work with it...
+
+ksDel (myConfig); // delete the in-memory configuration
+
+
+// maybe you want kdbSet() myConfig here
+
+kdbClose(handle); // no more affairs with the key database.
+ * @endcode
+ *
+ * @section kdbgetdetail Details
+ *
+ * When no backend could be found (e.g. no backend mounted)
+ * the default backend will be used.
+ *
+ * If you pass a NULL pointer as handle and/or returned  kdbGet() will
+ * return -1 and do nothing but keyDel() the parentKey when requested
+ * and not a NULL pointer.
+ *
+ * If you pass NULL as parentKey the root keys of all namespaces
+ * will be appended to returned.
+ *
+ * For every directory key (keyIsDir()) the appropriate backend
+ * will be chosen and keys in it will be requested.
+ *
+ * If any backend reports an failure the recursive getting of
+ * keys will be stopped. Backends only report failure when they
+ * are not able to get keys for any problems.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param parentKey parent key or NULL to get the root keys
+ * @param returned the (pre-initialized) KeySet returned with all keys found
+ * @param options ORed options to control approaches
+ * @see #option_t
+ * @see @link kdbhighlevel kdb higher level Methods @endlink that rely on kdbGet()
+ * @see ksLookupByName(), ksLookupByString() for powerful
+ *     lookups after the KeySet was retrieved
+ * @see commandList() code in kdb command for usage example
+ * @see commandEdit() code in kdb command for usage example
+ * @see commandExport() code in kdb command for usage example
+ * @return number of keys contained by @p returned
+ * @return -1 on failure
+ * @ingroup kdb
+ *
+ */
+ssize_t kdbGet (KDB *handle, KeySet *returned,
+       Key * parentKey, option_t options)
+{
+       ssize_t size = 0; /* nr of keys get */
+       ssize_t ret = 0;
+       KeySet *keys;
+       KeySet *tmp;
+       Key *current;
+       KDB *backend_handle;
+
+       if (!handle || !returned)
+       {
+               if (parentKey && (options & KDB_O_DEL)) keyDel (parentKey);
+               /* errno=KDB_ERR_NOSYS; */
+               return -1;
+       }
+
+       if (!parentKey)
+       {
+               ksAppendKey(returned, keyNew("db", KEY_DIR, KEY_END)); ret ++;
+               ksAppendKey(returned, keyNew("file", KEY_DIR, KEY_END)); ret ++;
+               ksAppendKey(returned, keyNew("memory", KEY_DIR, KEY_END)); ret ++;
+               return ret;
+       }
+
+#if DEBUG && VERBOSE
+       fprintf (stderr, "now in new kdbGet (%s)\n", keyName(parentKey));
+#endif
+
+       backend_handle=kdbGetBackend(handle,parentKey);
+       if (backend_handle==NULL)
+               backend_handle=handle;
+
+       if (options & KDB_O_NOSTAT) parentKey->flags &= ~KEY_FLAG_STAT;
+       else if (options & KDB_O_STATONLY) keyStat (parentKey);
+
+       keys = ksNew (0);
+       tmp = ksNew (0);
+       ksRewind (returned);
+       while ((current = ksPop(returned)) != 0)
+       {
+               if (options & KDB_O_NOSTAT) current->flags &= ~KEY_FLAG_STAT;
+               else if (options & KDB_O_STATONLY) keyStat(current);
+
+               if (keyIsDirectBelow(parentKey, current))
+               {
+                       if (keyNeedStat(parentKey)) keyStat (current);
+                       set_bit (current->flags, KEY_FLAG_SYNC);
+                       ksAppendKey(keys, current);
+               } else {
+                       ksAppendKey(tmp, current);
+               }
+       }
+
+       ksAppend (returned, tmp);
+       ksDel (tmp);
+       if (ksNeedSort (keys)) ksSort (keys);
+
+       ret = backend_handle->kdbGet(backend_handle,keys,parentKey);
+       if (ret == -1)
+       {
+               if (options & KDB_O_DEL) keyDel (parentKey);
+               ksDel (keys);
+#if DEBUG && VERBOSE
+               fprintf (stderr, "call of handle->kdbGet failed\n");
+#endif
+               return -1;
+       } else if (ret == 0)
+       {
+               ksRewind (keys);
+               while ((current = ksNext(keys)) != 0) clear_bit (current->flags, KEY_FLAG_SYNC);
+               ksAppend (returned, keys);
+               ksDel (keys);
+               return 0;
+       }
+
+       ksRewind(keys);
+       while ((current = ksPop(keys)) != 0)
+       {
+               const char *parentName = keyName(parentKey);
+               const char *currentName = keyName(current);
+               if (keyNeedSync(current))
+               {       /* Key was not updated, throw it away */
+                       keyDel (current);
+                       continue;
+               }
+               if ((options & KDB_O_NORECURSIVE) &&
+                       !(!strcmp (parentName, currentName) || keyIsDirectBelow(parentKey, current)))
+               {       /* Only parentKey itself or keys direct below */
+                       keyDel (current);
+                       continue;
+               }
+               if (strcmp (parentName, currentName) == 0)
+               {
+                       if (! (options & KDB_O_POP))
+                       {
+                               if (keyIsDir(current))
+                               {
+                                       if (options & KDB_O_NODIR)
+                                       {
+                                               keyDel (current);
+                                               continue;
+                                       }
+                               } else if (options & KDB_O_DIRONLY) {
+                                       keyDel (current);
+                                       continue;
+                               }
+                               size ++;
+                               ksAppendKey(returned, current);
+                       } else {
+                               keyDel (current);
+                       }
+                       continue;
+               }
+               if ((!(options & KDB_O_INACTIVE)) && keyIsInactive (current))
+               {
+                       keyDel (current);
+                       continue;
+               }
+               if (keyIsDir (current))
+               {
+                       if (! (options & KDB_O_NORECURSIVE))
+                       {
+                               ret = kdbGet(handle, returned, current,
+                                       options & ~KDB_O_DEL & ~KDB_O_SORT & ~KDB_O_POP);
+                               if (ret == -1)
+                               {
+#if DEBUG && VERBOSE
+                                       fprintf (stderr, "recursive call failed\n");
+#endif
+                                       size = -1;
+                                       keyDel (current);
+                                       break;
+                               } else if (ret > 0)
+                               {
+                                       size += ret;
+                                       keyDel (current);
+                                       continue; /*current was already handeled*/
+                               } /* Fallthrough if ret == 0, add current */
+                       }
+                       if (options & KDB_O_NODIR)
+                       {
+                               keyDel (current);
+                               continue;
+                       }
+               }
+               else if (options & KDB_O_DIRONLY)
+               {
+                       keyDel (current);
+                       continue;
+               }
+               if (size > -1)
+               {
+                       size ++;
+                       ksAppendKey(returned, current);
+               }
+       }
+       ksDel(keys);
+
+       if (options & KDB_O_SORT) ksSort (returned);
+       if (options & KDB_O_DEL) keyDel (parentKey);
+       return size;
+}
+
+
+
+/**
+ * Set keys in an atomic and universal way, all other kdbSet Functions
+ * rely on that one.
+ *
+ * The given handle and keyset are the objects to work with.
+ *
+ * With parentKey you can only store a part of the given keyset.
+ * Otherwise pass a null pointer or a parentKey without a name.
+ *
+ * @code
+KeySet *ks = ksNew(0);
+kdbGet (h, ks, keyNew("system/myapp",0), KDB_O_DEL);
+kdbGet (h, ks, keyNew("user/myapp",0), KDB_O_DEL);
+
+//now only set everything below user, because you can't write to system
+kdbSet (h, ks, keyNew("user/myapp",0), KDB_O_DEL);
+
+ksDel (ks);
+ * @endcode
+ *
+ *
+ * Each key is checked with keyNeedSync() before being actually committed. So
+ * only changed keys are updated. If no key of a backend needs to be synced
+ * the kdbSet_backend() will be omitted.
+ *
+ * If some error occurs, kdbSet() will stop. In this situation the KeySet
+ * internal cursor will be set on the key that generated the error.
+ * This specific key and all behind it were not set.
+ * To be failsafe jump over it and try to set the rest, but report the error
+ * to the user.
+ *
+ * @par Example of how this method can be used:
+ * @code
+int i;
+KeySet *ks;  // the KeySet I want to set
+// fill ks with some keys
+for (i=0; i< 10; i++) // limit to 10 tries
+{
+       ret=kdbSet(handle,ks, 0, 0);
+       if (ret == -1)
+       {
+               // We got an error. Warn user.
+               Key *problem;
+               problem=ksCurrent(ks);
+               if (problem)
+               {
+                       char keyname[300]="";
+                       keyGetFullName(problem,keyname,sizeof(keyname));
+                       fprintf(stderr,"kdb import: while importing %s", keyname);
+               } else break;
+               // And try to set keys again starting from the next key,
+               // unless we reached the end of KeySet
+               if (ksNext(ks) == 0) break;
+       }
+}
+ * @endcode
+ *
+ * @section kdbsetoption Options
+ *
+ * There are some options changing the behaviour of kdbSet():
+ *
+ * - @p option_t::KDB_O_DEL \n
+ *   Its often useful to keyDel() the parentKey in the line after kdbGet().
+ *   Using this flag, you can just pass a key allocated with keyNew(),
+ *   kdbGet() will free it for you in the end.
+ * - @p option_t::KDB_O_SYNC \n
+ *   Will force to save all keys, independent of their sync state.
+ * - @p option_t::KDB_O_NOREMOVE \n
+ *   Don't remove any key from disk, even if keyRemove() was set.
+ *   With that flag removing keys can't happen unintentional.
+ *   The flag will result in that all keys in @p returned don't have
+ *   keyNeedRemove() set.
+ * - @p option_t::KDB_O_REMOVEONLY \n
+ *   Remove all keys instead of setting them. All keys in @p returned
+ *   will have keyNeedRemove() set, but not keyNeedStat() saying to you
+ *   that the key was deleted permanently.
+ *   This option implicit also activates @p option_t::KDB_O_SYNC
+ *   because the sync state will be changed when they are marked remove.
+ *   You might need @ref option_t::KDB_O_INACTIVE set for the previous call
+ *   of kdbGet() if there are any. Otherwise the recursive remove will fail,
+ *   because removing directories is only possible when all subkeys are
+ *   removed.
+ *
+ * @section kdbsetdetail Details
+ *
+ * When you dont have a parentKey or its name empty, then all keys will
+ * be set.
+ *
+ * You can remove some keys instead of setting them by marking them with keyRemove().
+ * The keyNeedSync() flag will be unset after successful removing. But the keyNeedRemove()
+ * flag will stay, but its safe to delete the key.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param ks a KeySet which should contain changed keys, otherwise nothing is done
+ * @param parentKey holds the information below which key keys should be set
+ * @param options see in kdbSet() documentation
+ * @return 0 on success
+ * @return -1 on failure
+ * @see keyNeedSync(), ksNext(), ksCurrent()
+ * @see keyRemove(), keyNeedRemove()
+ * @see commandEdit(), commandImport() code in kdb command for usage and error
+ *       handling example
+ * @ingroup kdb
+ */
+ssize_t kdbSet (KDB *handle, KeySet *ks,
+       Key * parentKey, option_t options)
+{
+       int i;
+       int t=0;
+       KDB *h;
+       Key *errorKey;
+
+       int size = 0;
+       int errors_occurred=0;
+       Split *keysets;
+
+       if (parentKey && !parentKey->key)
+       {
+               if (options & KDB_O_DEL) keyDel (parentKey);
+               parentKey = 0;
+       }
+
+       if (!handle || !ks)
+       {
+               if (parentKey && (options & KDB_O_DEL)) keyDel (parentKey);
+               /*errno=KDB_ERR_NOSYS;*/
+               return -1;
+       }
+
+       if (ksNeedSort(ks) || (options & KDB_O_SORT)) ksSort (ks);
+
+       if (!kdbhGetTrie(handle)) 
+       { /* Fallback code without mounting */
+               ksRewind (ks);
+               return handle->kdbSet(handle,ks,parentKey);
+       }
+
+       keysets=split_keyset(handle, ks, parentKey, options);
+
+       for (i=0; i<keysets->no;i++) {
+               t=0;
+
+               h=keysets->handles[i];
+               /* if there is no backend in the trie use the default */
+               if (h==NULL) {
+                       h=handle;
+               }
+               if (keysets->syncbits[i] && keysets->belowparents[i])
+               {
+                       if (ksNeedSort(keysets->keysets[i])) ksSort(keysets->keysets[i]);
+                       ksRewind (keysets->keysets[i]);
+                       t=h->kdbSet(h,keysets->keysets[i],keysets->parents[i]);
+               }
+               if (t==-1) {
+                       errors_occurred=1;
+                       errorKey = ksCurrent (keysets->keysets[i]);
+                       if (errorKey) ksLookup(ks, errorKey, KDB_O_WITHOWNER);
+                       break;
+               }
+               else {
+                       size+=t;
+               }
+       }
+
+       free_splitted_keysets(keysets);
+       if (options & KDB_O_DEL) keyDel (parentKey);
+       if (errors_occurred)
+               return -1;
+       return size;
+}
+
+
+
+/**
+ * This function must be called by a backend's kdbBackendFactory() to
+ * define the backend's methods that will be exported.
+ *
+ * See KDBEXPORT() how to use it for backends.
+ *
+ * The order and number of arguments are flexible (as in keyNew() and ksNew()) to let
+ * libelektra.so evolve without breaking its ABI compatibility with backends.
+ * So for each method a backend must export, there is a flag defined by
+ * #backend_t. Each flag tells kdbBackendExport() which method comes
+ * next. A backend can have no implementation for a few methods that have
+ * default inefficient high-level implementations and to use these defaults, simply
+ * don't pass anything to kdbBackendExport() about them.
+ *
+ * @param backendName a simple name for this backend
+ * @return an object that contains all backend informations needed by
+ *     libelektra.so
+ * @ingroup backend
+ */
+KDB *kdbBackendExport(const char *backendName, ...) {
+       va_list va;
+       KDB *returned;
+       backend_t method=0;
+
+       if (backendName == 0) return 0;
+
+       returned=malloc(sizeof(KDB));
+       memset(returned,0,sizeof(KDB));
+
+       returned->capability = capNew();
+       returned->capability->name = backendName;
+       returned->capability->version = "";
+       returned->capability->description = "";
+       returned->capability->author = "";
+       returned->capability->licence = "";
+
+       /* Start processing parameters */
+       
+       va_start(va,backendName);
+
+       while ((method=va_arg(va,backend_t))) {
+               switch (method) {
+                       case KDB_BE_OPEN:
+                               returned->kdbOpen=va_arg(va,kdbOpenPtr);
+                               break;
+                       case KDB_BE_CLOSE:
+                               returned->kdbClose=va_arg(va,kdbClosePtr);
+                               break;
+                       case KDB_BE_GET:
+                               returned->kdbGet=va_arg(va,kdbGetPtr);
+                               break;
+                       case KDB_BE_SET:
+                               returned->kdbSet=va_arg(va,kdbSetPtr);
+                               break;
+                       case KDB_BE_VERSION:
+                               returned->capability->version=va_arg(va, char *);
+                               break;
+                       case KDB_BE_DESCRIPTION:
+                               returned->capability->description=va_arg(va, char *);
+                               break;
+                       case KDB_BE_AUTHOR:
+                               returned->capability->author=va_arg(va, char *);
+                               break;
+                       case KDB_BE_LICENCE:
+                               returned->capability->licence=va_arg(va, char *);
+                               break;
+               }
+       }
+       va_end(va);
+       
+       return returned;
+}
+
+
diff --git a/src/libelektra/kdbcapability.c b/src/libelektra/kdbcapability.c
new file mode 100644 (file)
index 0000000..9a6db62
--- /dev/null
@@ -0,0 +1,453 @@
+/***************************************************************************
+      kdbcapability.c  -  Capability and attribute functions for backends
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/*
+ * @defgroup capability KDB Backends :: Capabilities of backends
+ * @brief The tactics to consider different possibilites of different backends.
+ *
+ * Since version 0.7 with the vast new possiblities of backends used
+ * together in a global namespace the fact that not every backend can
+ * fulfill any requirement as datastore and the intersection would
+ * lead to a useless limited storage, capabilities were introduced.
+ *
+ * Applications heavily using kdbGet() and kdbSet() together with metadata
+ * may be interested if what they show are default values or something you
+ * can rely on.
+ *
+ * On the other hand backends may be interested to tell that they
+ * can't fullfill the requriements described in kdbGet_backend() and
+ * kdbSet_backend().
+ *
+ * Many backends are very limited in how granular the metadata is. If
+ * many keys are stored in the same file, it is of course not possible
+ * that one key has e.g. another owner than another key in the same
+ * file.
+ *
+ * There might even be a limit in what names can be accepted by a
+ * backend, because of syntax in a configuration file. A formal description
+ * needed for that problem would be out of scope and not very useful.
+ * For that reason capabilites are limited to yes/no questions
+ * if something is provided or not.
+ *
+ * Capabilites are also very useful for testing driven or
+ * iterative development. Beginning with simple reading everything out
+ * transformed to keyset in kdbGet_backend() and generating a new
+ * file with the keyset of kdbSet_backend() you can iterative
+ * delete handicaps of your backend until you hit the full specification,
+ * if it is possible.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <kdbbackend.h>
+
+KDBCap *capNew ()
+{
+       KDBCap * cap = (KDBCap *)malloc(sizeof(KDBCap));
+       if (!cap) return 0;
+       memset(cap,0,sizeof(KDBCap));
+       return cap;
+}
+
+void capDel (KDBCap *cap)
+{
+       free (cap);
+}
+
+
+/*Version of the library.
+ *
+ * @param cap the obsolete cap object
+ * @ingroup capability*/
+const char * kdbcGetVersion (const KDBCap *cap)
+{
+       return cap->version;
+}
+
+
+/*Name of backend being or that will be used 
+ *
+ * @param cap the obsolete cap object
+ * @ingroup capability*/
+const char * kdbcGetName (const KDBCap *cap)
+{
+       return cap->name;
+}
+
+
+/*Any text describing the backend.
+ *
+ * @param cap the obsolete cap object
+ * @ingroup capability*/
+const char * kdbcGetDescription (const KDBCap *cap)
+{
+       return cap->description;
+}
+
+
+/*The author of the backend.
+ *
+ * Best is format "Full Name <email@libelektra.org>"
+ * e.g.:
+ *
+ * Avi Alkalay <avi@unix.sh>
+ * Markus Raab <elektra@markus-raab.org>
+ * Yannick Lecaillez <yl@itioweb.com>
+ * Jens Andersen <jens.andersen@gmail.com>
+ * Patrick Sabin <patricksabin@gmx.at>
+ * Pier Luigi Fiorini <pierluigi.fiorini@mockup.org>
+ * Rèmi <remipouak@yahoo.fr>
+ * Studio-HB <contact@studio-hb.com>
+ *
+ *
+ * @param cap the obsolete cap object
+ * @ingroup capability*/
+const char * kdbcGetAuthor (const KDBCap *cap)
+{
+       return cap->author;
+}
+
+
+/*The licence of the backend.
+ *
+ * - Must be "BSD" for bsd licence, this is the licence for elektra core.
+ * - Must be "GPL2" for gpl version 2.
+ * - Must be "GPL3" for gpl version 3.
+ * - Must be "LGPL" for lgpl.
+ *
+ * Because of BSD licence even commercial is allowed.
+ *
+ *
+ * @param cap the obsolete cap object
+ * @ingroup capability*/
+const char * kdbcGetLicence (const KDBCap *cap){
+       return cap->licence;
+}
+
+
+/*You cant get specific keys.
+ *
+ * The specification of kdbGet_backend() says that you must
+ * get a key with its subkey and nothing more or less. Declaring
+ * onlyFullGet you are allowed to get all keys when you get
+ * a kdbGet_backend() request of your mountpoint, but you need
+ * to return 0 for any subkeys to avoid an infinite loop.
+ *
+ *
+ * @param cap the obsolete cap object
+ * @see kdbGet_backend()
+ * @ingroup capability
+ * */
+unsigned int kdbcGetonlyFullGet (const KDBCap *cap)
+{
+       return cap->onlyFullGet;
+}
+
+
+/*All keys need to be in keyset when setting.
+ *
+ * Backends have not problem with setting single keys, when the
+ * gap between those keys is already filled out with previous
+ * calls of kdbSet(). The keys in between may not be in the
+ * keyset.
+ *
+ * Declaring onlyFullSet requires the caller to kdbGet() all
+ * keys of the backend and manipulating this full set. This
+ * allows the backend to regenerate the whole config, making
+ * implementation much easier.
+ *
+ *
+ * @param cap the obsolete cap object
+ * @see kdbSet_backend()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetonlyFullSet (const KDBCap *cap)
+{
+       return cap->onlyFullSet;
+}
+
+
+/*When getting keys they cant be stated.
+ *
+ * @see keyStat(), keyNeedStat()
+ * @see kdbGet_backend()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoStat (const KDBCap *cap)
+{
+       return cap->noStat;
+}
+
+
+/*You can only remove all keys at once.
+ *
+ * @see kdbSet_backend()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetonlyRemoveAll (const KDBCap *cap)
+{
+       return cap->onlyRemoveAll;
+}
+
+
+/*When setting keys with new name below parentKey, they will be added.
+ *
+ * Otherwise (changing keys) all keys are required.
+ *
+ * @see kdbSet_backend()
+ * @ingroup capability
+ * */
+unsigned int kdbcGetonlyAddKeys (const KDBCap *cap)
+{
+       return cap->onlyAddKeys;
+}
+
+
+/*The backend does not support a owner of keys.
+ *
+ *
+ * @see keySetOwner() and keyGetOwner()
+ * @ingroup capability
+ * */
+unsigned int kdbcGetnoOwner (const KDBCap *cap)
+{
+       return cap->noOwner;
+}
+
+
+/*No value is supported in the backend.
+ *
+ * A backend might be useful only having comments, metadata or only
+ * names without any values as information for applications. Declare
+ * this if your backend is not capable of storing values.
+ *
+ * @see keySetRaw()
+ * @see keySetString() and keyGetString()
+ * @see keyValue() (togehter with keyGetValueSize() if it is binary)
+ * @ingroup capability
+ * */
+unsigned int kdbcGetnoValue (const KDBCap *cap)
+{
+       return cap->noValue;
+}
+
+
+/*No comment is supported in the backend.
+ *
+ * @see keySetComment() and keyGetComment()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoComment (const KDBCap *cap)
+{
+       return cap->noComment;
+}
+
+
+/*No uid is supported in the backend.
+ *
+ * @see keySetUID()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoUID (const KDBCap *cap)
+{
+       return cap->noUID;
+}
+
+
+/*No gid is supported in the backend.
+ *
+ * @see keySetGID()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoGID (const KDBCap *cap)
+{
+       return cap->noGID;
+}
+
+
+/*No mode is supported in the backend.
+ *
+ * Means that keys will only have 664 or
+ * 775 as mode values.
+ *
+ * @see keySetMode()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoMode (const KDBCap *cap)
+{
+       return cap->noMode;
+}
+
+
+/*Directories are not supported in the backend.
+ *
+ * Tells that mode won't have any executable
+ * bits set.
+ *
+ * Declaring kdbcGetnoDir() means
+ * that the backend is flat, no key will be true for keyIsDir()
+ * and so can't have any subkeys.
+ *
+ * @see keySetDir()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoDir (const KDBCap *cap)
+{
+       return cap->noDir;
+}
+
+
+/*Access Time not supported.
+ *
+ * @see keySetATime()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoATime (const KDBCap *cap)
+{
+       return cap->noATime;
+}
+
+
+/*Modification Time not supported.
+ *
+ * @see keySetMTime()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoMTime (const KDBCap *cap)
+{
+       return cap->noMTime;
+}
+
+
+/*Meta Info and Subkeys Change Time not supported.
+ *
+ * @see keySetCTime()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoCTime (const KDBCap *cap)
+{
+       return cap->noCTime;
+}
+
+
+/*The backend does not support removing keys.*/
+unsigned int kdbcGetnoRemove (const KDBCap *cap)
+{
+       return cap->noRemove;
+}
+
+
+
+/*Mount type not supported.*/
+unsigned int kdbcGetnoMount (const KDBCap *cap)
+{
+       return cap->noMount;
+}
+
+
+/*Binary types not supported.
+ *
+ * @see keySetBinary()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoBinary (const KDBCap *cap)
+{
+       return cap->noBinary;
+}
+
+
+/*String types not supported.
+ *
+ * @see keySetString()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoString (const KDBCap *cap)
+{
+       return cap->noString;
+}
+
+
+/*Typing of keys is not supported.
+ *
+ * @see keySetRaw() and keySetType()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoTypes (const KDBCap *cap)
+{
+       return cap->noTypes;
+}
+
+
+/*Do not expect errno to be set correctly.
+ *
+ * @see kdbhSetError()
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoError (const KDBCap *cap)
+{
+       return cap->noError;
+}
+
+
+/*Backend does not lock.
+ *
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoLock (const KDBCap *cap)
+{
+       return cap->noLock;
+}
+
+
+/*Backend uses global variables and is not threadsafe.
+ *
+ * @ingroup capability
+ **/
+unsigned int kdbcGetnoThread (const KDBCap *cap)
+{
+       return cap->noThread;
+}
+
diff --git a/src/libelektra/kdbhandle.c b/src/libelektra/kdbhandle.c
new file mode 100644 (file)
index 0000000..7404807
--- /dev/null
@@ -0,0 +1,239 @@
+/***************************************************************************
+  kdbhandle.c  -  Functions which operate on the private kdbhandle struct
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+/**
+ * @defgroup backendhandle KDB Backends :: KDB access functions
+ * @brief Methods to access the backend handle.
+ *
+ * To use them:
+ * @code
+ * #include <kdb.h>
+ * @endcode
+ *
+ * These functions provide access to the information stored in
+ * Backend Handles.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+
+#include "kdbbackend.h"
+
+/**
+ * Set some backend-specific @p data in the @p handle.
+ *
+ * This is useful when your backend have a backend-global context or
+ * environment.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param data a pointer to general data specific to a backend implementation.
+ * @see kdbhGetBackendData()
+ * @ingroup backendhandle
+ */
+void *kdbhSetBackendData(KDB *handle, void *data) {
+       return handle->backendData = data;
+}
+
+
+/**
+ * Get the previously set backend-specific @p data from the @p handle.
+ *
+ * This is useful when your backend have a backend-global context or
+ * environment.
+ * 
+ * This method will probably be called everytime one of your kdb*()
+ * implementations is called. And if you change something inside the data, you
+ * don't have to kdbhSetBackendData() again, bacause you are manipulating your
+ * data, and not a copy of it.
+ *
+ * @par Example:
+ * @code
+struct MyBackendData {
+ int context1;
+ int context2;
+};
+
+int kdbOpen_mybackend(KDB *handle) {
+       struct MyBackendData *context;
+
+       context=malloc(sizeof(struct MyBackendData));
+       // a random initialization...
+       context->context1=1;
+       context->context2=2;
+
+       kdbhSetBackendData(*handle,context);
+
+       return 0;
+}
+
+int kdbGetKey_maybackend(KDB handle) {
+       struct MyBackendData *context;
+
+       context=kdbhGetBackendData(handle);
+
+       // No do something with the context
+       . . .
+
+       return 0;
+}
+ * @endcode
+ *
+ * On the kdbClose() implementation of your backend, you must remember to
+ * free all resources associated to your data. 
+ *
+ * @par Example of kdbClose() implementation that correctly cleans the context:
+ * @code
+int kdbClose_mybackend(KDB &handle) {
+       struct MyBackendData *context;
+
+       context=kdbhGetBackendData(handle);
+       free(context);
+
+       return 0;
+}
+ * @endcode
+ * @return a pointer to the data previously set be kdbhSetBackendData()
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @ingroup backendhandle
+ */
+void *kdbhGetBackendData(const KDB *handle) {
+       return handle->backendData;
+}
+
+
+/**
+ * Sets capabilty for handle.
+ *
+ * @param cap a pointer to capability structure
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @return The backend name set in @p handle.
+ * @ingroup backendhandle
+ */
+KDBCap* kdbhSetCapability(KDB *handle, KDBCap *cap) {
+       return handle->capability = cap;
+}
+
+/**
+ * Gets capability for handle.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @return The backend name set in @p handle.
+ * @ingroup backendhandle
+ */
+KDBCap* kdbhGetCapability(const KDB *handle) {
+       return handle->capability;
+}
+
+/**
+ * Gets trie for handle.
+ *
+ * The trie is a datastructure containing the mounted backends.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @see kdbhSetTrie()
+ * @return The backend name set in @p handle.
+ * @ingroup backendhandle
+ */
+Trie *kdbhGetTrie(const KDB *handle) {
+       return handle->trie;
+}
+
+/**
+ * Sets trie for handle.
+ *
+ * The trie is a datastructure containing the mounted backends. This must not done inside
+ * backends, it was set correctly already for you.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param trie the datastructure referencing to the other handles of backends
+ * @see kdbhGetTrie()
+ * @return nothing
+ * @ingroup backendhandle
+ */
+void kdbhSetTrie(KDB *handle, Trie *trie) {
+       handle->trie = trie;
+}
+
+/**
+ * Gets mountpoint for handle.
+ *
+ * Every mounted backend has a specific mountpoint where it is mounted. You may need to know
+ * where you were mounted inside a backend to calculate relative pathes.
+ *
+ * The keyName() is where the backend is mounted, keyString() gives the name of which
+ * backend is mounted.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @see kdbhSetMountpoint()
+ * @return The Key containing the mountpoint.
+ * @ingroup backendhandle
+ */
+const Key *kdbhGetMountpoint(KDB *handle) {
+       return handle->mountpoint;
+}
+
+/**
+ * Sets mountpoint for handle.
+ *
+ * You must not change the mountpoint inside your backend, it was set correctly already
+ * for you.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param mountpoint the key containing as name where backend is mounted and as value the backendname
+ * @see kdbhGetMountpoint()
+ * @return nothing
+ * @ingroup backendhandle
+ */
+void kdbhSetMountpoint(KDB *handle, const Key* mountpoint) {
+       if (handle->mountpoint)
+               keyDel(handle->mountpoint);
+       handle->mountpoint = keyDup(mountpoint);
+}
+
+/**
+ * Returns configuration for handle.
+ *
+ * Every backend may have its own configuration using a Keyset.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @return the keyset containing configuration for a backend
+ * @ingroup backendhandle
+ * */
+KeySet *kdbhGetConfig(KDB *handle)
+{
+       return handle->config;
+}
+
diff --git a/src/libelektra/kdbhighlevel.c b/src/libelektra/kdbhighlevel.c
new file mode 100755 (executable)
index 0000000..3ce3947
--- /dev/null
@@ -0,0 +1,566 @@
+/***************************************************************************
+         kdbhighlevel.c  -  Highlevel interface for accessing the Key Database
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/**
+ * @defgroup kdbhighlevel KDB :: High Level methods
+ * @brief High level methods to access the Key database.
+ *
+ * To use them:
+ * @code
+ * #include <kdb.h>
+ * @endcode
+ *
+ * These methods are higher level. They use kdbOpen(), kdbClose(),
+ * kdbGet() and kdbSet() methods to do their
+ * job, and don't have to be reimplemented for a different backend.
+ *
+ * These functions avoid limitations through not implemented capabilities.
+ * This will of course cost some effort, so read through the description
+ * carefully and decide if it is appropriate for your problem.
+ *
+ * Binding writers don't have to implement these functions, use features
+ * of the binding language instead. But you can use these functions as
+ * ideas what high level methods may be useful.
+ *
+ * Don't use writing single keys in a loop, prefer always writing out
+ * a keyset!
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "kdbbackend.h"
+
+
+
+static ssize_t kdbGetHelper(KDB *handle, KeySet *returned, Key *key,
+       unsigned long options)
+
+{
+       Key *dir = keyNew(keyName(key), KEY_END);
+       KDBCap * cap = kdbGetCapability(handle, key);
+       Key *mp;
+       Key *current;
+       KeySet *keys;
+       ssize_t ret;
+
+       /* Check where the mountpoint is */
+       if (kdbcGetonlyFullGet(cap))
+       {
+               mp = kdbGetMountpoint(handle, key);
+               if (mp && strlen(keyName(mp)) > 1)
+               {
+                       keySetName (dir, keyName(mp));
+               } else {
+                       keySetBaseName (dir, ""); /* delete basename */
+               }
+       } else {
+               keySetBaseName (dir, ""); /* delete basename */
+       }
+
+       if (strlen(keyName(dir)) <= 2) /* copy root (user, system) */
+       {
+               keySetName (dir, keyName(key));
+       }
+
+#ifdef TUNNING_ELEKTRA_0_7
+       keySetString(dir, strrchr (key->key, '/')+1);
+#endif
+
+#if DEBUG && VERBOSE
+       printf ("using %s\n", keyName(dir));
+#endif
+
+       /* We are ready, get the keys
+        * Without recursion it does not make sense because we stepped
+        * one level higher */
+       keys = ksNew(0);
+#ifdef TUNNING_ELEKTRA_0_7
+       ret=kdbGet (handle, keys, dir, options | KDB_O_NORECURSIVE);
+#else
+       ret=kdbGet (handle, keys, dir, options & ~KDB_O_NORECURSIVE);
+#endif
+
+       /* Now filter the keys */
+       ksRewind (keys);
+       while ((current = ksNext(keys)) != 0)
+       {
+               const char * namekey = keyName(key);
+               const char * namecur = keyName(current);
+               if ( (strcmp (namekey, namecur) == 0) ||
+                  ( (options & KDB_O_NORECURSIVE) && keyIsDirectBelow(key, current)) ||
+                  (!(options & KDB_O_NORECURSIVE) && keyIsBelow(key, current)))
+               {
+                       ksAppendKey(returned, current);
+               }
+       }
+
+       ksDel (keys);
+       keyDel (dir);
+
+       return ret;
+}
+
+
+/**
+ * Fully retrieves the passed @p key from the backend storage.
+ *
+ * The backend will try to get the key, identified through its
+ * name.
+ *
+ * It uses kdbGet() for retrieving the key and copies the found data
+ * to dest.
+ *
+ * While kdbGetKey() is perfect for a simple get of a specific key,
+ * kdbGet() and kdbGetByName() gives you more control over the
+ * keyset.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param dest a pointer to a Key that has a name set
+ * @return 0 on success
+ * @return -1 on failure
+ * @return -1 on NULL pointer
+ * @see kdbSetKey() to set a single key
+ * @see commandGet() code in kdb command for usage example
+ * @see kdbGet() and kdbGetByName() to have more control over keyset and options
+ * @ingroup kdbhighlevel
+ */
+int kdbGetKey(KDB *handle, Key * dest)
+{
+       Key * source;
+       KeySet *ks = 0;
+       ssize_t ret;
+
+       if (!handle || !dest) return -1;
+
+       ks = ksNew(0);
+
+       ret = kdbGetHelper (handle, ks, dest, KDB_O_INACTIVE);
+
+       if (ret == -1)
+       {
+               ksDel (ks);
+               /*errno = KDB_ERR_NOTFOUND;*/
+               return -1;
+       }
+
+       source = ksLookup (ks, dest, 0);
+
+       if (!source)
+       {
+               ksDel (ks);
+               /*errno = KDB_ERR_NOTFOUND;*/
+               return -1;
+       }
+
+       if (keyCopy(dest, source) == -1)
+       {
+               ksDel (ks);
+               return -1;
+       }
+
+       ksDel (ks);
+       return 0;
+}
+
+
+
+
+/**
+ * Sets @p key in the backend storage.
+ *
+ * While kdbSetKey() is perfect for a simple get of a specific key,
+ * kdbGet() and kdbGetByName() gives you more control over the
+ * keyset.
+ *
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param key Key to set
+ * @return 0 on success
+ * @return -1 on failure
+ * @return -1 on NULL pointer
+ * @see kdbGetKey() to get a single key
+ * @see kdbSet() for more control over keyset and options
+ * @see commandSet() code in kdb command for usage example
+ * @ingroup kdbhighlevel
+ */
+int kdbSetKey(KDB * handle, const Key *key)
+{
+       int rc=0;
+       Key *dest=0;
+       Key *parent=0;
+       Key *mp = 0;
+       Key *dir = 0;
+       KeySet *ks = 0;
+       KDBCap * cap = 0;
+
+       if (!handle || !key) return -1;
+
+       cap = kdbGetCapability(handle, key);
+       ks = ksNew(0);
+       dir = keyNew(keyName(key), KEY_END);
+       mp = keyDup (kdbGetMountpoint(handle, key));
+
+
+       if (kdbcGetonlyFullSet(cap))
+       {
+               if (mp && strlen(keyName(mp)) > 1)
+               {
+                       keySetName (dir, keyName(mp));
+               } else {
+                       keySetBaseName (dir, ""); /* delete basename */
+               }
+
+               if (strlen(keyName(dir)) <= 2) /* copy root (user, system) */
+               {
+                       keySetName (dir, keyName(key));
+               }
+
+#if DEBUG && VERBOSE
+               printf ("kdbGetKey using %s\n", keyName(dir));
+#endif
+
+               kdbGet (handle, ks, dir, KDB_O_NORECURSIVE | KDB_O_INACTIVE);
+
+               dest = ksLookupByName (ks, keyName(key), 0);
+
+               if (!dest)
+               {
+                       parent = keyDup (key);
+                       keySetBaseName(parent, "");
+                       dest = ksLookupByName (ks, keyName(parent), 0);
+                       keyDel (parent);
+                       if (dest) keySetDir (dest);
+                       else {
+                               ksDel (ks);
+                               keyDel (dir);
+                               keyDel (mp);
+                               /*errno = KDB_ERR_NOTFOUND;*/
+                               return -1; /* no parent found */
+                       }
+                       ksAppendKey(ks, keyDup (key));
+               } else keyCopy(dest, key);
+       } else ksAppendKey(ks, keyDup (key));
+
+       rc = kdbSet(handle, ks, mp, 0);
+       ksDel (ks);
+       keyDel (dir);
+       keyDel (mp);
+
+       if (rc >= 0) return 0;
+       return -1;
+}
+
+
+
+
+
+/**
+ * A high-level method to get a key value, by key name.
+ *
+ * This method gets a backend from any backend with kdbGetKey() and
+ * extracts the string and store it into returned. It only works with
+ * string keys.
+ *
+ * This method gives you the direct relation between a keyname and
+ * the value, without any kdb specific structures. Use it when
+ * you just want some values out of the kdb namespace.
+ *
+ * You need to know the maximum string length of the object. That
+ * could be the case when you e.g. save a path which is limited
+ * with MAX_PATH.
+ *
+ * @code
+KDB *handle = kdbOpen();
+char buffer [MAX_PATH];
+
+if (kdbGetString(handle, "user/key/to/get/pathname", buffer, sizeof(buffer)) == -1)
+{
+       // handle error cases
+} else {
+       printf ("The keys value is %s\n", buffer);
+}
+kdbClose(handle);
+ * @endcode
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param keyname the name of the key to receive the value
+ * @param returned a buffer to put the key value
+ * @param maxSize the size of the buffer
+ * @return 0 on success
+ * @return -1 on failure
+ * @return -1 on NULL pointers
+ * @return -1 if maxSize is 0 or larger than SSIZE_MAX
+ * @see kdbSetString() and kdbRemove() to set and remove a string
+ * @see kdbGetKey(), keySetKey() to work with Keys
+ * @see kdbGet() and kdbGetByName() for full access to internal datastructures
+ * @ingroup kdbhighlevel
+ *
+ */
+int kdbGetString(KDB * handle,const char *keyname,
+               char *returned,size_t maxSize)
+{
+       Key *key = 0;
+       int rc = 0;
+
+       if (!handle || !keyname || !returned) return -1;
+       if (!maxSize) return -1;
+       if (maxSize > SSIZE_MAX) return -1;
+
+       key=keyNew(0);
+       if (!key) return -1;
+       rc=keySetName(key,keyname);
+       if (rc == -1)
+       {
+               keyDel (key);
+               return -1;
+       }
+
+       rc=kdbGetKey(handle, key);
+       if (rc == 0) rc = keyGetString(key,returned,maxSize);
+
+       keyDel(key);
+       return rc;
+}
+
+
+
+/**
+ * A high-level method to set a value to a key, by key name.
+ *
+ * It will check if key exists first, and keep its metadata.
+ * So you'll not loose the previous key comment.
+ *
+ * This will set a text key. So if the key was previously a binary
+ * it will be retyped as string.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param keyname the name of the key to receive the value
+ * @param value the value to be set
+ * @return 0 on success
+ * @return -1 on NULL pointers
+ * @return -1 on failure
+ * @see kdbGetString(), keySetString(), kdbSetKey()
+ * @ingroup kdbhighlevel
+ */
+int kdbSetString(KDB * handle, const char *keyname, const char *value)
+{
+       Key *key = 0;
+       int rc = 0;
+
+       if (!handle || !keyname || !value) return -1;
+
+       key=keyNew(0);
+       if (!key) return -1;
+       rc=keySetName(key,keyname);
+       if (rc == -1)
+       {
+               keyDel (key);
+               return -1;
+       }
+
+       kdbGetKey(handle,key);
+       keySetString(key,value);
+       rc=kdbSetKey(handle,key);
+       keyDel(key);
+       return rc;
+}
+
+
+/**
+ * Remove a key by its name from the backend storage.
+ *
+ * With kdbSetString() its only possible to set a key with an empty
+ * string. To really remove a key in a highlevel way you can use
+ * this method.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param keyname the name of the key to be removed
+ * @return 0 on success
+ * @return -1 on failure
+ * @return -1 on NULL pointers
+ * @see together with kdbSetString() and kdbGetString() a highlevel interface for kdb
+ * @see commandRemove() code in kdb command for usage example
+ * @ingroup kdbhighlevel
+ */
+int kdbRemove(KDB *handle, const char *keyname)
+{
+       int rc=0;
+       Key *key=0;
+
+       if (!handle || !keyname) return -1;
+
+       key=keyNew(0);
+       if (!key) return -1;
+       rc=keySetName(key,keyname);
+       if (rc == -1)
+       {
+               keyDel(key);
+               return -1;
+       }
+
+       keyRemove (key);
+       rc=kdbSetKey(handle,key);
+       keyDel(key);
+
+       return rc;
+}
+
+
+
+/**
+ * This method is similar kdbGet() but the path is given
+ * by a string.
+ *
+ * When it is not possible to make a key out of that string
+ * -1 is returned .
+ *
+ * When parentName starts with / cascading will be used and both
+ * keys from user and system will be fetched.
+ *
+ * A typically app with about 3000 keys may have this line:
+ *
+ *@code
+KDB *handle = kdbOpen();
+KeySet *myConfig = (4096, KS_END);
+ssize_t ret = kdbGetByName (handle, myConfig, "/sw/app/current", 0);
+
+// check ret and work with keyset myConfig
+
+ksDel (myConfig);
+kdbClose (handle);
+ *@endcode
+ *
+ * myConfig will be loaded with keys from system/sw/app/current but
+ * also user/sw/app/current.
+ *
+ * When one of these kdbGet() fails -1 will be returned, but the other
+ * kdbGet() will be tried too.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param name the name where to get the keys below
+ * @param returned the (pre-initialized) KeySet returned with all keys found
+ * @param options ORed options to control approaches
+ *   Unlike to kdbGet() is KDB_O_POP set per default.
+ * @return number of keys contained by @p returned
+ * @return -1 on failure
+ * @return -1 when @p name is no valid key
+ * @return -1 on NULL pointer
+ * @see kdbGet()
+ * @ingroup kdbhighlevel
+ */
+ssize_t kdbGetByName(KDB *handle, KeySet *returned, const char *name,
+               option_t options)
+{
+       Key *parentKey;
+       ssize_t ret, ret2;
+       char *newname;
+
+       if (!handle || !returned || !name) return -1;
+
+       if (name[0] == '/')
+       {
+               newname = kdbiMalloc (strlen (name) + sizeof ("system") + 1);
+               if (!newname)
+               {
+                       /*errno = KDB_ERR_NOMEM;*/
+                       return -1;
+               }
+               strncpy (newname+4, "db", 2);
+               strcpy  (newname+6, name);
+               parentKey = keyNew (newname+4, KEY_END);
+               ret = kdbGetHelper(handle, returned, parentKey, options & ~KDB_O_DEL);
+               keyDel (parentKey);
+
+               strncpy (newname+2, "file",4);
+               parentKey = keyNew (newname+2, KEY_END);
+               ret2 = kdbGetHelper(handle, returned, parentKey, options & ~KDB_O_DEL);
+               keyDel (parentKey);
+
+               strncpy (newname, "memory",6);
+               parentKey = keyNew (newname, KEY_END);
+               ret2 = kdbGetHelper(handle, returned, parentKey, options & ~KDB_O_DEL);
+               keyDel (parentKey);
+
+               kdbiFree (newname);
+               if (ret == -1 || ret2 == -1) return -1;
+               else return returned->size;
+       } else {
+               parentKey=keyNew(name,KEY_END);
+               if (parentKey == 0)
+               {
+                       /*errno = KDB_ERR_NOKEY;*/
+                       return -1;
+               }
+               ret = kdbGet(handle, returned,(Key *) parentKey, (options & ~KDB_O_DEL) | KDB_O_POP);
+               keyDel(parentKey);
+               if (ret == -1) return -1;
+               else return returned->size;
+       }
+}
+
+
+
+/*
+ * Stats the key only for its meta-info from the backend storage.
+ *
+ * The key may not hold value and comment after using kdbStatKey().
+ *
+ * Info like comments and key data type will not be retrieved.
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param key an initialized Key pointer to be filled.
+ * @return 0 on success
+ * @return -1 on failure
+ * @ingroup kdbhighlevel
+ */
+int kdbStatKey(KDB *handle, Key *key)
+{
+       if (!handle || !key) return -1;
+
+       keyStat(key);
+       return kdbGetKey (handle, key);
+}
+
+
diff --git a/src/libelektra/key.c b/src/libelektra/key.c
new file mode 100644 (file)
index 0000000..94978f8
--- /dev/null
@@ -0,0 +1,681 @@
+ /***************************************************************************
+                          key.c  -  Methods for Key manipulation
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/**
+ * @defgroup key Key :: Basic Methods
+ * @brief Key construction and initialization methods.
+ *
+ * To use them:
+ * @code
+#include <kdb.h>
+ * @endcode
+ *
+ * A Key is the essential class that encapsulates key @link keyname name @endlink,
+ * @link keyvalue value @endlink and @link keymeta metainfo @endlink.
+ * Key properties are:
+ * - @link keyname Key name @endlink
+ * - @link keyvalue Key value @endlink
+ * - @link keySetType() Data type @endlink
+ * - @link keyGetComment() Key comment @endlink
+ * - @link keyGetOwner() Key owner @endlink
+ * - @link keymeta UID, GID and filesystem-like mode permissions @endlink
+ * - @link keymeta Mode, change and modification times @endlink
+ *
+ * Described here the methods to allocate and free the key.
+ *
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+/**
+ * A practical way to fully create a Key object in one step.
+ *
+ * This function tries to mimic the C++ way for constructors.
+ *
+ * To just get a key object, simple do:
+ * @code
+Key *k = keyNew(0);
+// work with it
+keyDel (k);
+ * @endcode
+ *
+ * If you want the key object to contain a name, value, comment and other
+ * meta info read on.
+ *
+ * @note When you already have a key with similar properties its
+ * easier and cheaper to keyDup() the key.
+ *
+ * Due to ABI compatibility, the @p Key structure is not defined in kdb.h,
+ * only declared. So you can only declare @p pointers to @p Keys in your
+ * program, and allocate and free memory for them with keyNew()
+ * and keyDel() respectively.
+ * See http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135
+ *
+ * You can call it in many different ways depending on the attribute tags you
+ * pass as parameters. Tags are represented as the #keyswitch_t values,
+ * and tell keyNew() which Key attribute comes next.
+ *
+ * The simplest and minimum way to use it is with no tags, only a key name:
+ * @code
+Key *nullKey,*emptyNamedKey;
+
+// Create a key that has no name, is completely empty, but is initialized
+nullKey=keyNew(0);
+keyDel (nullKey);
+
+// Is the same as above
+nullKey=keyNew("", KEY_END);
+keyDel (nullKey);
+
+// Create and initialize a key with a name and nothing else
+emptyNamedKey=keyNew("user/some/example",KEY_END);
+keyDel (emptyNamedKey);
+ * @endcode
+ *
+ * keyNew() allocates memory for a key object and cleans everything up. After
+ * that, it processes the given argument list.
+ *
+ * The Key attribute tags are the following:
+ * - keyswitch_t::KEY_TYPE \n
+ *   Next parameter is a type of the value. Default assumed is KEY_TYPE_UNDEFINED.
+ *   Set this attribute so that a subsequent KEY_VALUE can toggle to keySetString()
+ *   or keySetBinary() regarding to keyIsString() or keyIsBinary().
+ *   If you don't use KEY_TYPE but a KEY_VALUE follows afterwards, KEY_TYPE_STRING
+ *   will be used.
+ * - keyswitch_t::KEY_SIZE \n
+ *   Define a maximum length of the value. This is especially useful for setting
+ *   a binary key. So make sure you use that before you KEY_VALUE for
+ *   binary keys.
+ * - keyswitch_t::KEY_VALUE \n
+ *   Next parameter is a pointer to the value that will be set to the key
+ *   If no keyswitch_t::KEY_TYPE was used before,
+ *   keyswitch_t::KEY_TYPE_STRING is assumed. If KEY_TYPE was previously
+ *   passed with a KEY_TYPE_BINARY,
+ *   you should have passed KEY_SIZE before! Otherwise it will be cut of
+ *   with first \\0 in string!
+ * - keyswitch_t::KEY_UID, @p keyswitch_t::KEY_GID \n
+ *   Next parameter is taken as the UID (uid_t) or GID (gid_t) that will
+ *   be defined on the key. See keySetUID() and keySetGID().
+ * - keyswitch_t::KEY_MODE \n
+ *   Next parameter is taken as mode permissions (mode_t) to the key.
+ *   See keySetMode().
+ * - keyswitch_t::KEY_DIR \n
+ *   Define that the key is a directory rather than a ordinary key.
+ *   This means its executable bits in its mode are set. This option
+ *   allows the key to have subkeys.
+ *   See keySetDir().
+ * - keyswitch_t::KEY_OWNER \n
+ *   Next parameter is the owner. See keySetOwner().
+ * - keyswitch_t::KEY_COMMENT \n
+ *   Next parameter is a comment. See keySetComment().
+ * - keyswitch_t::KEY_REMOVE \n
+ *   Mark the key to be removed instead of set it.
+ *   See keyRemove().
+ * - keyswitch_t::KEY_STAT \n
+ *   Mark the key to be stated instead of get it.
+ *   See keyStat().
+ * - keyswitch_t::KEY_END \n
+ *   Must be the last parameter passed to keyNew(). It is always
+ *   required, unless the @p keyName is 0.
+ *
+ * @par Example:
+ * @code
+KeySet *ks=ksNew(0);
+
+ksAppendKey(ks,keyNew(0));       // an empty key
+
+ksAppendKey(ks,keyNew("user/sw",              // the name of the key
+       KEY_END));                      // no more args
+
+ksAppendKey(ks,keyNew("user/tmp/ex1",
+       KEY_VALUE,"some data",          // set a string value
+       KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew("user/tmp/ex2",
+       KEY_VALUE,"some data",          // with a simple value
+       KEY_MODE,0777,                  // permissions
+       KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew("user/tmp/ex4",
+       KEY_TYPE,KEY_TYPE_BINARY,       // key type
+       KEY_SIZE,7,                     // assume binary length 7
+       KEY_VALUE,"some data",          // value that will be truncated in 7 bytes
+       KEY_COMMENT,"value is truncated",
+       KEY_OWNER,"root",               // owner (not uid) is root
+       KEY_UID,0,                      // root uid
+       KEY_END));                      // end of args
+
+ksAppendKey(ks,keyNew("user/tmp/ex5",
+       KEY_TYPE,
+               KEY_TYPE_DIR | KEY_TYPE_BINARY,// dir key with a binary value
+       KEY_SIZE,7,
+       KEY_VALUE,"some data",          // value that will be truncated in 7 bytes
+       KEY_COMMENT,"value is truncated",
+       KEY_OWNER,"root",               // owner (not uid) is root
+       KEY_UID,0,                      // root uid
+       KEY_END));                      // end of args
+
+ksDel(ks);
+ * @endcode
+ *
+ * The reference counter (see keyGetRef()) will be initialized
+ * with 0, that means a subsequent call of keyDel() will delete
+ * the key. If you append the key to a keyset the reference counter
+ * will be incremented by one (see keyInc()) and the key can't be
+ * be deleted by a keyDel().
+ *
+ *@code
+Key *k = keyNew(0); // ref counter 0
+ksAppendKey(ks, k); // ref counter of key 1
+ksDel(ks); // key will be deleted with keyset
+ *@endcode
+ *
+ * If you increment only by one with keyInc() the same as said above
+ * is valid:
+ *
+ *@code
+Key *k = keyNew(0); // ref counter 0
+keyIncRef(k); // ref counter of key 1
+keyDel(k);    // has no effect
+keyDecRef(k); // ref counter back to 0
+keyDel(k);    // key is now deleted
+ *@endcode
+ *
+ * If you add the key to more keySets:
+ *
+ *@code
+Key *k = keyNew(0); // ref counter 0
+ksAppendKey(ks1, k); // ref counter of key 1
+ksAppendKey(ks2, k); // ref counter of key 2
+ksDel(ks1); // ref counter of key 1
+ksDel(ks2); // k is now deleted
+ *@endcode
+ *
+ * or use keyInc() more than once:
+ *
+ *@code
+Key *k = keyNew(0); // ref counter 0
+keyIncRef(k); // ref counter of key 1
+keyDel (k);   // has no effect
+keyIncRef(k); // ref counter of key 2
+keyDel (k);   // has no effect
+keyDecRef(k); // ref counter of key 1
+keyDel (k);   // has no effect
+keyDecRef(k); // ref counter is now 0
+keyDel (k); // k is now deleted
+ *@endcode
+ *
+ * they key won't be deleted by a keyDel() as long refcounter is not 0.
+ *
+ * The key's sync bit will always be set for any call, except:
+ * @code
+Key *k = keyNew(0);
+// keyNeedSync() will be false
+ * @endcode
+ *
+ * @param keyName a valid name to the key, or NULL to get a simple
+ *     initialized, but really empty, object 
+ * @see keyDel()
+ * @return a pointer to a new allocated and initialized Key object,
+ *     or NULL if an invalid @p keyName was passed (see keySetName()).
+ * @ingroup key
+ * 
+ */
+Key *keyNew(const char *keyName, ...) {
+       Key * k;
+       va_list va;
+
+       if (keyName) va_start(va,keyName);
+       k = keyVNew (keyName, va);
+       if (keyName) va_end (va);
+
+       return k;
+}
+
+
+Key *keyVNew (const char *keyName, va_list va)
+{
+       Key *key=0;
+       type_t type=0;
+       keyswitch_t action=0;
+       void * value=0;
+       ssize_t valueSize=-1;
+
+       key=(Key *)malloc(sizeof(Key));
+       if (!key) return 0;
+       keyInit(key);
+
+       if (keyName) {
+               keySetName(key,keyName);
+
+               action=va_arg(va, keyswitch_t);
+               while (action) {
+                       switch (action) {
+                               case KEY_TYPE:
+                                       type = (type_t) va_arg(va, type_t);
+                                       keySetType(key, type);
+                                       break;
+                               case KEY_SIZE:
+                                       valueSize=va_arg(va, size_t);
+                                       break;
+                               case KEY_VALUE:
+                                       value = va_arg(va, void *);
+                                       if (valueSize>=0 && keyIsBinary(key))
+                                       {
+                                               keySetBinary(key,value, valueSize);
+                                       } else if (keyIsBinary(key)) {
+                                               valueSize = (ssize_t) kdbiStrLen (value);
+                                               keySetBinary(key,value, valueSize);
+                                       } else {
+                                               keySetString(key,value);
+                                       }
+                                       break;
+                               case KEY_UID:
+                                       keySetUID(key,va_arg(va,uid_t));
+                                       break;
+                               case KEY_GID:
+                                       keySetGID(key,va_arg(va,gid_t));
+                                       break;
+                               case KEY_MODE:
+                                       keySetMode(key,va_arg(va, mode_t));
+                                       break;
+                               case KEY_OWNER:
+                                       keySetOwner(key,va_arg(va,char *));
+                                       break;
+                               case KEY_COMMENT:
+                                       keySetComment(key,va_arg(va,char *));
+                                       break;
+                               case KEY_REMOVE:
+                                       keyRemove(key);
+                                       break;
+                               case KEY_STAT:
+                                       keyStat(key);
+                                       break;
+                               case KEY_DIR:
+                                       keySetDir(key);
+                                       break;
+                               default:
+#if DEBUG
+                                       fprintf (stderr, "Unknown option in keyNew %ld\n", (long int)action);
+#endif
+                                       break;
+                       }
+                       action=va_arg(va, keyswitch_t);
+               }
+       }
+       return key;
+}
+
+/**
+ * Return a duplicate of a key.
+ *
+ * Memory will be allocated as needed for dynamic properties.
+ *
+ * The new key will not be member of any KeySet and
+ * will start with a new reference counter at 0. A
+ * subsequent keyDel() will delete the key.
+ *
+ * @code
+int f (const Key * source)
+{
+       Key * dup = keyDup (source);
+       // work with duplicate
+       keyDel (dup);
+       // everything related to dup is freed
+       // and source is unchanged
+}
+ * @endcode
+ *
+ * Like for a new key after keyNew() a subsequent ksAppend()
+ * makes a KeySet to take care of the lifecycle of the key.
+ *
+ * @code
+int g (const Key * source, KeySet * ks)
+{
+       Key * dup = keyDup (source);
+       // work with duplicate
+       ksAppendKey (ks, dup);
+       // ksDel(ks) will also free the duplicate
+       // source remains unchanged.
+}
+ * @endcode
+ *
+ * Duplication of keys should be preferred to keyNew(),
+ * because data like owner can be filled with a copy
+ * of the key instead of asking the environment.
+ * It can also be optimized in the checks, because the keyname
+ * is known to be valid.
+ *
+ * @param source has to be an initializised source Key
+ * @return 0 failure or on NULL pointer
+ * @return a fully copy of source on success
+ * @see ksAppend(), keyDel()
+ * @see keyClear(), keyNew()
+ * @ingroup key
+ */
+Key* keyDup(const Key *source)
+{
+       Key * dest = 0;
+
+       if (!source) return 0;
+
+       dest = (Key *)malloc(sizeof(Key));
+       if (!dest) return 0;
+       keyInit(dest);
+
+       /* Copy the struct data, including the "next" pointer */
+       *dest=*source;
+
+       /* prepare to set dynamic properties */
+       dest->key=
+       dest->comment=
+       dest->owner=
+       dest->data=0;
+
+       dest->ksReference = 0;
+
+       if (source->key && keySetName(dest,source->key) == -1) goto memerror;
+       if (source->comment && keySetComment(dest,source->comment) == -1) goto memerror;
+       if (source->owner && keySetOwner(dest,source->owner) == -1) goto memerror;
+       if (source->data && keySetRaw(dest,source->data,source->dataSize) == -1) goto memerror;
+       
+       return dest;
+memerror:
+       keyDel (dest);
+       return 0;
+}
+
+
+
+
+/**
+ * Copy or Clear a key.
+ *
+ * Most often you may prefer keyDup() which allocates
+ * a new key and returns a duplication of another key.
+ *
+ * But when you need to copy into an existing key, e.g.
+ * because it was passed by a pointer in a function
+ * you can do so:
+ *
+ * @code
+int h (Key *k)
+{
+       // receive key c
+       keyCopy (k, c);
+       // the caller will see the changed key k
+}
+ * @endcode
+ *
+ * The reference counter will not change for
+ * the destination key. Affiliation to keysets
+ * are also not affected.
+ *
+ * When you pass a NULL-pointer as source the
+ * data of dest will be cleaned completely and
+ * you get a fresh dest key.
+ *
+ * @code
+int g (Key *k)
+{
+       keyCopy (k, 0);
+       // k is now an empty and fresh key
+}
+ * @endcode
+ *
+ * @param dest the key which will be written to
+ * @param source the key which should be copied
+ *     or NULL to clean the destination key
+ * @ingroup key
+ * @return -1 on failure when a NULL pointer
+ *     was passed for dest or a dynamic property could not
+ *     be written.
+ * @return 0 when dest was cleaned
+ * @return 1 when source was successfully copied
+ * @see keyDup() to get a duplication of a key
+ */
+int keyCopy (Key *dest, const Key *source)
+{
+       size_t destref = 0;
+
+       if (!dest) return -1;
+       destref = dest->ksReference;
+
+       /* free everything in dest */
+       keySetName (dest, 0);
+       keySetComment (dest,0);
+       keySetOwner (dest, 0);
+       keySetRaw (dest, 0, 0);
+
+       keyClear (dest);
+
+       if (!source) return 0;
+
+       /* copy all data of structure */
+       *dest=*source;
+
+       /* Set reference properties */
+       dest->ksReference = destref;
+
+       /* prepare to set dynamic properties */
+       dest->key=
+       dest->comment=
+       dest->owner=
+       dest->data=0;
+
+       if (keySetName(dest,source->key) == -1) return -1;
+       if (keySetComment(dest,source->comment) == -1) return -1;
+       if (keySetOwner(dest,source->owner) == -1) return -1;
+       if (keySetRaw(dest,source->data,source->dataSize) == -1) return -1;
+
+       return 1;
+}
+
+
+
+
+
+/**
+ * A destructor for Key objects.
+ *
+ * Every key created by keyNew() must be
+ * deleted with keyDel().
+ *
+ * It is save to delete keys which are
+ * in a keyset, the number of references
+ * will be returned then.
+ *
+ * It is save to delete a nullpointer,
+ * -1 will be returned then.
+ *
+ * @param key the key object to delete
+ * @see keyNew(), keyInc(), keyGetRef()
+ * @return the value of the reference counter
+ *         if the key is within keyset(s)
+ * @return 0 when the key was freed
+ * @return -1 on null pointers
+ * @ingroup key
+ *
+ */
+int keyDel(Key *key) {
+       int rc;
+
+       if (!key) return -1;
+
+       if (key->ksReference > 0)
+       {
+               return key->ksReference;
+       }
+
+       rc=keyClear(key);
+       kdbiFree (key);
+
+       return rc;
+}
+
+/*
+ * Key Object Cleaner.
+ *
+ * Will reset all internal data.
+ *
+ * After this call you will receive a fresh
+ * key.
+ *
+ * @code
+int f (Key *k)
+{
+       keyClear (k);
+       // you have a fresh key k here
+       keySetString (k, "value");
+       // the caller will get an empty key k with an value
+}
+ * @endcode
+ *
+ * @param key the key object to work with
+ * @ingroup key
+ */
+int keyClear(Key *key)
+{
+       if (key->key) free(key->key);
+       if (key->data) free(key->data);
+       if (key->comment) free(key->comment);
+       if (key->owner) free(key->owner);
+
+       memset(key,0,sizeof(Key));
+       keyInit (key);
+
+       return 0;
+}
+
+
+
+/**
+ * Increment the viability of a key object.
+ *
+ * This function is intended for applications
+ * using their own reference counter for
+ * key objects. With it you can increment
+ * the reference and thus avoid destruction
+ * of the object in a subsequent keyDel().
+ *
+ * @code
+Key *k;
+keyInc (k);
+function_that_keyDec(k);
+// work with k
+keyDel (k); // now really free it
+ * @endcode
+ *
+ * The reference counter can't be incremented
+ * once it reached SSIZE_MAX. In that situation
+ * nothing will happen and SSIZE_MAX will be
+ * returned.
+ *
+ * @return the value of the new reference counter
+ * @return -1 on null pointer
+ * @return SSIZE_MAX when maximum exceeded
+ * @param key the key object to work with
+ * @see keyGetRef(), keyDecRef(), keyDel()
+ * @ingroup key
+ */
+ssize_t keyIncRef(Key *key)
+{
+       if (!key) return -1;
+
+       if (key->ksReference < SSIZE_MAX) return ++ key->ksReference;
+       else return SSIZE_MAX;
+}
+
+
+
+/**
+ * Decrement the viability of a key object.
+ *
+ * The reference counter can't be decremented
+ * once it reached 0. In that situation
+ * nothing will happen and 0 will be
+ * returned.
+ *
+ * @return the value of the new reference counter
+ * @return -1 on null pointer
+ * @return 0 when the key is ready to be freed
+ * @param key the key object to work with
+ * @see keyGetRef(), keyDel(), keyIncRef()
+ * @ingroup key
+ */
+ssize_t keyDecRef(Key *key)
+{
+       if (!key) return -1;
+
+       if (key->ksReference>0) return -- key->ksReference;
+       else return 0;
+}
+
+
+
+
+/**
+ * Return how many references the key has.
+ *
+ * The references will be incremented when ksAppendKey()
+ * or ksAppend() uses the key and will be decremented when
+ * ksPop() is used.
+ *
+ * keyDup() will reset the references for dupped
+ * key.
+ *
+ * For your own applications you can use
+ * keyIncRef() and keyDelRef() for reference
+ * counting. Keys with zero references
+ * will be deleted when using keyDel().
+ *
+ * @param key the key object to work with
+ * @return the number of references
+ * @return -1 on null pointer
+ * @see keyIncRef() and keyDecRef()
+ * @ingroup key
+ **/
+ssize_t keyGetRef(const Key *key)
+{
+       if (!key) return -1;
+
+       return key->ksReference;
+}
+
diff --git a/src/libelektra/keyhelpers.c b/src/libelektra/keyhelpers.c
new file mode 100755 (executable)
index 0000000..73416eb
--- /dev/null
@@ -0,0 +1,538 @@
+/***************************************************************************
+                          keyhelpers.c  -  Methods for Key manipulation
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+
+/*
+ * Returns one level of the key name.
+ *
+ * This method is used to skip repeating '/' and to find escaping chars.
+ * Given @p keyName, this method returns a pointer to the next name level
+ * found and changes @p size to the number of bytes on this level name.
+ *
+ * This method is used by keySetName() to cleanup parameters
+ * before being accepted in the Key object.
+ *
+ * @code
+// Lets define a key name with a lot of repeating '/' and escaped '/'
+char *keyName="user////abc/def\/ghi////jkl///";
+char *p;
+size_t size=0;
+int level=0;
+char buffer[20];
+
+p=keyName;
+while (*(p=keyNameGetOneLevel(p+size,&size))) {
+       level++;
+
+       // copy what we found to a buffer, so we can NULL-terminate it
+       strncpy(buffer,p,size);
+       buffer[size]=0;
+
+       printf("Level %d name: \"%s\"\n",level,buffer);
+}
+
+ * The above example will produce the following output:
+ *
+ * @code
+Level 1 name: user
+Level 2 name: abc
+Level 3 name: def\/ghi
+Level 4 name: jkl
+ * @endcode
+ *
+ * @param name the string that will be searched
+ * @param size the number of bytes of level name found in @p keyName until
+ *     the next delimiter ('/')
+ * @return a pointer to the first char of the next level name, it will point to
+ *     NULL when done.
+ * @ingroup keyname
+ */
+char *keyNameGetOneLevel(const char *name, size_t *size) {
+       char *real=(char *)name;
+       size_t cursor=0;
+       int escapeNext=0;
+       int end=0;
+       
+       /* skip all repeating '/' in the begining */
+       while (*real && *real == PATH_SEPARATOR) real++;
+       
+       /* now see where this basename ends handling escaped chars with '\' */
+       while (real[cursor] && ! end) {
+               switch (real[cursor]) {
+                       case '\\':
+                               escapeNext=1;
+                               break;
+                       case PATH_SEPARATOR:
+                               if (! escapeNext) end=1;
+                       default:
+                               escapeNext=0;
+               }
+               ++cursor;
+       }
+       
+       /* if a '/' stopped our loop, balance the counter */
+       if (end) --cursor;
+       
+       *size=cursor;
+       return real;
+}
+
+
+
+
+/*
+ * Gets number of bytes needed to store root name of a key name
+ *
+ * Possible root key names are @p system, @p user or @p "user:someuser" .
+ *
+ * @return number of bytes needed with ending NULL
+ * @param keyName the name of the key
+ * @see keyGetFullRootNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyNameGetFullRootNameSize(const char *keyName) {
+       char *end;
+       int length=strlen(keyName);
+
+       if (!length) return 0;
+
+       /*
+               Possible situations:
+               user:someuser
+               user:someuser/
+               user:someuser/key/name
+               user:some.user/key/name
+               .
+               \.
+               (empty)
+       */
+       end=strchr(keyName,PATH_SEPARATOR);
+       if (!end) /* Reached end of string. Root is entire key. */
+               end = (char *)keyName + length;
+
+       return end-keyName+1;
+}
+
+
+
+/*
+ * Gets number of bytes needed to store root name of a key.
+ *
+ * Possible root key names are @p system or @p user .
+ * This method does not consider the user domain in @p user:username keys.
+ *
+ * @param key the key object to work with
+ * @return number of bytes needed with the ending NULL
+ * @see keyGetFullRootNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyGetRootNameSize(const Key *key) {
+       if (!key->key) return 0;
+
+       if (keyIsUser(key)) return sizeof("db");
+       else if (keyIsSystem(key)) return sizeof("file");
+       else return sizeof("memory");
+}
+
+
+
+/*
+ * Copy to @p returned the root name of @p key.
+ *
+ * Some examples:
+ * - root of @p system/some/key is @p system
+ * - root of @p user:denise/some/key is @p user
+ * - root of @p user/env/env1 is @p user
+ *
+ * Use keyGetFullRootName() to get also the user domain.
+ *
+ * @param key the key to extract root from
+ * @param returned a pre-allocated buffer to store the rootname
+ * @param maxSize size of the @p returned buffer
+ * @return number of bytes needed with ending NULL
+ * @see keyGetRootNameSize(), keyGetFullRootName()
+ * @ingroup keyname
+ */
+ssize_t keyGetRootName(const Key *key, char *returned, size_t maxSize) {
+       size_t size;
+
+       if (!key->key) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return -1;
+       }
+
+       if (!(size=keyGetRootNameSize(key))) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return -1;
+       }
+
+       if (maxSize < size) {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       } else strncpy(returned,key->key,size-1);
+       returned[size-1]=0; /* null terminate it */
+       return size;
+}
+
+
+
+
+
+
+/*
+ * Gets number of bytes needed to store root name of a key name without
+ * user domain.
+ *
+ * Some examples:
+ * - root of @p system/some/key is @p system
+ * - root of @p user:denise/some/key is @p user
+ * - root of @p user/env/env1 is @p user
+ *
+ * @param keyName the name of the key
+ * @return number of bytes needed with ending NULL or -1 if @p keyName is
+ *     not a valid name and @c errno is set to KDBErr::KDB_ERR_INVALIDKEY
+ * @see keyGetRootNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyNameGetRootNameSize(const char *keyName) {
+       int length=strlen(keyName);
+
+       if (!length) return 0;
+
+       /*
+               Possible situations:
+       user
+       user/
+       user/blabla
+       user:someuser
+       user:someuser/
+       user:someuser/key/name
+       system
+       system/
+       system/blabla
+       (empty)
+       */
+       
+       if (keyNameIsUser(keyName)) return sizeof("db");
+       else if (keyNameIsSystem(keyName)) return sizeof("file");
+       else if (keyNameIsMemory(keyName)) return sizeof("memory");
+       else {
+               /*errno=KDB_ERR_INVALIDKEY;*/
+               return -1;
+       }
+}
+
+
+
+
+
+/*
+ * Calculates number of bytes needed to store full root name of a key.
+ *
+ * Possible root key names are @p system, @p user or @p user:someuser.
+ * In contrast to keyGetRootNameSize(), this method considers the user
+ * domain part, and you should prefer this one.
+ *
+ * @param key the key object to work with
+ * @return number of bytes needed with ending NULL
+ * @see keyGetRootNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyGetFullRootNameSize(const Key *key) {
+       size_t size=0;
+
+       if (keyIsUser(key)) {
+               if (key->owner) size=kdbiStrLen(key->owner);
+               
+               return size+sizeof("db");
+       } else {
+               return keyNameGetRootNameSize(key->key);
+       }
+}
+
+
+/*
+ * Copy to @p returned the full root name of the key.
+ *
+ * Some examples:
+ * - root of @p system/some/key is @p system
+ * - root of @p user:denise/some/key is @p user:denise
+ * - root of @p user/env/env1 is @p user:$USER
+ *
+ * This method is more robust then keyGetRootName()
+ *
+ * @param key the key to extract root from
+ * @param returned a pre-allocated buffer to store the rootname
+ * @param maxSize size of the @p returned buffer
+ * @return number of bytes written to @p returned including ending NULL
+ * @see keyGetFullRootNameSize(), keyGetRootName()
+ * @ingroup keyname
+ */
+ssize_t keyGetFullRootName(const Key *key, char *returned, size_t maxSize) {
+       size_t size;
+       size_t rootSize;
+       char *cursor;
+
+       if (!key->key) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return 0;
+       }
+
+       if (!(size=keyGetFullRootNameSize(key))) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return 0;
+       }
+
+       if (maxSize < size) {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       }
+       
+       rootSize = keyGetRootNameSize(key)-1;
+       strncpy(returned,key->key, rootSize); /* copy "user" or "system" */
+       if (keyIsUser(key)) {
+               cursor = returned + rootSize;
+               *cursor = ':'; cursor++;
+               if (key->owner)
+               {
+                       strncpy (cursor, key->owner, size - rootSize - 1); /* -1 for : */
+               }
+       } else {
+               returned[rootSize]=0;
+       }
+
+       return size;
+}
+
+
+
+
+
+
+/*
+ * Get the number of bytes needed to store this key's parent name without
+ * user domain, and with the ending NULL.
+ *
+ * @param key the key object to work with
+ * @see keyGetParentName() for example
+ * @ingroup keyname
+ */
+ssize_t keyGetParentNameSize(const Key *key) {
+       char *parentNameEnd=0;
+       char *p;
+       size_t size;
+
+       if (!key->key) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return 0;
+       }
+
+       /*
+               user   (size=0)
+               user/parent/base       (size=sizeof("user/parent"))
+               user/parent/base/      (size=sizeof("user/parent"))
+               user/parent/base\/name (size=sizeof("user/parent"))
+       */
+
+       /* initialize */
+       p=key->key;
+       size=0;
+       
+       /* iterate over level names */
+       while (*(p=keyNameGetOneLevel(p+size,&size))) parentNameEnd=p;
+       
+       /* handle NULL or root key ("user" or "system") */
+       if (!parentNameEnd || parentNameEnd==key->key) return 0;
+
+       /* at this point, parentNameEnd points to the first char of the basename */
+       /* example: it points to the 'b' of "user/key/basename" */
+       
+       /* the delta is the size we want */
+       return parentNameEnd - key->key;
+}
+
+
+
+/*
+ * Copy this key's parent name (without owner) into a pre-allocated buffer.
+ *
+ * @par Example:
+ * @code
+Key *key=keyNew("system/parent/base",KEY_SWITCH_END);
+char *parentName;
+size_t parentSize;
+
+parentSize=keyGetParentNameSize(key);
+parentName=malloc(parentSize);
+keyGetParentName(key,parentName,parentSize);
+// work with parentName
+free (parentName);
+ * @endcode
+ * @see keyGetParentNameSize()
+ * @param key the key object to work with
+ * @param returnedParent pre-allocated buffer to copy parent name to
+ * @param maxSize number of bytes pre-allocated
+ * @return number of bytes copied including ending NULL
+ * @ingroup keyname
+ */
+ssize_t keyGetParentName(const Key *key, char *returnedParent, size_t maxSize) {
+       ssize_t parentSize;
+
+       parentSize=keyGetParentNameSize(key);
+
+       if (parentSize > maxSize) {
+               /*errno=KDB_ERR_TRUNC;*/
+               return 0;
+       } else strncpy(returnedParent,key->key,parentSize);
+
+       returnedParent[parentSize-1]=0; /* ending NULL */
+       
+       return parentSize;
+}
+
+
+
+
+/*
+ * Return the namespace of a key name.
+ *
+ * Currently valid namespaces are KeyNamespace::KEY_NS_SYSTEM and KeyNamespace::KEY_NS_USER.
+ *
+ * @param keyName the name to deduce the namespace from
+ * @return KeyNamespace::KEY_NS_SYSTEM, KeyNamespace::KEY_NS_USER
+ * @return 0 for no valid namespace found (key has no name)
+ * @see keyGetNamespace(), keyIsUser(), keyIsSystem()
+ * @see #KeyNamespace
+ * @ingroup keytest
+ *
+ */
+int keyNameGetNamespace(const char *keyName)
+{
+       if (keyNameIsSystem(keyName)) return KEY_NS_SYSTEM;
+       if (keyNameIsUser(keyName)) return KEY_NS_USER;
+       if (keyNameIsMemory(keyName)) return KEY_NS_MEMORY;
+       return 0;
+}
+
+
+
+/*
+ * Return the namespace of a key
+ *
+ * Currently valid namespaces are KeyNamespace::KEY_NS_SYSTEM and KeyNamespace::KEY_NS_USER.
+ *
+ * @param key the key object to work with
+ * @return 0
+ * @see keyIsUser(), keyIsSystem()
+ * @ingroup keytest
+ *
+ */
+int keyGetNamespace(const Key *key)
+{
+       if (keyIsUser (key)) return KEY_NS_USER;
+       else if (keyIsSystem (key)) return KEY_NS_SYSTEM;
+       else if (keyIsMemory(key)) return KEY_NS_MEMORY;
+       else return 0;
+}
+
+
+/*
+ * Check whether a key name is under the @p system namespace or not
+ *
+ * @return 1 if string begins with @p system , 0 otherwise
+ * @param keyName the name of a key
+ * @see keyIsSystem(), keyIsUser(), keyNameIsUser()
+ * @ingroup keyname
+ *
+ */
+int keyNameIsSystem(const char *keyName)
+{
+       if (!strncmp("file",keyName,sizeof("file")-1)) return 1;
+       return 0;
+}
+
+
+/*
+ * Check whether a key name is under the @p user namespace or not
+ *
+ * @return 1 if string begins with @p user, 0 otherwise
+ * @param keyName the name of a key
+ * @see keyIsSystem(), keyIsUser(), keyNameIsSystem()
+ * @ingroup keyname
+ *
+ */
+int keyNameIsUser(const char *keyName)
+{
+       if (!strncmp("db",keyName,sizeof("db")-1)) return 1;
+       return 0;
+}
+
+
+/*
+ * Check whether a key name is under the @p memory namespace or not
+ *
+ * @return 1 if string begins with @p user, 0 otherwise
+ * @param keyName the name of a key
+ * @see keyIsSystem(), keyIsUser(), keyNameIsSystem()
+ * @ingroup keyname
+ *
+ */
+int keyNameIsMemory(const char *keyName)
+{
+       if (!strncmp("memory",keyName,sizeof("memory")-1)) return 1;
+       return 0;
+}
+
+
+/*********************************************************************
+ *                Data constructors (protected)                      *
+ *********************************************************************/
+int keyInit(Key *key)
+{
+       memset(key,0,sizeof(Key));
+
+       key->type = KEY_TYPE_UNDEFINED;
+
+       key->uid = (uid_t)-1;
+       key->gid = (gid_t)-1;
+
+       key->mode = KEY_DEF_MODE;
+       return 0;
+}
+
diff --git a/src/libelektra/keymeta.c b/src/libelektra/keymeta.c
new file mode 100644 (file)
index 0000000..7b2d4d3
--- /dev/null
@@ -0,0 +1,641 @@
+ /***************************************************************************
+                      keymeta.c  -  Methods for Key manipulation
+                             -------------------
+    begin                : Fri Sep 26 2008
+    copyright            : (C) 2008 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/**
+ * @defgroup keymeta Key :: Meta Info Manipulation Methods
+ * @brief Methods to do various operations on Key metainfo
+ *
+ * To use them:
+ * @code
+#include <kdb.h>
+ * @endcode
+ *
+ * Next to \link keyname Name (key and owner) \endlink and 
+ * \link keyvalue Value (data and comment) \endlink there
+ * is the so called metainfo inside every key.
+ *
+ * Key metainfo insists of:
+ * - UID, the user id
+ * - GID, the group id 
+ * - filesystem-like mode permissions (rwx)
+ * - Mode, change and modification times
+ *
+ * The comment can contain userdata which directly
+ * belong to that key.
+ *
+ * Owner is the user that owns the key. It only
+ * works for the user/ hierachy.
+ *
+ * Every user and group of your System has a uniqe ID.
+ * These values are used in the keys too. They are
+ * very important for the mode. See man 2 chown.
+ *
+ * With the mode mode you can choose if a user, group
+ * or the world can mode your key. See man 2 chmod.
+ *
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+
+/**
+ * Only stat a key instead of receiving value, comment and key type.
+ *
+ * Only stat the key in the database when doing
+ * kdbGet(). The key may not have any value, comment
+ * or key type set.
+ *
+ * It is not possible to revert the action on per-key basis.
+ * When you want to remove the flag you have to pass
+ * option_t::KDB_O_NOSTAT to the next kdbGet().
+ *
+ * @see keyNeedStat(), kdbGet()
+ * @param key the key object to work with
+ * @return 1 on succuess
+ * @return -1 on NULL pointer
+ *
+ * @ingroup keymeta
+ */
+int keyStat(Key *key)
+{
+       if (!key) return -1;
+
+       key->flags |= KEY_FLAG_STAT;
+       return 1;
+}
+
+
+/**
+ * Permanently remove a key after committing to database.
+ *
+ * This functions sets a flag that the key needs to be removed.
+ * It also sets a flag that it is not synced.
+ *
+ * Remove the key instead of writing it in the key database when doing
+ * kdbSet() and related functions.
+ *
+ * This key will be ignored and it is save to delete it afterwards.
+ * To be sure that it was removed, check if it needs sync with
+ * keyNeedSync().
+ *
+ * @note Delete in elektra terminology means to free memory,
+ * remove means to free permanent storage.
+ *
+ * @warning You should not change a key's remove status once it belongs to a keyset.
+ * See ksSort() for more information.
+ *
+ * @see keyNeedRemove(), kdbSet(), kdbRemove()
+ * @param key the key object to work with
+ * @return 1 on success
+ * @return -1 on NULL pointer
+ *
+ * @ingroup keymeta
+ */
+int keyRemove(Key *key) {
+       if (!key) return -1;
+
+       key->flags |= KEY_FLAG_REMOVE;
+       key->flags |= KEY_FLAG_SYNC;
+       return 1;
+}
+
+
+/*********************************************
+ *       UID, GID access methods             *
+ *********************************************/
+
+
+
+/**
+ * Get the user ID of a key.
+ *
+ * @section UID UID
+ *
+ * The user ID is a unique identification for every user present on a
+ * system. Keys will belong to root (0) as long as you did not get their
+ * real UID with kdbGet().
+ *
+ * Although usually the same, the UID of a key is not related to its owner.
+ *
+ * A fresh key will have (uid_t)-1 also known as the user nobody.
+ * It means that the key is not related to a user ID at the moment.
+ *
+ * @param key the key object to work with
+ * @return the system's UID of the key
+ * @return (uid_t)-1 on NULL key or currently unknown ID
+ * @see keyGetGID(), keySetUID(), keyGetOwner()
+ * @ingroup keymeta
+ */
+uid_t keyGetUID(const Key *key)
+{
+       if (!key) return (uid_t)-1;
+
+       return key->uid;
+}
+
+
+
+/**
+ * Set the user ID of a key.
+ *
+ * See @ref UID for more information about user IDs.
+ *
+ * @param key the key object to work with
+ * @param uid the user ID to set
+ * @return 0 on success
+ * @return -1 on NULL key
+ * @see keySetGID(), keyGetUID(), keyGetOwner()
+ * @ingroup keymeta
+ */
+int keySetUID(Key *key, uid_t uid)
+{
+       if (!key) return -1;
+
+       key->uid=uid;
+       key->flags |= KEY_FLAG_SYNC;
+
+       return 0;
+}
+
+
+
+/**
+ * Get the group ID of a key.
+ *
+ * @section GID GID
+ *
+ * The group ID is a unique identification for every group present on
+ * a system. Keys will belong to root (0) as long as you did not get their
+ * real GID with kdbGet().
+ *
+ * Unlike UID users might change their group. This makes it possible to
+ * share configuration between some users.
+ *
+ * A fresh key will have (gid_t)-1 also known as the group nogroup.
+ * It means that the key is not related to a group ID at the moment.
+ *
+ * @param key the key object to work with
+ * @return the system's GID of the key
+ * @return (gid_t)-1 on NULL key or currently unknown ID
+ * @see keySetGID(), keyGetUID()
+ * @ingroup keymeta
+ */
+gid_t keyGetGID(const Key *key)
+{
+       if (!key) return (gid_t)-1;
+
+       return key->gid;
+}
+
+
+
+/**
+ * Set the group ID of a key.
+ *
+ * See @ref GID for more information about group IDs.
+ *
+ * @param key the key object to work with
+ * @param gid is the group ID
+ * @return 0 on success
+ * @return -1 on NULL key
+ * @see keyGetGID(), keySetUID()
+ * @ingroup keymeta
+ */
+int keySetGID(Key *key, gid_t gid)
+{
+       if (!key) return -1;
+
+       key->gid=gid;
+       key->flags |= KEY_FLAG_SYNC;
+
+       return 0;
+}
+
+
+
+
+/**
+ * Set mode so that key will be recognized as directory.
+ *
+ * The function will add all executable bits.
+ *
+ * - Mode 0200 will be translated to 0311
+ * - Mode 0400 will be translated to 0711
+ * - Mode 0664 will be translated to 0775
+ *
+ * The macro KEY_DEF_DIR (defined to 0111) will be used for that.
+ *
+ * The executable bits show that child keys are allowed and listable. There
+ * is no way to have child keys which are not listable for anyone, but it is
+ * possible to restrict listing the keys to the owner only.
+ *
+ * - Mode 0000 means that it is a key not read or writable to anyone.
+ * - Mode 0111 means that it is a directory not read or writable to anyone.
+ *   But it is recognized as directory to anyone.
+ *
+ * For more about mode see keySetMode().
+ *
+ * It is not possible to access keys below a not executable key.
+ * If a key is not writeable and executable kdbSet() will fail to access the
+ * keys below.
+ * If a key is not readable and executable kdbGet() will fail to access the
+ * keys below.
+ *
+ * @param key the key to set permissions to be recognized as directory.
+ * @return 0 on success
+ * @return -1 on NULL pointer
+ * @see keySetMode()
+ * @ingroup keymeta
+ */
+int keySetDir(Key *key)
+{
+       if (!key) return -1;
+
+       key->mode |= KEY_DEF_DIR;
+       key->flags |= KEY_FLAG_SYNC;
+       
+       return 0;
+}
+
+
+
+/**
+ * Return the key mode permissions.
+ *
+ * Default is 0664 (octal) for keys and 0775 for directory keys
+ * which used keySetDir().
+ *
+ * The defaults are defined with the macros KEY_DEF_MODE and KEY_DEF_DIR.
+ *
+ * For more information about the mode permissions see @ref mode.
+ *
+ * @param key the key object to work with
+ * @return mode permissions of the key
+ * @return (mode_t)-1 on NULL pointer
+ * @see keySetMode()
+ * @ingroup keymeta
+ */
+mode_t keyGetMode(const Key *key)
+{
+       if (!key) return (mode_t) -1;
+
+       return key->mode;
+}
+
+
+
+/**
+ * Set the key mode permissions.
+ *
+ * The mode consists of 9 individual bits for mode permissions.
+ * In the following explanation the octal notation with leading
+ * zero will be used.
+ *
+ * Default is 0664 (octal) for keys and 0775 for directory keys
+ * which used keySetDir().
+ *
+ * The defaults are defined with the macros KEY_DEF_MODE and KEY_DEF_DIR.
+ *
+ * @note libelektra 0.7.0 only allows 0775 (directory keys) and
+ * 0664 (other keys). More will be added later in a sense of the
+ * description below.
+ *
+ * @section mode Modes
+ *
+ * 0000 is the most restrictive mode. No user might read, write
+ * or execute the key.
+ *
+ * Reading the key means to get the value and comment by kdbGet()
+ * or all highlevel methods.
+ *
+ * Writing the key means to set the value and comment by kdbSet()
+ * or all highlevel methods.
+ *
+ * Execute the key means to make a step deeper in the hierarchy.
+ * But you must be able to read the key to be able to list the
+ * keys below. See also keySetDir() in that context.
+ * But you must be able to write the key to be able to add or
+ * remove keys below.
+ *
+ * 0777 is the most relaxing mode. Every user is allowed to
+ * read, write and execute the key, if he is allowed to execute
+ * and read all keys below.
+ *
+ * 0700 allows every action for the current user, identified by
+ * the uid. See keyGetUID() and keySetUID().
+ *
+ * To be more specific for the user the single bits can elect
+ * the mode for read, write and execute. 0100 only allows
+ * executing which gives the information that it is a directory
+ * for that user, but not accessable. 0200 only allows reading.
+ * This information may be combined to 0300, which allows execute
+ * and reading of the directory. Last 0400 decides about the
+ * writing permissions.
+ *
+ * The same as above is also valid for the 2 other octal digits.
+ * 0070 decides about the group permissions, in that case full
+ * access. Groups are identified by the gid. See keyGetGID() and
+ * keySetGID(). In that example everyone with a different uid,
+ * but the gid of the the key, has full access.
+ *
+ * 0007 decides about the world permissions. This is taken into
+ * account when neighter the uid nor the gid matches. So that
+ * example would allow everyone with a different uid and gid
+ * of that key gains full access.
+ *
+ * @param key the key to set mode permissions
+ * @param mode the mode permissions
+ * @return 0 on success
+ * @return -1 on NULL key
+ * @see keyGetMode()
+ * @ingroup keymeta
+ */
+int keySetMode(Key *key, mode_t mode)
+{
+       if (!key) return -1;
+
+       key->mode=mode;
+       key->flags |= KEY_FLAG_SYNC;
+
+       return 0;
+}
+
+
+
+/**
+ * Returns the key data type.
+ *
+ * See #type_t for the type definition.
+ *
+ * @see keySetType()
+ * @see keyIsBinary() and keyIsString()
+ * @see keyIsDir() is not related to the type system
+ * @param key key where to get the type.
+ * @return the key type
+ * @return KEY_TYPE_UNDEFINED on keys without type
+ * @return -1 on NULL pointer
+ * @ingroup keymeta
+ *
+ */
+type_t keyGetType(const Key *key)
+{
+       if (!key) return -1;
+
+       return key->type;
+}
+
+
+
+/**
+ * Set a new key type.
+ *
+ * This method is usually not needed, unless you are working with more
+ * semantic value types, or want to force a specific value type for a key.
+ * It is not usually needed because the data type is automatically set
+ * when setting the key value.
+ *
+ * See #type_t for the type defintion.
+ *
+ * @par Example:
+ * @code
+// here we define the new type
+enum
+{
+       KEY_TYPE_COLOR=KEY_TYPE_STRING+4
+};
+// here we make a new key with the type
+Key *k1 = keyNew ("user/sw/oyranos/current/color1",
+       KEY_VALUE, "#4B52CA",
+       KEY_COMMENT, "a custom color",
+       KEY_TYPE, KEY_TYPE_COLOR,
+       KEY_END);
+// lets check if it is really correct type
+if (keyGetType(k1) == KEY_TYPE_COLOR) printf ("correct type");
+ * @endcode
+ *
+ * When using type_t::KEY_TYPE_DIR, this method will not set mode
+ * permissions to the key. You'll have to set it manually after
+ * keySetType(), calling keySetMode() with appropriate permissions.
+ * Or use the keySetDir().
+ *
+ * @see keyGetType()
+ * @see keySetDir() to see that the directory concept is independent of types
+ * @param key the key object to work with
+ * @param newType contains the new type
+ * @return 0 on sucess
+ * @return -1 on NULL pointer and when newType >= KEY_TYPE_MAX
+ * @ingroup keymeta
+ *
+ */
+int keySetType(Key *key, type_t newType)
+{
+       if (!key) return -1;
+
+       if (newType >= KEY_TYPE_MAX) return -1;
+
+       key->type=newType;
+       key->flags |= KEY_FLAG_SYNC;
+       return 0;
+}
+
+
+/*********************************************
+ *    Access times methods                   *
+ *********************************************/
+
+
+/**
+ * Get last time the key data was read from disk.
+ *
+ * Every kdbGet() might update the access time
+ * of a key. You get information when the key
+ * was read the last time from the database.
+ *
+ * You will get 0 when the key was not read already.
+ *
+ * Beware that multiple copies of keys with keyDup() might have different
+ * atimes because you kdbGet() one, but not the
+ * other. You can use this information to decide which
+ * key is the latest.
+ *
+ * @param key Key to get information from.
+ * @return the time you got the key with kdbGet()
+ * @return 0 on key that was never kdbGet()
+ * @return (time_t)-1 on NULL pointer
+ * @see keySetATime()
+ * @see kdbGet()
+ * @ingroup keymeta
+ */
+time_t keyGetATime(const Key *key)
+{
+       if (!key) return (time_t)-1;
+
+       return key->atime;
+}
+
+/**
+ * Update the atime information for a key.
+ *
+ * When you do manual sync of keys you might also
+ * update the atime to make them indistinguishable.
+ *
+ * It can also be useful if you work with
+ * keys not using a keydatabase.
+ *
+ * @param key The Key object to work with
+ * @param atime The new access time for the key
+ * @return 0 on success
+ * @return -1 on NULL pointer
+ * @see keyGetATime()
+ * @ingroup keymeta
+ */
+int keySetATime(Key *key, time_t atime)
+{
+       if (!key) return -1;
+
+       key->atime = atime;
+       return 0;
+}
+
+
+/**
+ * Get last modification time of the key on disk.
+ *
+ * You will get 0 when the key was not read already.
+ *
+ * Everytime you change value or comment and kdbSet()
+ * the key the mtime will be updated. When you kdbGet()
+ * the key, the atime is set appropriate.
+ *
+ * Not changed keys may not even passed to kdbSet_backend()
+ * so it will not update this time, even after kdbSet().
+ *
+ * It is possible that other keys written to disc
+ * influence this time if the backend is not grained
+ * enough.
+ *
+ * If you add or remove a key the key thereunder
+ * in the hierarchy will update the mtime
+ * if written with kdbSet() to disc.
+ *
+ * @param key Key to get information from.
+ * @see keySetMTime()
+ * @return the last modification time
+ * @return (time_t)-1 on NULL pointer
+ * @ingroup keymeta
+ */
+time_t keyGetMTime(const Key *key)
+{
+       if (!key) return (time_t)-1;
+
+       return key->mtime;
+}
+
+/**
+ * Update the mtime information for a key.
+ *
+ * @param key The Key object to work with
+ * @param mtime The new modification time for the key
+ * @return 0 on success
+ * @see keyGetMTime()
+ * @ingroup keymeta
+ */
+int keySetMTime(Key *key, time_t mtime)
+{
+       if (!key) return -1;
+
+       key->mtime = mtime;
+       return 0;
+}
+
+
+/**
+ * Get last time the key metadata was changed from disk.
+ *
+ * You will get 0 when the key was not read already.
+ *
+ * Any changed field in metadata will influence the
+ * ctime of a key.
+ *
+ * This time is not updated if only value
+ * or comment are changed.
+ *
+ * Not changed keys will not update this time,
+ * even after kdbSet().
+ *
+ * It is possible that other keys written to disc
+ * influence this time if the backend is not grained
+ * enough.
+ *
+ * @param key Key to get information from.
+ * @see keySetCTime()
+ * @return (time_t)-1 on NULL pointer
+ * @return the metadata change time
+ * @ingroup keymeta
+ */
+time_t keyGetCTime(const Key *key)
+{
+       if (!key) return (time_t)-1;
+
+       return key->ctime;
+}
+
+
+/**
+ * Update the ctime information for a key.
+ *
+ * @param key The Key object to work with
+ * @param ctime The new change metadata time for the key
+ * @return 0 on success
+ * @return -1 on NULL pointer
+ * @see keyGetCTime()
+ * @ingroup keymeta
+ */
+int keySetCTime(Key *key, time_t ctime)
+{
+       if (!key) return -1;
+
+       key->ctime = ctime;
+       return 0;
+}
+
+
diff --git a/src/libelektra/keyname.c b/src/libelektra/keyname.c
new file mode 100755 (executable)
index 0000000..9a9755d
--- /dev/null
@@ -0,0 +1,993 @@
+ /***************************************************************************
+                      keyname.c  -  Methods for Key manipulation
+                             -------------------
+    begin                : Fri Sep 26 2008
+    copyright            : (C) 2008 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+
+/**
+ * @defgroup keyname Key :: Name Manipulation Methods
+ * @brief Methods to do various operations on Key names.
+ *
+ * To use them:
+ * @code
+#include <kdb.h>
+* @endcode
+ *
+ * These functions make it easier for c programmers to work with key names.
+ * Everything here can also be done with keySetName, described in key.
+ *
+ *
+ * @par Rules for Key Names
+ *
+ * When using Elektra to store your application's configuration and state,
+ * please keep in mind the following rules:
+ * - You are not allowed to create keys right under @p system or @p user.
+ * - You are not allowed to create folder keys right under @p system or @p user.
+ *   They are reserved for very essential OS subsystems.
+ * - The keys for your application, called say @e MyApp, should be created under
+ *   @p system/sw/MyApp and/or @p user/sw/MyApp.
+ * - It is suggested to make your application look for default keys under
+ *   @p system/sw/MyApp/current and/or @p user/sw/MyApp/current. This way, from
+ *   a sysadmin perspective, it will be possible to copy the
+ *   @p system/sw/MyApp/current tree to something like @p system/sw/MyApp/old,
+ *   and keep system clean and organized.
+ * - \\0 must not occur in names.
+ * - / is the seperator.
+ *
+ */
+
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+
+
+/*******************************************
+ *    General name manipulation methods    *
+ *******************************************/
+
+
+/**
+ * Returns a pointer to the abbreviated real internal @p key name.
+ *
+ * This is a much more efficient version of keyGetName() and can use
+ * it if you are responsible enough to not mess up things. You are not allowed
+ * to change anything in the returned array. The content of that string
+ * may change after keySetName() and similar functions. If you need a copy of the name,
+ * consider using keyGetName().
+ *
+ * The name will be without owner, see keyGetFullName() if
+ * you need the name with its owner.
+ *
+ * keyName() returns "" when there is no keyName. The reason is
+ * @code
+key=keyNew(0);
+keySetName(key,"");
+keyName(key); // you would expect "" here
+keyDel(key);
+ * @endcode
+ *
+ * @note Note that the Key structure keeps its own size field that is calculated
+ * by library internal calls, so to avoid inconsistencies, you
+ * must never use the pointer returned by keyName() method to set a new
+ * value. Use keySetName() instead.
+ *
+ * @param key the key object to work with
+ * @return a pointer to the keyname which must not be changed.
+ * @return "" when there is no (a empty) keyname
+ * @return 0 on NULL pointer
+ * @see keyGetNameSize() for the string length
+ * @see keyGetFullName(), keyGetFullNameSize() to get the full name
+ * @see keyGetName() as alternative to get a copy
+ * @see keyOwner() to get a pointer to owner
+ * @ingroup keyname
+ */
+const char *keyName(const Key *key)
+{
+       if (!key) return 0;
+
+       if (!key->key) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return "";
+       }
+
+       return key->key;
+}
+
+
+/**
+ * Bytes needed to store the key name without owner.
+ *
+ * For an empty key name you need one byte to store the ending NULL.
+ * For that reason 1 is returned.
+ *
+ * @param key the key object to work with
+ * @return number of bytes needed, including ending NULL, to store key name
+ *     without owner
+ * @return 1 if there is is no key Name
+ * @return -1 on NULL pointer
+ * @see keyGetName(), keyGetFullNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyGetNameSize(const Key *key)
+{
+       if (!key) return -1;
+
+       if (!key->key)
+       {
+               /*errno = KDB_ERR_NOKEY;*/
+               return 1;
+       }
+       else return key->keySize;
+}
+
+
+
+
+/**
+ * Get abbreviated key name (without owner name).
+ *
+ * When there is not enough space to write the name,
+ * nothing will be written and -1 will be returned.
+ *
+ * maxSize is limited to SSIZE_MAX. When this value
+ * is exceeded -1 will be returned. The reason for that
+ * is that any value higher is just a negative return
+ * value passed by accident. Of course malloc is not
+ * as failure tolerant and will try to allocate.
+ *
+ * @return number of bytes written to @p returnedName
+ * @return 1 when only a null was written
+ * @return -1 when keyname is longer then maxSize or 0 or any NULL pointer
+ * @param key the key object to work with
+ * @param returnedName pre-allocated memory to write the key name
+ * @param maxSize maximum number of bytes that will fit in returnedName, including the final NULL
+ * @see keyGetNameSize(), keyGetFullName(), keyGetFullNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyGetName(const Key *key, char *returnedName, size_t maxSize)
+{
+       if (!key) return -1;
+
+       if (!returnedName) return -1;
+
+       if (!maxSize) return -1;
+
+       if (maxSize > SSIZE_MAX) return -1;
+
+       if (!key->key) {
+               /*errno=KDB_ERR_NOKEY;*/
+               returnedName[0]=0;
+               return 1;
+       }
+
+       if (key->keySize > maxSize) {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       }
+
+       strncpy(returnedName,key->key,maxSize);
+
+       return key->keySize;
+}
+
+
+
+
+/**
+ * Set a new name to a key.
+ *
+ * A valid name is of the forms:
+ * - @p system/something
+ * - @p user/something
+ * - @p user:username/something
+ *
+ * The last form has explicitly set the owner, to let the library
+ * know in which user folder to save the key. A owner is a user name.
+ * If not defined (the second form) current user is calculated and used
+ * as default.
+ *
+ * You should always follow the guidelines for key tree structure creation.
+ *
+ * A private copy of the key name will be stored, and the @p newName
+ * parameter can be freed after this call.
+ *
+ * .., . and / will be handled correctly. A valid name will be build
+ * out of the (valid) name what you pass, e.g. user///sw/../sw//././MyApp -> user/sw/MyApp
+ *
+ * On invalid names, NULL or "" the name will be "" afterwards.
+ *
+ * @warning You should not change a keys name once it belongs to a keyset.
+ * See ksSort() for more information.
+ *
+ * @return size in bytes of this new key name including ending NULL
+ * @return -1 if @p newName is empty or invalid or any NULL pointer
+ * @param key the key object to work with
+ * @param newName the new key name
+ * @see keyNew(), keySetOwner()
+ * @see keyGetName(), keyGetFullName(), keyName()
+ * @see keySetBaseName(), keyAddBaseName() to manipulate a name
+ * @ingroup keyname
+ */
+
+ssize_t keySetName(Key *key, const char *newName)
+{
+       size_t length;
+       size_t rootLength, userLength, systemLength, ownerLength, memoryLength;
+       char *p=0;
+       size_t size=0;
+
+       if (!key) return -1;
+
+       if (key->key) free (key->key);
+       key->key = 0;
+       key->keySize=1; /* equal to length plus room for \\0 */
+
+       /* handle null new key name, removing the old name */
+       if (!newName || !(length=kdbiStrLen(newName)-1))
+       {
+               if (key->key) {
+                       key->keySize = 0;
+                       free(key->key);
+                       key->key=0;
+               }
+               key->type = 0;
+               return 0;
+       }
+
+       rootLength=keyNameGetFullRootNameSize(newName)-1;
+       if (!rootLength) {
+               /*errno=KDB_ERR_INVALIDKEY;*/
+               return -1;
+       }
+       userLength=sizeof("db")-1;
+       systemLength=sizeof("file")-1;
+       memoryLength=sizeof("memory")-1;
+       ownerLength=rootLength-userLength;
+       
+       if (ownerLength>0) --ownerLength;
+
+       if (keyNameIsUser(newName))
+       {
+               /* handle "user*" */
+               if (length > userLength)
+               {
+                       /* handle "user?*" */
+                       if (*(newName+userLength)==':')
+                       {
+                               /* handle "user:*" */
+                               if (ownerLength > 0)
+                               {
+                                       p=realloc(key->owner,ownerLength+1);
+                                       if (NULL==p) goto error_mem;
+                                       key->owner=p;
+                                       strncpy(key->owner,newName+userLength+1,ownerLength);
+                                       key->owner[ownerLength]=0;
+                                       key->ownerSize = ownerLength+1;
+                               }
+                               key->keySize+=length-ownerLength-1;  /* -1 is for the ':' */
+                       } else if (*(newName+userLength)!=PATH_SEPARATOR) {
+                               /* handle when != "user/ *" */
+                               /*errno=KDB_ERR_INVALIDKEY;*/
+                               key->key = 0;
+                               key->keySize = 1;
+                               return -1;
+                       } else {
+                               /* handle regular "user/ *" */
+                               key->keySize+=length;
+                       }
+               } else {
+                       /* handle "user" */
+                       key->keySize+=userLength;
+               }
+
+               if (!key->owner)
+               {
+                       char *envVar;
+
+                       envVar = getenv("KDB_USER");
+                       if ( envVar ) {
+                               keySetOwner(key, envVar);
+                       } else {
+                               envVar = getenv ("USER");
+                               if ( envVar ) {
+                                       keySetOwner(key, envVar);
+                               } else {
+                                       keySetOwner(key, NULL);
+                               }
+                       }
+               }
+
+               rootLength  = userLength;
+       } else if (keyNameIsSystem(newName)) {
+               /* handle "system*" */
+               if (length > systemLength && *(newName+systemLength)!=PATH_SEPARATOR)
+               {       /* handle when != "system/ *" */
+                       /*errno=KDB_ERR_INVALIDKEY;*/
+                       key->key = 0;
+                       key->keySize = 1;
+                       return -1;
+               }
+               key->keySize+=length;
+
+               keySetOwner (key, NULL);
+
+               rootLength  = systemLength;
+       } else if (keyNameIsMemory(newName)){
+               if (length > memoryLength && *(newName+memoryLength)!=PATH_SEPARATOR)
+               {       /* handle when != "memory/ *" */
+                       /*errno=KDB_ERR_INVALIDKEY;*/
+                       key->key = 0;
+                       key->keySize = 1;
+                       return -1;
+               }
+               key->keySize+=length;
+
+               keySetOwner (key, NULL);
+
+               rootLength  = memoryLength;
+       } else {
+               /* Given newName is neither "system" or "user" */
+               /*errno=KDB_ERR_INVALIDKEY;*/
+               key->key = 0;
+               key->keySize = 1;
+
+               keySetOwner (key, NULL);
+
+               return -1;
+       }
+
+       /*
+          At this point:
+          - key->key has no memory (re)allocated yet
+          - key->keySize has number of bytes that will be allocated for key name
+            with already removed owner.
+          - key->owner is already set
+          - rootLength is sizeof("user")-1 or sizeof("system")-1
+       */
+
+       /* Allocate memory for key->key */
+       p=malloc(key->keySize);
+       if (NULL==p) goto error_mem;
+       if (key->key) free(key->key);
+       key->key=p;
+
+       /* here key->key must have a correct size allocated buffer */
+       if (!key->key) return -1;
+
+       /* copy the root of newName to final destination */
+       strncpy(key->key,newName,rootLength);
+       
+       /* skip the root */
+       p=(char *)newName;
+       size=0;
+       p=keyNameGetOneLevel(p+size,&size);
+       
+       /* iterate over each single folder name removing repeated '/' and escaping when needed */
+       key->keySize=rootLength;
+       while (*(p=keyNameGetOneLevel(p+size,&size))) {
+               /* printf ("level: %s, size: %d\n", p, size); */
+               if (size == 1 && strncmp (p, ".",1) == 0)
+               {
+                       /* printf ("ignore .\n"); */
+                       continue; /* just ignore current directory */
+               }
+               else if (size == 2 && strncmp (p, "..",2) == 0) /* give away directory */
+               {
+                       key->key[key->keySize] = 0; /* initialize first (valgrind) */
+                       while (key->keySize > rootLength && key->key[key->keySize] != PATH_SEPARATOR) key->keySize--;
+                       /* printf ("do .. (key->keySize: %d), key->key: %s, rootLength: %d, key->keySize: %d\n",
+                                       key->keySize, key->key, rootLength, key->keySize); */
+                       continue;
+               }
+               /* Add a '/' to the end of key name */
+               key->key[key->keySize]=PATH_SEPARATOR;
+               key->keySize++;
+               
+               /* carefully append basenames */
+               memcpy(key->key+key->keySize,p,size);
+               key->keySize+=size;
+       }
+
+       /* remove unescaped trailing slashes */
+       while (key->key[key->keySize-1] == PATH_SEPARATOR && key->key[key->keySize-2] != '\\') key->keySize--;
+       key->key[key->keySize]=0; /* finalize string */
+
+       key->flags |= KEY_FLAG_SYNC;
+
+       key->keySize ++; /*for \0 ending*/
+       return key->keySize;
+
+error_mem:
+       /*errno=KDB_ERR_NOMEM;*/
+       return -1;
+}
+
+
+
+
+
+/**
+ * Bytes needed to store the key name including user domain and ending NULL.
+ *
+ * @param key the key object to work with
+ * @return number of bytes needed to store key name including user domain
+ * @return 1 on empty name
+ * @return -1 on NULL pointer
+ * @see keyGetFullName(), keyGetNameSize()
+ * @ingroup keyname
+ */
+ssize_t keyGetFullNameSize(const Key *key)
+{
+       size_t returnedSize=0;
+
+       if (!key) return -1;
+
+       if (!key->key) return 1;
+
+       returnedSize=kdbiStrLen(key->key);
+
+       if (keyNameIsUser(key->key) && key->owner)
+               returnedSize+=kdbiStrLen(key->owner);
+
+       /*
+          After 2 kdbiStrLen() calls looks like we counted one more NULL.
+          But we need this byte count because a full key name has an
+          additional ':' char.
+       */
+
+       return returnedSize;
+}
+
+
+
+
+/**
+ * Get key full name, including the user domain name.
+ *
+ * @return number of bytes written
+ * @return 1 on empty name
+ * @return -1 on NULL pointers
+ * @return -1 if maxSize is 0 or larger than SSIZE_MAX
+ * @param key the key object
+ * @param returnedName pre-allocated memory to write the key name
+ * @param maxSize maximum number of bytes that will fit in returnedName, including the final NULL
+ * @ingroup keyname
+ */
+ssize_t keyGetFullName(const Key *key, char *returnedName, size_t maxSize)
+{
+       size_t userSize=sizeof("db")-1;
+       size_t ownerSize;
+       ssize_t length;
+       char *cursor;
+
+       if (!key) return -1;
+       if (!returnedName) return -1;
+       if (!maxSize) return -1;
+
+       if (maxSize > SSIZE_MAX) return -1;
+
+       length=keyGetFullNameSize(key);
+       if (length == 1) {
+               /*errno=KDB_ERR_NOKEY;*/
+               returnedName[0]=0;
+               return length;
+       }
+       else if (length < 0) return length;
+       else if (length > maxSize) {
+               /* errno=KDB_ERR_TRUNC; */
+               return -1;
+       }
+
+       cursor=returnedName;
+       if (keyIsUser(key))
+       {
+               strncpy(cursor,key->key,userSize);
+               cursor+=userSize;
+               if (key->owner) {
+                       *cursor=':'; ++cursor;
+                       ownerSize=kdbiStrLen(key->owner)-1;
+                       strcpy(cursor,key->owner);
+                       cursor+=ownerSize;
+               }
+               strcpy(cursor,key->key+userSize);
+       } else strcpy(cursor,key->key);
+
+       return length;
+}
+
+
+
+/**
+ * Returns a pointer to the real internal key name where the @p basename starts.
+ *
+ * This is a much more efficient version of keyGetBaseName() and you should
+ * use it if you are responsible enough to not mess up things. The name might
+ * change or even point to a wrong place after a keySetName(). If you need
+ * a copy of the basename consider to use keyGetBaseName().
+ *
+ * keyBaseName() returns "" when there is no keyBaseName. The reason is
+ * @code
+key=keyNew(0);
+keySetName(key,"");
+keyName(key); // you would expect "" here
+keySetName(key,"user");
+keyName(key); // you would expect "" here
+keyDel(key);
+ * @endcode
+ *
+ * @note Note that the Key structure keeps its own size field that is calculated
+ * by library internal calls, so to avoid inconsistencies, you
+ * must never use the pointer returned by keyBaseName() method to set a new
+ * value. Use keySetBaseName() instead.
+ *
+ * @param key the object to obtain the basename from
+ * @return a pointer to the basename
+ * @return "" on null pointer or when the key has no name
+ * @return 0 on NULL pointer
+ * @see keyGetBaseName(), keyGetBaseNameSize()
+ * @see keyName() to get a pointer to the name
+ * @see keyOwner() to get a pointer to the owner
+ * @ingroup keyname
+ */
+const char *keyBaseName(const Key *key)
+{
+       char *p=0;
+       char *base=0;
+       size_t size=0;
+
+       if (!key) return 0;
+
+       p = key->key;
+
+       if (!p) return "";
+
+       while (*(p=keyNameGetOneLevel(p+size,&size))) base=p;
+
+       if (base != key->key) return base;
+       else return "";
+}
+
+
+
+/**
+ * Calculates number of bytes needed to store basename of @p key.
+ *
+ * Key names that have only root names (e.g. @c "system" or @c "user"
+ * or @c "user:domain" ) does not have basenames, thus the function will
+ * return 1 bytes to store "".
+ *
+ * Basenames are denoted as:
+ * - @c system/some/thing/basename -> @c basename
+ * - @c user:domain/some/thing/base\/name > @c base\/name
+ *
+ * @param key the key object to work with
+ * @return size in bytes of @p key's basename including ending NULL
+ * @see keyBaseName(), keyGetBaseName()
+ * @see keyName(), keyGetName(), keySetName()
+ * @ingroup keyname
+ */
+ssize_t keyGetBaseNameSize(const Key *key)
+{
+       char *p=0;
+       char *base=0;
+       size_t size=0;
+       size_t baseSize=0;
+
+       if (!key) return -1;
+
+       p = key->key;;
+
+       if (!p) return 1;
+
+       while (*(p=keyNameGetOneLevel(p+size,&size))) {
+               base=p;
+               baseSize=size;
+       }
+       
+       if (base == key->key) return 1;
+       else return baseSize+1;
+}
+
+
+
+/**
+ * Calculate the basename of a key name and put it in @p returned finalizing
+ * the string with NULL.
+ *
+ * Some examples:
+ * - basename of @c system/some/keyname is @c keyname
+ * - basename of @c "user/tmp/some key" is @c "some key"
+ *
+ * @param key the key to extract basename from
+ * @param returned a pre-allocated buffer to store the basename
+ * @param maxSize size of the @p returned buffer
+ * @return number of bytes copied to @p returned
+ * @return 1 on empty name
+ * @return -1 on NULL pointers
+ * @return -1 when maxSize is 0 or larger than SSIZE_MAX
+ * @see keyBaseName(), keyGetBaseNameSize()
+ * @see keyName(), keyGetName(), keySetName()
+ * @ingroup keyname
+ */
+ssize_t keyGetBaseName(const Key *key, char *returned, size_t maxSize)
+{
+       size_t size=0;
+       char *p=0;
+       char *baseName=0;
+       size_t baseSize=0;
+
+       if (!key) return -1;
+       if (!returned) return -1;
+       if (!maxSize) return -1;
+
+       if (maxSize > SSIZE_MAX) return -1;
+
+       p = key->key;
+
+       if (!p)
+       {
+               returned[0] = 0;
+               return 1;
+       }
+
+       while (*(p=keyNameGetOneLevel(p+size,&size)))
+       {
+               baseName=p;
+               baseSize=size+1;
+       }
+
+       if (!baseName || baseName==key->key)
+       {
+               returned[0] = 0;
+               return 1;
+       }
+
+       if (maxSize < baseSize) {
+               /*strncpy(returned,baseName,maxSize);*/
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       } else {
+               strncpy(returned,baseName,baseSize);
+               return baseSize;
+       }
+}
+
+
+
+
+/**
+ * Adds @p baseName to the current key name.
+ *
+ * Assumes that @p key is a directory. @p baseName is appended to it.
+ * The function adds @c '/' if needed while concatenating.
+ *
+ * So if @p key has name @c "system/dir1/dir2" and this method is called with
+ * @p baseName @c "mykey", the resulting key will have name
+ * @c "system/dir1/dir2/mykey".
+ *
+ * When baseName is 0 or "" nothing will happen and the size of the name is returned.
+ *
+ * @warning You should not change a keys name once it belongs to a keyset.
+ * See ksSort() for more information.
+ *
+ * @param key the key object to work with
+ * @param baseName the string to append to the name
+ * @return the size in bytes of the new key name including the ending NULL
+ * @return -1 if the key had no name
+ * @return -1 on NULL pointers
+ * @see keySetBaseName()
+ * @see keySetName() to set a new name.
+ * @ingroup keyname
+ *
+ */
+ssize_t keyAddBaseName(Key *key, const char *baseName)
+{
+       size_t size=0;
+       const char *p=0;
+
+       if (!key) return -1;
+
+       if (!baseName || !baseName[0]) return key->keySize;
+
+       if (key->key) {
+               p = baseName;
+               while (*(p=keyNameGetOneLevel(p+size,&size)))
+               {
+                       key->keySize += size+1;
+                       key->key=realloc(key->key,key->keySize);
+
+                       key->key[key->keySize-size-2]=PATH_SEPARATOR;
+                       memcpy(key->key+key->keySize-size-1,p,size);
+               }
+               key->key[key->keySize-1]=0; /* finalize string */
+               return key->keySize;
+       } else return keySetName(key,baseName);
+}
+
+
+
+
+
+/**
+ * Sets @c baseName as the new basename for @c key.
+ *
+ * All text after the last @c '/' in the @p key keyname is erased and
+ * @p baseName is appended.
+ *
+ * So lets suppose @p key has name @c "system/dir1/dir2/key1". If @p baseName
+ * is @c "key2", the resulting key name will be @c "system/dir1/dir2/key2".
+ * If @p baseName is empty or NULL, the resulting key name will
+ * be @c "system/dir1/dir2".
+ *
+ * @warning You should not change a keys name once it belongs to a keyset.
+ * See ksSort() for more information.
+ *
+ * @param key the key object to work with
+ * @param baseName the string used to overwrite the basename of the key
+ * @return the size in bytes of the new key name
+ * @return -1 on NULL pointers
+ * @see keyAddBaseName()
+ * @see keySetName() to set a new name
+ * @ingroup keyname
+ */
+ssize_t keySetBaseName(Key *key, const char *baseName)
+{
+       size_t size=0;
+       const char *p=0;
+
+       if (!key) return -1;
+
+       if (key->key) {
+               /*Throw away basename of key->key*/
+               p=strrchr (key->key, '/');
+               if (p == 0) return keySetName(key, baseName);
+               key->keySize -= (key->key+key->keySize-1)-p;
+               key->key=realloc(key->key,key->keySize);
+               key->key[key->keySize-1]=0; /* finalize string */
+               /*Now add new baseName*/
+               p = baseName;
+               while (*(p=keyNameGetOneLevel(p+size,&size)))
+               {
+                       key->keySize += size+1;
+                       key->key=realloc(key->key,key->keySize);
+
+                       key->key[key->keySize-size-2]=PATH_SEPARATOR;
+                       memcpy(key->key+key->keySize-size-1,p,size);
+               }
+               key->key[key->keySize-1]=0; /* finalize string */
+               return key->keySize;
+       } else return keySetName(key,baseName);
+}
+
+
+
+/*****************************************************
+ *         General owner manipulation methods        *
+ *****************************************************/
+
+
+
+
+/**
+ * Return a pointer to the real internal @p key owner.
+ *
+ * This is a much more efficient version of keyGetOwner() and you
+ * should use it if you are responsible enough to not mess up things.
+ * You are not allowed to modify the returned string in any way.
+ * If you need a copy of the string, consider to use keyGetOwner() instead.
+ *
+ * keyOwner() returns "" when there is no keyOwner. The reason is
+ * @code
+key=keyNew(0);
+keySetOwner(key,"");
+keyOwner(key); // you would expect "" here
+keySetOwner(key,"system");
+keyOwner(key); // you would expect "" here
+ * @endcode
+ *
+ * @note Note that the Key structure keeps its own size field that is calculated
+ * by library internal calls, so to avoid inconsistencies, you
+ * must never use the pointer returned by keyOwner() method to set a new
+ * value. Use keySetOwner() instead.
+ *
+ * @param key the key object to work with
+ * @return a pointer to internal owner
+ * @return "" when there is no (a empty) owner
+ * @return 0 on NULL pointer
+ * @see keyGetOwnerSize() for the size of the string with concluding 0
+ * @see keyGetOwner(), keySetOwner()
+ * @see keyName() for name without owner
+ * @see keyGetFullName() for name with owner
+ * @ingroup keyname
+ */
+const char *keyOwner(const Key *key)
+{
+       if (!key) return 0;
+
+       if (!key->owner) {
+               /*errno=KDB_ERR_NOKEY;*/
+               return "";
+       }
+
+       return key->owner;
+}
+
+
+
+
+
+
+/**
+ * Return the size of the owner of the Key with concluding 0.
+ *
+ * The returned number can be used to allocate a string.
+ * 1 will returned on an empty owner to store the concluding 0
+ * on using keyGetOwner().
+ *
+ * @code
+char * buffer;
+buffer = malloc (keyGetOwnerSize (key));
+// use buffer and keyGetOwnerSize (key) for maxSize
+ * @endcode
+ *
+ * @note that -1 might be returned on null pointer, so when you
+ * directly allocate afterwards its best to check if you will pass
+ * a null pointer before.
+ *
+ * @param key the key object to work with
+ * @return number of bytes
+ * @return 1 if there is no owner
+ * @return -1 on NULL pointer
+ * @see keyGetOwner()
+ * @ingroup keyname
+ */
+ssize_t keyGetOwnerSize(const Key *key)
+{
+       if (!key) return -1;
+
+       if (!key->owner) {
+               /*errno=KDB_ERR_NOOWNER;*/
+               return 1;
+       }
+
+       return key->ownerSize;
+}
+
+
+
+/**
+ * Return the owner of the key.
+ * - Given @p user:someuser/..... return @p someuser
+ * - Given @p user:some.user/.... return @p some.user
+ * - Given @p user/.... return the current user
+ *
+ * Only @p user/... keys have a owner.
+ * For @p system/... keys (that doesn't have a key owner) an empty
+ * string ("") is returned.
+ *
+ * Although usually the same, the owner of a key is not related to its
+ * UID. Owner are related to WHERE the key is stored on disk, while
+ * UIDs are related to mode controls of a key.
+ *
+ * @param key the object to work with
+ * @param returned a pre-allocated space to store the owner
+ * @param maxSize maximum number of bytes that fit returned
+ * @return number of bytes written to buffer
+ * @return 1 if there is no owner
+ * @return -1 on NULL pointers
+ * @return -1 when maxSize is 0, larger than SSIZE_MAX or too small for ownername
+ * @see keySetName(), keySetOwner(), keyOwner(), keyGetFullName()
+ * @ingroup keyname
+ */
+ssize_t keyGetOwner(const Key *key, char *returned, size_t maxSize)
+{
+       if (!key) return -1;
+       if (!maxSize) return -1;
+       if (!returned) return -1;
+
+       if (maxSize > SSIZE_MAX) return -1;
+
+       if (!key->owner) {
+               /*errno=KDB_ERR_NOOWNER;*/
+               returned[0]=0;
+               return 1;
+       }
+
+       if (maxSize < key->ownerSize)
+       {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       } else strcpy(returned,key->owner);
+       return key->ownerSize;
+}
+
+
+
+/**
+ * Set the owner of a key.
+ *
+ * A owner is a name of a system user related to a UID.
+ * The owner decides on which location on the disc the key
+ * goes.
+ *
+ * A private copy is stored, so the passed parameter can be freed after
+ * the call.
+ *
+ * @param key the key object to work with
+ * @param owner the owner (or user name)
+ * @return the number of bytes actually saved including final NULL
+ * @return 1 when owner is freed (by setting 0 or "")
+ * @return -1 on null pointer or memory problems
+ * @see keySetName(), keyGetOwner(), keyGetFullName()
+ * @ingroup keyname
+ */
+ssize_t keySetOwner(Key *key, const char *owner)
+{
+       if (!key) return -1;
+
+       if (owner && (key->ownerSize=kdbiStrLen(owner)) > 1)
+       {
+               if (key->owner) {
+                       char *p=0;
+                       p=realloc(key->owner,key->ownerSize);
+                       if (NULL==p) {
+                               /*errno=KDB_ERR_NOMEM;*/
+                               return -1;
+                       }
+                       key->owner=p;
+               } else {
+                       key->owner=malloc(key->ownerSize);
+                       if (!key->owner) {
+                               /*errno=KDB_ERR_NOMEM;*/
+                               return -1;
+                       }
+               }
+
+               strcpy(key->owner,owner);
+               key->flags |= KEY_FLAG_SYNC;
+       } else if (key->owner) {
+               free(key->owner);
+               key->ownerSize=1;
+               key->owner=0;
+               key->flags |= KEY_FLAG_SYNC;
+       }
+       return key->ownerSize;
+}
+
+
diff --git a/src/libelektra/keyset.c b/src/libelektra/keyset.c
new file mode 100755 (executable)
index 0000000..086918f
--- /dev/null
@@ -0,0 +1,1752 @@
+/***************************************************************************
+                          keyset.c  -  Methods for KeySet manipulation
+                             -------------------
+    begin                : Sun Oct 02 2005
+    copyright            : (C) 2005 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/**
+ * @defgroup keyset KeySet :: Class Methods
+ * @brief Methods to manipulate KeySets.
+ *
+ * A KeySet is a unsorted set of keys.
+ *
+ * Terminate with ksNew(0) or ksNew(20, ..., KS_END)
+ * This is because there is a list of Key* required and
+ * KS_END has the length of (Key*).
+ *
+ * It can be implemented in various ways like a linked list or with a
+ * dynamically allocated array.
+ *
+ * With ksNew() you can create a new KeySet.
+ *
+ * You can add keys with ksAppendKey() in the keyset. ksGetSize() tells you
+ * the current size of the keyset.
+ *
+ * With ksRewind() and ksNext() you can navigate through the keyset. Don't
+ * expect any particular order, but it is assured that you will get every
+ * key of the set.
+ *
+ * KeySets have an @link ksCurrent() internal cursor @endlink. This is used
+ * for ksLookup() and kdbSet().
+ *
+ * KeySet has a fundamental meaning inside elektra. It makes it possible
+ * to get and store many keys at once inside the database. In addition to
+ * that the class can be used as high level datastructure in applications.
+ * With ksLookupByName() it is possible to fetch easily specific keys
+ * out of the list of keys.
+ *
+ * You can easily create and iterate keys:
+ * @code
+#include <kdb.h>
+
+// create a new keyset with 3 keys
+// with a hint that about 20 keys will be inside
+KeySet *myConfig = ksNew(20,
+       keyNew ("user/name1", 0),
+       keyNew ("user/name2", 0),
+       keyNew ("user/name3", 0),
+       KS_END);
+// append a key in the keyset
+ksAppendKey(myConfig, keyNew("user/name4", 0));
+
+Key *current;
+ksRewind(myConfig);
+while ((current=ksNext(myConfig))!=0) {
+       printf("Key name is %s.\n", keyName (current));
+}
+ksDel (myConfig); // delete keyset and all keys appended
+ * @endcode
+ *
+ * @{
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "kdbbackend.h"
+
+
+
+/**
+ * Allocate, initialize and return a new KeySet object.
+ *
+ * Objects created with ksNew() must be destroyed with ksDel().
+ *
+ * You can use a various long list of parameters to preload the keyset
+ * with a list of keys. Either your first and only parameter is 0 or
+ * your last parameter must be KEY_END.
+ *
+ * For most uses
+ * @code
+KeySet *keys = ksNew(0);
+// work with it
+ksDel (keys);
+ * @endcode
+ * goes ok, the alloc size will be 16, defined in kdbprivate.h.
+ * The alloc size will be doubled whenever size reaches alloc size,
+ * so it also performs out large keysets.
+ *
+ * But if you have any clue how large your keyset may be you should
+ * read the next statements.
+ *
+ * If you want a keyset with length 15 (because you know of your
+ * application that you normally need about 12 up to 14 keys), use:
+ * @code
+KeySet * keys = ksNew (15, KS_END);
+// work with it
+ksDel (keys);
+ * @endcode
+ *
+ * If you start having 3 keys, and your application needs approximately
+ * 200-500 keys, you can use:
+ * @code
+KeySet * config = ksNew (500,
+       keyNew ("user/sw/app/fixedConfiguration/key1", KEY_SWITCH_VALUE, "value1", 0),
+       keyNew ("user/sw/app/fixedConfiguration/key2", KEY_SWITCH_VALUE, "value2", 0),
+       keyNew ("user/sw/app/fixedConfiguration/key3", KEY_SWITCH_VALUE, "value3", 0),
+       KS_END); // don't forget the KS_END at the end!
+// work with it
+ksDel (config);
+ * @endcode
+ * Alloc size is 500, the size of the keyset will be 3 after ksNew.
+ * This means the keyset will reallocate when appending more than
+ * 497 keys.
+ *
+ * The main benefit of taking a list of variant length parameters is to be able
+ * to have one C-Statement for any possible KeySet.
+ *
+ * Due to ABI compatibility, the @p KeySet structure is only declared in kdb.h,
+ * and not defined. So you can only declare @p pointers to @p KeySets in your
+ * program.
+ * See http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html#AEN135
+ *
+ * @see ksDel() to free the keyset afterwards
+ * @see ksDup() to duplicate an existing keyset
+ * @param alloc gives a hint for the size how many Keys may be stored initially
+ * @return a ready to use KeySet object
+ * @return 0 on memory error
+ */
+KeySet *ksNew(size_t alloc, ...) {
+       KeySet *ks;
+       va_list va;
+
+       if (alloc) va_start(va, alloc);
+       ks = ksVNew (alloc, va);
+       if (alloc) va_end (va);
+
+       return ks;
+}
+
+KeySet *ksVNew (size_t alloc, va_list va)
+{
+       KeySet *keyset=0;
+       Key *key = 0;
+
+       keyset= (KeySet *)kdbiMalloc(sizeof(KeySet));
+       if (!keyset)
+       {
+               /*errno = KDB_ERR_NOMEM;*/
+               return 0;
+       }
+       ksInit(keyset);
+
+       if (alloc < KEYSET_SIZE) keyset->alloc=KEYSET_SIZE;
+       else keyset->alloc=alloc;
+
+       keyset->array = kdbiMalloc (sizeof(struct _Key *) * keyset->alloc);
+       if (!keyset->array)
+       {
+               kdbiFree(keyset);
+               /*errno = KDB_ERR_NOMEM;*/
+               return 0;
+       }
+       keyset->array[0] = 0;
+       
+
+       if (alloc) {
+               key = (struct _Key *) va_arg (va, struct _Key *);
+               while (key) {
+                       ksAppendKey(keyset, key);
+                       key = (struct _Key *) va_arg (va, struct _Key *);
+               }
+
+       }
+
+       return keyset;
+}
+
+/**
+ * Return a duplicate of a keyset.
+ *
+ * Objects created with ksDup() must be destroyed with ksDel().
+ *
+ * Memory will be allocated as needed for dynamic properties,
+ * so you need to ksDel() the returned pointer.
+ *
+ * A flat copy is made, so the keys will not be duplicated,
+ * but there reference counter is updated, so both keysets
+ * need ksDel().
+ *
+ * @param source has to be an initializised source KeySet
+ * @return a flat copy of source on success
+ * @return 0 on NULL pointer
+ * @see ksNew(), ksDel()
+ * @see keyDup() for key duplication
+ */
+KeySet *ksDup (const KeySet * source)
+{
+       KeySet *keyset=0;
+
+       if (!source) return 0;
+
+       keyset = ksNew(source->alloc,KS_END);
+       ksAppend (keyset, source);
+       return keyset;
+}
+
+
+
+/**
+ * Copy a keyset.
+ *
+ * Most often you may want a duplicate of a keyset, see
+ * ksDup() or append keys, see ksAppend().
+ * But in some situations you need to copy a
+ * keyset to a existing keyset, for that this function
+ * exists.
+ *
+ * You can also use it to clear a keyset when you pass
+ * a NULL pointer as @p source.
+ *
+ * Note that all keys in @p dest will be deleted. Afterwards
+ * the content of the source will be added to the destination
+ * and the ksCurrent() is set properly in @p dest.
+ *
+ * A flat copy is made, so the keys will not be duplicated,
+ * but there reference counter is updated, so both keysets
+ * need to be  ksDel().
+ *
+ * @code
+int f (KeySet *ks)
+{
+       KeySet *c = ksNew (20, ..., KS_END);
+       // c receives keys
+       ksCopy (ks, c); // pass the keyset to the caller
+
+       ksDel (c);
+}      // caller needs to ksDel (ks)
+ * @endcode
+ *
+ * @param source has to be an initialized source KeySet or NULL
+ * @param dest has to be an initialized KeySet where to write the keys
+ * @return 1 on success
+ * @return 0 if dest was cleared successfully (source is NULL)
+ * @return -1 on NULL pointer
+ * @see ksNew(), ksDel(), ksDup()
+ * @see keyCopy() for copying keys
+ */
+int ksCopy (KeySet *dest, const KeySet *source)
+{
+       if (!dest) return -1;
+       ksClear (dest);
+       if (!source) return 0;
+
+       ksAppend (dest, source);
+       ksSetCursor (dest, ksGetCursor (source));
+
+       return 1;
+}
+
+
+
+/**
+ * A destructor for KeySet objects.
+ *
+ * Cleans all internal dynamic attributes, decrement all reference pointers
+ * to all keys and then keyDel() all contained Keys,
+ * and free()s the release the KeySet object memory (that was previously
+ * allocated by ksNew()).
+ *
+ * @param ks the keyset object to work with
+ * @return 0 when the keyset was freed
+ * @return -1 on null pointer
+ * @see ksNew()
+ */
+int ksDel(KeySet *ks)
+{
+       int rc;
+
+       if (!ks) return -1;
+
+       rc=ksClose(ks);
+       kdbiFree(ks);
+
+       return rc;
+}
+
+/*
+ * KeySet object cleaner.
+ *
+ * Will keyDel() all contained keys, reset internal pointers and counters.
+ *
+ * After this call you can use the empty keyset again.
+ *
+ * @param ks the keyset object to work with
+ * @see ksAppendKey() for details on how keys are inserted in KeySets
+ * @return 0 on sucess
+ * @return -1 on failure
+ */
+int ksClear(KeySet *ks)
+{
+       ksClose (ks);
+
+       if (ks->array)
+       {       /* go back to standard size KEYSET_SIZE */
+               if (kdbiRealloc ((void**) &ks->array, sizeof(struct _Key *) * KEYSET_SIZE) == -1)
+               {
+                       /*errno = KDB_ERR_NOMEM;*/
+                       kdbiFree (ks->array);
+                       ks->array = 0;
+                       ks->size = 0;
+                       return -1;
+               }
+       } else {
+               if ((ks->array = kdbiMalloc (sizeof(struct _Key *) * KEYSET_SIZE)) == 0)
+               {
+                       /*errno = KDB_ERR_NOMEM;*/
+                       ks->size = 0;
+                       return -1;
+               }
+       }
+       ks->alloc = KEYSET_SIZE;
+
+
+       return 0;
+}
+
+
+
+/*
+ * Returns if keyset needs sort.
+ *
+ * It is very inefficient to resort the keyset every time when a
+ * key or a keyset is appended, so only a flag will be set to
+ * show that the keyset is not sorted any more.
+ *
+ * Before operations where the code depends on a sorted keyset,
+ * namely in kdbGet(), kdbSet() and ksLookup(), this simple code
+ * sorts when needed:
+ *
+ * @code
+if (ksNeedSort(ks)) ksSort(ks);
+ * @endcode
+ *
+ * @warning ksNeedSort() does not track every change which actually
+ * affects sorting: changing of names with keySetName() and
+ * marking keys remove with keyRemove() won't be recognized.
+ * But any ks* Method will leave the KeySet sorted or
+ * set the flag.
+ *
+ * So if your code might rename keys or flag them to remove,
+ * make sure that you use
+ *
+ * @code
+ksSort(ks);
+ * @endcode
+ *
+ * somewhere afterwards before using ksLookup(). Otherwise the
+ * renamed key won't be found.
+ *
+ * kdbSet() will have no problems with that issue, because it
+ * duplicates its keysets using ksDup().
+ *
+ * @param ks the keyset object to work with
+ * @see ksAppendKey(), ksAppend() and ksSort()
+ * @return 1 if sort is needed
+ *  0 if the keyset is sorted
+ */
+int ksNeedSort (const KeySet *ks)
+{
+       return (ks->flags & KS_FLAG_DIRTY) == KS_FLAG_DIRTY;
+}
+
+
+/* Used as a callback by the qsort() function */
+static int keyCompareWithRemove(const void *p1, const void *p2) {
+       Key *key1=*(Key **)p1;
+       Key *key2=*(Key **)p2;
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+       int ret = strcmp(name1, name2);
+
+       /* one key remove, sort in order of KEY_FLAG_REMOVE */
+       if (keyNeedRemove(key1) && !keyNeedRemove(key2)) return -1;
+       else if (!keyNeedRemove(key1) && keyNeedRemove(key2)) return 1;
+
+
+       if (keyNeedRemove(key1) && keyNeedRemove(key2))
+       {       /* both keys remove, sort reverse */
+               if (ret < 0) return 1;
+               else if (ret > 0) return -1;
+               else return 0;
+       } else { /* sort by owner */
+               if (ret == 0)
+               {
+                       const char *owner1 = keyOwner(key1);
+                       const char *owner2 = keyOwner(key2);
+                       return strcmp(owner1, owner2);
+               }
+               else return ret;
+       }
+}
+
+
+/**
+ * Sorts a KeySet alphabetically by Key name.
+ *
+ * You need ksSort() only in few cases directly.
+ *
+ * @section sortlookup Don't need to sort before using ksLookup
+ *
+ * You don't need it if you just just kdbGet() and subsequent
+ * ksLookup().
+ * @code
+KeySet *ks = ksNew(0);
+kdbGet(h, ks, k, 0);
+// ksPop(), ksAppend() and ksAppendKey() allowed here
+ksLookup(ks, s, 0); // you dont need to sort ks
+ * @endcode
+ *
+ * This is because the KeySet tracks if it needs to be sorted
+ * and ksLookup() will sort when needed.
+ *
+ * @section sortiterate Sort when iterating
+ *
+ * Before you iterate over a keyset you have to sort it, if
+ * you need it in a sorted way.
+ *
+ * To achieve that you can pass option_t::KDB_O_SORT to kdbGet()
+ * and kdbSet(). Then you will receive a already sorted keyset
+ * over which you can iterate.
+ * @code
+KeySet *ks = ksNew(0);
+kdbGet(h, ks, k, KDB_O_SORT);
+// no changes to keyset allowed
+ksRewind(ks);
+// now you can iterate over a sorted keyset
+ * @endcode
+ *
+ * Its of course also possible to use ksLookup() once, because it
+ * will sort the keyset too.
+ *
+ * @section sortkey Sort when changing key
+ *
+ * @warning You should not use keySetName() or keyRemove() when a
+ * key belongs to a keyset. When you are doing this, you always need to @p manually
+ * sort @p all keysets where the key was before using ksLookup() (otherwise ksLookup()
+ * won't find that keys), kdbGet() or kdbSet() methods.
+ *
+ * When you change a key's name or its remove status the order
+ * which was previously correctly is then wrong. The keyset
+ * does not recognize this, so the programmer has to take care
+ * that ksSort() is called before any operation which needs
+ * a sorted keyset (which are all ksLookup(), all kdbGet()
+ * and all kdbSet() functions).
+ *
+ * @note You can remember that easily that all functions which get options
+ * require one of the following:
+ * - that you did not manipulate a keys name or a remove status
+ * - that you pass KDB_O_SORT when you know that you manipulated at least one key
+ * - that you ksSort() yourself after manipulating keys
+ *
+ * @section dirty Dirty KeySet
+ *
+ * When you use ksAppend(), ksAppendKey(), ksPop() the keyset is dirty
+ * afterwards, which means that it needs to be sorted. This is done
+ * automatically using a ksLookup() method and in ksGet() or ksSet()
+ * (All methods which accept options).
+ *
+ * It won't be done if you just iterate over the keyset, so you might
+ * use a ksLookup() or ksSort() first. ksLookup() will be more efficient
+ * in that case, because it will only sort when needed. Don't pass
+ * KDB_O_NOALL (it will deactivate the sorting feature),
+ * see ksLookup() for more information.
+ *
+ * @param ks KeySet to be sorted
+ * @see kdbGet(), kdbSet(), ksLookup() for some functions which may
+ *     need sorting before they are called. (All functions which take
+ *     options as arguments)
+ * @see keySetName(), keySetBaseName(), keyAddBaseName() and keyRemove()
+ *     for all methods which change the sorting state where the keyset
+ *     can't track the change.
+ * @see ksAppend(), ksAppendKey(), ksPop() for all methods which make
+ *     a keyset dirty.
+ */
+void ksSort(KeySet *ks)
+{
+       if (!ks) return;
+
+       ks->flags = ~KS_FLAG_DIRTY & ks->flags;
+       if (! ks->size) return;
+
+       qsort(ks->array,ks->size,sizeof(Key *),keyCompareWithRemove);
+}
+
+
+
+
+/**
+ * Return the number of keys that @p ks contains.
+ *
+ * @param ks the keyset object to work with
+ * @return the number of keys that @p ks contains.
+ * @return -1 on NULL pointer
+ * @see ksNew(0), ksDel()
+ */
+ssize_t ksGetSize(const KeySet *ks)
+{
+       if (!ks) return -1;
+
+       return ks->size;
+}
+
+
+
+/******************************************* 
+ *           Filling up KeySets            *
+ *******************************************/
+
+/**
+ * Appends a Key to the end of @p ks.
+ *
+ * A pointer to the key will
+ * be stored, and not a private copy. So a future ksDel() on
+ * @p ks may keyDel() the @p toAppend object, see keyGetRef().
+ *
+ * The reference counter of the key will be incremented, and
+ * thus toAppend is not const.
+ *
+ * The KeySet internal cursor is not moved.
+ *
+ * Makes the keyset dirty, see ksSort().
+ *
+ * @return the size of the KeySet after insertion
+ * @return -1 on NULL pointers
+ * @param ks KeySet that will receive the key
+ * @param toAppend Key that will be appended to ks
+ * @see ksInsert(), ksInsertKeys(), ksAppend(), keyNew(), ksDel()
+ * @see keyIncRef()
+ *
+ */
+ssize_t ksAppendKey(KeySet *ks, Key *toAppend)
+{
+       if (!ks) return -1;
+       if (!toAppend) return -1;
+
+       ks->flags |= KS_FLAG_DIRTY;
+       ++ ks->size;
+       if (keyNeedRemove (toAppend)) ++ ks->rsize;
+       if (ks->size >= ks->alloc) ksResize (ks, ks->alloc * 2);
+       keyIncRef (toAppend);
+       ks->array[ks->size-1] = toAppend;
+       ks->array[ks->size] = 0;
+       return ks->size;
+}
+
+
+
+/**
+ * Append all @p toAppend contained keys to the end of the @p ks.
+ *
+ * @p toAppend KeySet will be left unchanged.
+ *
+ * Makes the keyset dirty, see ksSort().
+ *
+ * @return the size of the KeySet after transfer
+ * @return -1 on NULL pointers
+ * @param ks the KeySet that will receive the keys
+ * @param toAppend the KeySet that provides the keys that will be transfered
+ * @see ksAppendKey(), ksInsert(), ksInsertKeys()
+ * 
+ */
+ssize_t ksAppend(KeySet *ks, const KeySet *toAppend)
+{
+       size_t oldSize = 0;
+       int i = 0;
+       int toAlloc = 0;
+
+       if (!ks) return -1;
+       if (!toAppend) return -1;
+
+       oldSize = ks->size;
+       toAlloc = ks->alloc;
+
+       if (toAppend->size <= 0) return ks->size;
+       ks->flags |= KS_FLAG_DIRTY;
+       ks->size += toAppend->size;
+       ks->rsize += toAppend->rsize;
+       while (ks->size >= toAlloc) toAlloc *= 2;
+       ksResize (ks, toAlloc);
+       for (i=0; i<toAppend->size; i++) keyIncRef(toAppend->array[i]);
+       memcpy (ks->array + oldSize, toAppend->array, toAppend->size * sizeof (Key *));
+       ks->array[ks->size] = 0;
+       return ks->size;
+}
+
+
+
+
+/**
+ * Remove and return the last key of @p ks.
+ *
+ * The reference counter will be decremented by one.
+ *
+ * The KeySets cursor will not be effected if it did not
+ * point to the popped key.
+ *
+ * @note You need to keyDel() the key afterwards, if
+ * you don't append it to another keyset. It has the
+ * same semantics like a key allocated with keyNew()
+ * or keyDup().
+ *
+ *@code
+ks1=ksNew(0);
+ks2=ksNew(0);
+
+k1=keyNew(0); // ref counter 0
+ksAppendKey(ks1, k1); // ref counter 1
+ksAppendKey(ks2, k1); // ref counter 2
+
+k1=ksPop (ks1); // ref counter 1
+k1=ksPop (ks2); // ref counter 0, like after keyNew()
+
+ksAppendKey(ks1, k1); // ref counter 1
+
+ksDel (ks1); // key is deleted too
+ksDel (ks2);
+ *@endcode
+ *
+ * @return the last key of @p ks
+ * @return NULL if @p ks is empty or on NULL pointer
+ * @param ks KeySet to work with
+ * @see ksAppendKey(), ksAppend()
+ * @see commandList() for an example
+ *
+ */
+Key *ksPop(KeySet *ks)
+{
+       Key *ret=0;
+
+       if (!ks) return 0;
+
+       if (ks->size <= 0) return 0;
+       -- ks->size;
+       if (ks->size+1 < ks->alloc/2) ksResize (ks, ks->alloc / 2);
+       ret = ks->array[ks->size];
+       ks->array[ks->size] = 0;
+       keyDecRef(ret);
+
+       return ret;
+}
+
+
+
+/*******************************************
+ *           KeySet browsing methods       *
+ *******************************************/
+
+
+
+/**
+ * Rewinds the KeySet internal cursor.
+ *
+ * Use it to set the cursor to the beginning of the KeySet.
+ * ksCurrent() will then always return NULL afterwards. So
+ * you want to ksNext() first.
+ *
+ * @code
+ksRewind (ks);
+while ((key = keyNext (ks))!=0) {}
+ * @endcode
+ *
+ * @param ks the keyset object to work with
+ * @return 0 on success
+ * @return -1 on NULL pointer
+ * @see ksNext(), ksCurrent()
+ */
+int ksRewind(KeySet *ks)
+{
+       if (!ks) return -1;
+
+       ks->cursor=0;
+       ks->current=0;
+       return 0;
+}
+
+
+/**
+ * Returns the next Key in a KeySet.
+ *
+ * KeySets have an internal cursor that can be reset with ksRewind(). Every
+ * time ksNext() is called the cursor is incremented and the new current Key
+ * is returned.
+ *
+ * You'll get a NULL pointer if the key after the end of the KeySet was reached.
+ * It will set the cursor to the beginning of
+ * the KeySet and the next time the first key is returned.
+ *
+ * The @p ks internal cursor will be changed, so it is not const.
+ *
+ * @note You must not delete or change the key, use ksPop() if you want to delete it.
+ *
+ * @param ks the keyset object to work with
+ * @return the new current Key
+ * @return 0 when the end is reached
+ * @return 0 on NULL pointer
+ * @see ksRewind(), ksCurrent()
+ */
+Key *ksNext(KeySet *ks)
+{
+       if (!ks) return 0;
+
+       if (ks->size == 0) return 0;
+       if (ks->current >= ks->size)
+       {
+               return 0;
+       }
+
+       if (ks->cursor) ks->current++;
+       return ks->cursor = ks->array[ks->current];
+}
+
+
+
+
+/**
+ * Return the current Key.
+ *
+ * The pointer is NULL if you reached the end or after
+ * ksRewind().
+ *
+ * @note You must not delete the key or change the key,
+ *    use ksPop() if you want to delete it.
+ *
+ * @param ks the keyset object to work with
+ * @return pointer to the Key pointed by @p ks's cursor
+ * @return 0 on NULL pointer
+ * @see ksNext(), ksRewind()
+ * @see kdbMonitorKeys() for a usage example
+ */
+Key *ksCurrent(const KeySet *ks)
+{
+       if (!ks) return 0;
+
+       return ks->cursor;
+}
+
+
+
+
+/**
+ * Return the first key in the KeySet.
+ *
+ * The KeySets cursor will not be effected.
+ *
+ * If ksCurrent()==ksHead() you know you are
+ * on the first key.
+ *
+ * @param ks the keyset object to work with
+ * @return the first Key of a keyset
+ * @return 0 on NULL pointer or empty keyset
+ * @see ksTail() for the last key
+ * @see ksRewind(), ksCurrent() and ksNext() for iterating over the keyset
+ */
+Key *ksHead(const KeySet *ks)
+{
+       if (!ks) return 0;
+
+       if (ks->size > 0) return ks->array[0];
+       else return 0;
+}
+
+
+
+
+
+/**
+ * Return the last key in the KeySet.
+ *
+ * The KeySets cursor will not be effected.
+ *
+ * If ksCurrent()==ksTail() you know you
+ * are on the last key. ksNext() will return
+ * a NULL pointer afterwards.
+ *
+ * @param ks the keyset object to work with
+ * @return the last Key of a keyset
+ * @return 0 on NULL pointer or empty keyset
+ * @see ksHead() for the first key
+ * @see ksRewind(), ksCurrent() and ksNext() for iterating over the keyset
+ */
+Key *ksTail(const KeySet *ks)
+{
+       if (!ks) return 0;
+
+       if (ks->size > 0) return ks->array[ks->size-1];
+       else return 0;
+}
+
+
+
+/**
+ * Get the KeySet internal cursor.
+ *
+ * Use it to get the cursor of the actual position.
+ *
+ * @warning Cursors are getting invalid when the key
+ * was ksPop()ed or ksLookup() with KDB_O_POP was
+ * used.
+ *
+ * @section readahead Read ahead
+ *
+ * With the cursors it is possible to read ahead
+ * in a keyset:
+ *
+ * @code
+cursor_t jump;
+ksRewind (ks);
+while ((key = keyNext (ks))!=0)
+{
+       // now mark this key
+       jump = ksGetCursor(ks);
+
+       //code..
+       keyNext (ks); // now browse on
+       // use ksCurrent(ks) to check the keys
+       //code..
+
+       // jump back to the position marked before
+       ksSetCursor(ks, jump);
+}
+ * @endcode
+ *
+ * @section restore Restoring state
+ *
+ * It can also be used to restore the state of a
+ * keyset in a function
+ *
+ * @code
+int f (KeySet *ks)
+{
+       cursor_t state = ksGetCursor(ks);
+
+       // work with keyset
+
+       // now bring the keyset to the state before
+       ksSetCursor (ks, state);
+}
+ * @endcode
+ *
+ * It is of course possible to make the KeySet const
+ * and cast its const away to set the cursor.
+ * Another way to achieve
+ * the same is to ksDup() the keyset, but it is
+ * not as efficient.
+ *
+ * An invalid cursor will be returned directly after
+ * ksRewind(). When you set an invalid cursor ksCurrent()
+ * is 0 and ksNext() == ksHead().
+ *
+ * @note Only use a cursor for the same keyset which it was
+ * made for.
+ *
+ * @param ks the keyset object to work with
+ * @return a valid cursor on success
+ * @return an invalid cursor on NULL pointer or after ksRewind()
+ * @see ksNext(), ksSetCursor()
+ */
+cursor_t ksGetCursor(const KeySet *ks)
+{
+       if (!ks) return (cursor_t) -1;
+
+       if (ks->cursor == 0) return (cursor_t) -1;
+       else return (cursor_t) ks->current;
+}
+
+
+
+
+/**
+ * Set the KeySet internal cursor.
+ *
+ * Use it to set the cursor to a stored position.
+ * ksCurrent() will then be the position which you got with.
+ *
+ * @warning Cursors may get invalid when the key
+ * was ksPop()ed or ksLookup() was used together
+ * with KDB_O_POP.
+ *
+ * @code
+cursor_t cursor;
+..
+// key now in any position here
+cursor = ksGetCursor (ks);
+while ((key = keyNext (ks))!=0) {}
+ksSetCursor (ks, cursor); // reset state
+ksCurrent(ks); // in same position as before
+ * @endcode
+ *
+ * An invalid cursor will set the keyset to its beginning like
+ * ksRewind(). When you set an invalid cursor ksCurrent()
+ * is 0 and ksNext() == ksHead().
+ *
+ * @param cursor the cursor to use
+ * @param ks the keyset object to work with
+ * @return 0 when the keyset is ksRewind()ed
+ * @return 1 otherwise
+ * @return -1 on NULL pointer
+ * @see ksNext(), ksGetCursor()
+ */
+int ksSetCursor(KeySet *ks, cursor_t cursor)
+{
+       if (!ks) return -1;
+
+       if ((cursor_t) -1 == cursor)
+       {
+               ksRewind (ks);
+               return 0;
+       }
+       ks->current= (size_t)cursor;
+       ks->cursor=ks->array[ks->current];
+       return 1;
+}
+
+
+
+
+/*
+ * Returns the previous Key in a KeySet.
+ *
+ * KeySets have an internal cursor that can be reset with ksRewind(). Every
+ * time ksPrev() is called the cursor is decremented and the new current Key
+ * is returned.
+ *
+ * You'll get a NULL pointer if the key before begin of the KeySet was reached.
+ *
+ * Don't delete the key, use ksPop() if you want to delete it.
+ *
+ * @return the new current Key
+ * @see ksRewind(), ksCurrent()
+ *
+ */
+static Key *ksPrev(KeySet *ks)
+{
+       if (ks->size == 0) return 0;
+       if (ks->current <= 0)
+       {
+               ksRewind (ks);
+               return 0;
+       }
+       ks->current--;
+       return ks->cursor = ks->array[ks->current];
+}
+
+
+
+
+/******************************************* 
+ *    Looking up Keys inside KeySets       *
+ *******************************************/
+
+static int keyCompareByName(const void *p1, const void *p2) {
+       Key *key1=*(Key **)p1;
+       Key *key2=*(Key **)p2;
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+
+       return strcmp(name1, name2);
+}
+
+static int keyCompareByNameCase(const void *p1, const void *p2) {
+       Key *key1=*(Key **)p1;
+       Key *key2=*(Key **)p2;
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+
+       return kdbiStrCaseCmp(name1, name2);
+}
+
+static int keyCompareByNameOwner(const void *p1, const void *p2) {
+       Key *key1=*(Key **)p1;
+       Key *key2=*(Key **)p2;
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+       int result = strcmp(name1, name2);
+
+       if (result == 0)
+       {
+               const char *owner1 = keyOwner(key1);
+               const char *owner2 = keyOwner(key2);
+               return strcmp(owner1, owner2);
+       }
+       else return result;
+}
+
+
+static int keyCompareByNameOwnerCase(const void *p1, const void *p2) {
+       Key *key1=*(Key **)p1;
+       Key *key2=*(Key **)p2;
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+       int result = kdbiStrCaseCmp(name1, name2);
+
+       if (result == 0)
+       {
+               const char *owner1 = keyOwner(key1);
+               const char *owner2 = keyOwner(key2);
+               return kdbiStrCaseCmp(owner1, owner2);
+       }
+       else return result;
+}
+
+
+/**
+ * Look for a Key contained in @p ks that matches the name of the @p key.
+ *
+ * @section Introduction
+ *
+ * @p ksLookup() is designed to let you work with
+ * entirely pre-loaded KeySets, so instead of kdbGetKey(), key by key, the
+ * idea is to fully kdbGet() for your application root key and
+ * process it all at once with @p ksLookup().
+ *
+ * This function is very efficient by using binary search. Together with
+ * kdbGet() which can you load the whole configuration with only
+ * some communication to backends you can write very effective but short
+ * code for configuration.
+ *
+ * @section Usage
+ *
+ * If found, @p ks internal cursor will be positioned in the matched key
+ * (also accessible by ksCurrent()), and a pointer to the Key is returned.
+ * If not found, @p ks internal cursor will not move, and a NULL pointer is
+ * returned.
+ *
+ * Cascading is done if the first character is a /. This leads to ignoring
+ * the prefix like system/ and user/.
+ * @code
+        if (kdbGet(handle, "user/myapp", myConfig, 0 ) == -1)
+                ErrorHandler ("Could not get Keys");
+
+        if (kdbGet(handle, "system/myapp", myConfig, 0 ) == -1)
+                ErrorHandler ("Could not get Keys");
+
+        if ((myKey = ksLookup(myConfig, key, 0)) == NULL)
+                ErrorHandler ("Could not Lookup Key");
+ * @endcode
+ *
+ * This is the way multi user Programs should get there configuration and
+ * search after the values. It is guaranteed that more namespaces can be
+ * added easily and that all values can be set by admin and user.
+ *
+ * @subsection KDB_O_NOALL
+ *
+ * When KDB_O_NOALL is set the keyset will be only searched from ksCurrent()
+ * to ksTail(). You need to ksRewind() the keyset yourself. ksCurrent() is
+ * always set properly after searching a key, so you can go on searching
+ * another key after the found key.
+ *
+ * When KDB_O_NOALL is not set the cursor will stay untouched and all keys
+ * are considered. A much more efficient binary search will be used then.
+ *
+ * So if you change keys, e.g. rename (keySetName()) or remove (keyRemove()) them
+ * make sure to sort the keyset with ksSort(). When the keyset is dirty,
+ * see ksNeedSort() it will be sorted automatically when needed.
+ *
+ * @subsection KDB_O_POP
+ *
+ * When KDB_O_POP is set the key which was found will be ksPop()ed. ksCurrent()
+ * will not be changed, only iff ksCurrent() is the searched key, then the keyset
+ * will be ksRewind()ed.
+ *
+ * @note Note that keyRemove() keys won't be found after the first time the keyset
+ * is resorted. Lookup automatically sorts the keyset, if needed, but it
+ * can't find it out when only keys are changed, not the keyset.
+ *
+ * @note Like in ksPop() the popped key always needs to be keyDel() afterwards, even
+ * if it is appended to another keyset.
+ *
+ * @warning All cursors on the keyset will be invalid
+ * iff you use KDB_O_POP, so don't use this if you rely on a cursor, see ksGetCursor().
+ *
+ * @note Never use ksLookup() with KDB_O_POP and ksAppendKey() or ksAppend() together in a loop.
+ * Otherwise ksLookup() will need to resort the keyset every iteration and spend 99.96% of the
+ * time in ksSort() (benchmarked with above 500k iterations).
+ *
+ * You can solve this problem by using KDB_O_NOALL, risking you have to iterate n^2 instead of n.
+ *
+ * The more elegant way is to separate the keyset you use for ksLookup() and ksAppendKey():
+ * @code
+int f(KeySet *iterator, KeySet *lookup)
+{
+       KeySet *append = ksNew (ksGetSize(lookup), KS_END);
+       Key *key;
+       Key *current;
+
+       ksRewind(iterator);
+       while (current=ksNext(iterator))
+       {
+               key = ksLookup (lookup, current, KDB_O_POP);
+               // do something...
+               ksAppendKey(append, key); // now append it to append, not lookup!
+               keyDel (key); // make sure to ALWAYS delete poped keys.
+       }
+       ksAppend(lookup, append);
+       // now lookup needs to be sorted only once, append never
+       ksDel (append);
+}
+ * @endcode
+ *
+ * @param ks where to look for
+ * @param key the key object you are looking for
+ * @param options some @p KDB_O_* option bits:
+ *     - @p KDB_O_NOCASE @n
+ *             Lookup ignoring case.
+ *     - @p KDB_O_WITHOWNER @n
+ *             Also consider correct owner.
+ *     - @p KDB_O_NOALL @n
+ *             Only search from ksCurrent() to end of keyset, see above text.
+ *     - @p KDB_O_POP @n
+ *             Pop the key which was found.
+ *     - @p KDB_O_SORT @n
+ *             Force sorting before searching, see ksSort().
+ *             Together with KDB_O_NOALL the search will start from beginning.
+ * @return pointer to the Key found, 0 otherwise
+ * @return 0 on NULL pointers
+ * @see ksLookupByName() to search by a name given by a string
+ * @see ksCurrent(), ksRewind(), ksNext() for iterating over a keyset
+ * @see ksSort() to understand how keyset sort themself
+ */
+Key *ksLookup(KeySet *ks, Key * key, option_t options)
+{
+       Key *current;
+       Key ** found;
+       cursor_t cursor = 0;
+       size_t jump = 0;
+
+       if (!ks) return 0;
+
+       jump = ks->rsize;
+       cursor = ksGetCursor (ks);
+
+       if (options & KDB_O_SORT)
+       {
+               ksSort (ks);
+               ksRewind (ks);
+       }
+       else if (!(options & KDB_O_NOALL) && ks->flags & KS_FLAG_DIRTY) ksSort(ks);
+
+       if (!key) return 0;
+
+       if (options & KDB_O_NOALL)
+       {
+               while ((current=ksNext(ks)) != 0) {
+                       if ((options & KDB_O_WITHOWNER) && (options & KDB_O_NOCASE))
+                       {
+                               if (!keyCompareByNameOwnerCase(&key, &current)) break;
+                       }
+                       else if (options & KDB_O_WITHOWNER)
+                       {
+                               if (!keyCompareByNameOwner(&key, &current)) break;
+                       }
+                       else if (options & KDB_O_NOCASE)
+                       {
+                               if (!keyCompareByNameCase(&key, &current)) break;
+                       }
+                       else if (!keyCompareByName(&key, &current)) break;
+               }
+               if (options & KDB_O_DEL) keyDel (key);
+               if (current == 0) ksSetCursor (ks, cursor);
+               return current;
+       } else {
+               if ((options & KDB_O_WITHOWNER) && (options & KDB_O_NOCASE))
+                       found = (Key **) bsearch (&key, ks->array+jump, ks->size-jump,
+                               sizeof (Key *), keyCompareByNameOwnerCase);
+               else if (options & KDB_O_WITHOWNER)
+                       found = (Key **) bsearch (&key, ks->array+jump, ks->size-jump,
+                               sizeof (Key *), keyCompareByNameOwner);
+               else if (options & KDB_O_NOCASE)
+                       found = (Key **) bsearch (&key, ks->array+jump, ks->size-jump,
+                               sizeof (Key *), keyCompareByNameCase);
+               else
+                       found = (Key **) bsearch (&key, ks->array+jump, ks->size-jump,
+                               sizeof (Key *), keyCompareByName);
+               if (options & KDB_O_DEL) keyDel (key);
+               if (found)
+               {
+                       if (options & KDB_O_POP)
+                       {
+                               Key * k = *found;
+                               /* Move the array over the place where key was found */
+                               memmove (found, found+1, ks->size*sizeof(Key *)-(found-ks->array)-sizeof(Key *));
+                               *(ks->array+ks->size-1) = k;
+                               if (found < ks->array+ks->current)
+                               {
+                                       ksPrev(ks);
+                               }
+                               else if (found == ks->array+ks->current)
+                               {
+                                       ksRewind(ks);
+                               }
+                               return ksPop(ks);
+                       } else {
+                               cursor = found-ks->array;
+                               ksSetCursor(ks, cursor);
+                               return (*found);
+                       }
+               } else {
+                       /*Reset Cursor to old position*/
+                       ksSetCursor(ks, cursor);
+                       return 0;
+               }
+       }
+}
+
+/**
+ * Look for a Key contained in @p ks that matches @p name.
+ *
+ * @p ksLookupByName() is designed to let you work with
+ * entirely pre-loaded KeySets, so instead of kdbGetKey(), key by key, the
+ * idea is to fully kdbGetByName() for your application root key and
+ * process it all at once with @p ksLookupByName().
+ *
+ * This function is very efficient by using binary search. Together with
+ * kdbGetByName() which can you load the whole configuration with only
+ * some communication to backends you can write very effective but short
+ * code for configuration.
+ *
+ * If found, @p ks internal cursor will be positioned in the matched key
+ * (also accessible by ksCurrent()), and a pointer to the Key is returned.
+ * If not found, @p ks internal cursor will not move, and a NULL pointer is
+ * returned.
+ *
+ * @section cascading Cascading
+ *
+ * Cascading is done if the first character is a /. This leads to ignoring
+ * the prefix like system/ and user/.
+ * @code
+        if (kdbGetByName(handle, "/sw/myapp/current", myConfig, 0 ) == -1)
+                ErrorHandler ("Could not get Keys");
+
+        if ((myKey = ksLookupByName (myConfig, "/myapp/current/key", 0)) == NULL)
+                ErrorHandler ("Could not Lookup Key");
+ * @endcode
+ * 
+ * This is the way multi user Programs should get there configuration and
+ * search after the values. It is guaranteed that more namespaces can be
+ * added easily and that all values can be set by admin and user.
+ *
+ * @section fullsearch Full Search
+ *
+ * When KDB_O_NOALL is set the keyset will be only searched from ksCurrent()
+ * to ksTail(). You need to ksRewind() the keyset yourself. ksCurrent() is
+ * always set properly after searching a key, so you can go on searching
+ * another key after the found key.
+ *
+ * When KDB_O_NOALL is not set the cursor will stay untouched and all keys
+ * are considered. A much more efficient binary search will be used then.
+ * 
+ * @param ks where to look for
+ * @param name key name you are looking for
+ * @param options some @p KDB_O_* option bits:
+ *     - @p KDB_O_NOCASE @n
+ *             Lookup ignoring case.
+ *     - @p KDB_O_WITHOWNER @n
+ *             Also consider correct owner.
+ *     - @p KDB_O_NOALL @n
+ *             Only search from ksCurrent() to end of keyset, see above text.
+ *     - @p KDB_O_POP @n
+ *             Pop the key which was found.
+ *     - @p KDB_O_SORT @n
+ *             Force sorting before searching, see ksSort().
+ *             Together with KDB_O_NOALL the search will start from beginning.
+ *
+ *     Currently no options supported.
+ * @return pointer to the Key found, 0 otherwise
+ * @return 0 on NULL pointers
+ * @see keyCompare() for very powerfull Key lookups in KeySets
+ * @see ksCurrent(), ksRewind(), ksNext()
+ */
+Key *ksLookupByName(KeySet *ks, const char *name, option_t options)
+{
+       Key * key=0;
+       Key * found=0;
+       char * newname=0;
+
+       if (!ks) return 0;
+       if (!name) return 0;
+
+       if (! ks->size) return 0;
+
+       if (name[0] == '/')
+       {
+               /* Will be freed by second key */
+               newname = kdbiMalloc (strlen (name) + sizeof ("system") + 1);
+               if (!newname)
+               {
+                       /*errno = KDB_ERR_NOMEM;*/
+                       return 0;
+               }
+               strncpy (newname+4, "db",2);
+               strcpy  (newname+6, name);
+               key = keyNew (newname+4, KEY_END);
+               found = ksLookup(ks, key, options);
+               keyDel (key);
+
+               if (!found)
+               {
+                       strncpy (newname+2, "file",4);
+                       key = keyNew (newname+2, KEY_END);
+                       found = ksLookup(ks, key, options);
+                       keyDel (key);
+               }
+
+               if (!found)
+               {
+                       strncpy (newname, "memory",6);
+                       key = keyNew (newname, KEY_END);
+                       found = ksLookup(ks, key, options);
+                       keyDel (key);
+               }
+
+               kdbiFree (newname);
+               return found;
+       } else {
+               key = keyNew (name, KEY_END);
+               if (!key) return 0;
+               found = ksLookup(ks, key, options);
+               keyDel (key);
+               return found;
+       }
+}
+
+
+/*
+ * Lookup for a Key contained in @p ks KeySet that matches @p value,
+ * starting from ks' ksNext() position.
+ *
+ * If found, @p ks internal cursor will be positioned in the matched key
+ * (also accessible by ksCurrent()), and a pointer to the Key is returned.
+ * If not found, @p ks internal cursor won't move, and a NULL pointer is
+ * returned.
+ *
+ * This method skips binary keys.
+ *
+ * @par Example:
+ * @code
+ksRewind(ks);
+while (key=ksLookupByString(ks,"my value",0)) {
+       // show all keys which value="my value"
+       keyToStream(key,stdout,0);
+}
+ * @endcode
+ *
+ * @param ks where to look for
+ * @param value the value which owner key you want to find
+ * @param options some @p KDB_O_* option bits. Currently supported:
+ *     - @p KDB_O_NOALL @n
+ *             Only search from ksCurrent() to end of keyset, see ksLookup().
+ *     - @p KDB_O_SORT @n
+ *             Force sorting before searching, see ksSort().
+ *             Together with KDB_O_NOALL the search will start from beginning.
+ *     - @p KDB_O_NOCASE @n
+ *       Lookup ignoring case.
+ * @return the Key found, 0 otherwise
+ * @see ksLookupByBinary()
+ * @see keyCompare() for very powerfull Key lookups in KeySets
+ * @see ksCurrent(), ksRewind(), ksNext()
+ */
+Key *ksLookupByString(KeySet *ks, const char *value, option_t options)
+{
+       cursor_t init=0;
+       Key *current=0;
+
+       if (!ks) return 0;
+
+       if (options & KDB_O_SORT)
+       {
+               ksSort (ks);
+               ksRewind (ks);
+       }
+       else if (!(options & KDB_O_NOALL) && ks->flags & KS_FLAG_DIRTY) ksSort(ks);
+
+       if (!(options & KDB_O_NOALL))
+       {
+               ksRewind (ks);
+               init=ksGetCursor(ks);
+       }
+
+       if (!value) return 0;
+
+       while ((current=ksNext(ks)) != 0)
+       {
+               if (!keyIsString(current)) continue;
+
+               /*fprintf (stderr, "Compare %s with %s\n", keyValue(current), value);*/
+
+               if ((options & KDB_O_NOCASE) && 
+                       !kdbiStrCaseCmp(keyValue(current),value)) break;
+               else if (!strcmp(keyValue(current),value)) break;
+       }
+
+       /* reached end of KeySet */
+       if (!(options & KDB_O_NOALL))
+       {
+               ksSetCursor (ks, init);
+       }
+
+       return current;
+}
+
+
+
+/*
+ * Lookup for a Key contained in @p ks KeySet that matches the binary @p value,
+ * starting from ks' ksNext() position.
+ *
+ * If found, @p ks internal cursor will be positioned in the matched key.
+ * That means it is also accessible by ksCurrent(). A pointer to the Key
+ * is returned. If not found, @p ks internal cursor won't move, and a 
+ * NULL pointer is returned.
+ *
+ * This method skips string keys.
+ *
+ * @param ks where to look for
+ * @param value the value which owner key you want to find
+ * @param size the size of @p value
+ * @param options some @p KDB_O_* option bits:
+ *     - @p KDB_O_NOALL @n
+ *             Only search from ksCurrent() to end of keyset, see above text.
+ *     - @p KDB_O_SORT @n
+ *             Force sorting before searching, see ksSort().
+ *             Together with KDB_O_NOALL the search will start from beginning.
+ * @return the Key found, NULL otherwise
+ * @return 0 on NULL pointer
+ * @see ksLookupByString()
+ * @see keyCompare() for very powerfull Key lookups in KeySets
+ * @see ksCurrent(), ksRewind(), ksNext()
+ */
+Key *ksLookupByBinary(KeySet *ks, const void *value, size_t size,
+               option_t options)
+{
+       cursor_t init=0;
+       Key *current=0;
+
+       if (!ks) return 0;
+
+       if (options & KDB_O_SORT)
+       {
+               ksSort (ks);
+               ksRewind (ks);
+       }
+       else if (!(options & KDB_O_NOALL) && ks->flags & KS_FLAG_DIRTY) ksSort(ks);
+
+       if (!(options & KDB_O_NOALL))
+       {
+               ksRewind (ks);
+               init=ksGetCursor(ks);
+       }
+
+       while ((current=ksNext(ks)))
+       {
+               if (!keyIsBinary(current)) continue;
+
+               if (size != current->dataSize) continue;
+
+               if (!value)
+               {
+                       if (!current->data) break;
+                       else continue;
+               }
+
+               if (current->data && 
+                       !memcmp(current->data,value,size)) break;
+       }
+
+       /* reached end of KeySet */
+       if (!(options & KDB_O_NOALL))
+       {
+               ksSetCursor (ks, init);
+       }
+
+       return 0;
+}
+
+
+
+/******************************************* 
+ *    Other operations                     *
+ *******************************************/
+
+
+
+
+/*
+ * Calculates the common parent to all keys in @p ks.
+ *
+ * This is a c-helper function, you need not implement it in bindings.
+ *
+ * Given the @p ks KeySet, calculates the parent name for all the keys.
+ * So if @p ks contains this keys:
+ *
+ * @code
+ *   system/sw/xorg/Monitors/Monitor1/vrefresh
+ *   system/sw/xorg/Monitors/Monitor1/hrefresh
+ *   system/sw/xorg/Devices/Device1/driver
+ *   system/sw/xorg/Devices/Device1/mode
+ * @endcode
+ *
+ * The common parent is @p system/sw/xorg .
+ *
+ * On the other hand, if we have this KeySet:
+ *
+ * @code
+ *   system/some/thing
+ *   system/other/thing
+ *   user/unique/thing
+ * @endcode
+ *
+ * No common parent is possible, so @p returnedCommonParent will contain nothing.
+ *
+ * This method will work correctly only on @link ksSort() sorted KeySets @endlink.
+ *
+ * @param working the Keyset to work with
+ * @param returnedCommonParent a pre-allocated buffer that will receive the
+ *        common parent, if found
+ * @param maxSize size of the pre-allocated @p returnedCommonParent buffer
+ * @return size in bytes of the parent name, or 0 if there is no common parent,
+ *         or -1 to indicate an error, then @p errno must be checked.
+ */
+ssize_t ksGetCommonParentName(const KeySet *working,char *returnedCommonParent, size_t maxSize) {
+       size_t parentSize=0;
+       Key *current=0;
+       cursor_t init;
+       KeySet *ks;
+
+       init = ksGetCursor (working);
+       ks = (KeySet *) working;
+
+       if (ks->size < 1) return 0;
+
+       ksRewind(ks);
+       current = ksNext(ks);
+       if (keyGetNameSize(current) > maxSize) {
+               /*errno=KDB_ERR_TRUNC;*/
+               returnedCommonParent[0]=0;
+               return -1;
+       }
+
+       strcpy(returnedCommonParent,keyName(current));
+       parentSize=kdbiStrLen(returnedCommonParent);
+
+       while (*returnedCommonParent) {
+               ksRewind(ks);
+               while ((current = ksNext(ks)) != 0) {
+                       /* Test if a key doesn't match */
+                       if (memcmp(returnedCommonParent,keyName(current),parentSize-1)) break;
+               }
+               if (current) {
+                       /* some key failed to be a child */
+                       /* parent will be the parent of current parent... */
+                       char *delim=0;
+
+                       if ((delim=strrchr(returnedCommonParent,PATH_SEPARATOR))) {
+                               *delim=0;
+                               parentSize=kdbiStrLen(returnedCommonParent);
+                       } else {
+                               *returnedCommonParent=0;
+                               parentSize=0;
+                               break; /* Better don't make comparision with parentSize-1 now */
+                       }
+               } else {
+                       /* All keys matched (current==0) */
+                       /* We have our common parent to return in commonParent */
+                       ksSetCursor (ks, init );
+                       return parentSize;
+               }
+       }
+       ksSetCursor (ks, init );
+       return parentSize; /* if reached, will be zero */
+}
+
+
+
+/*********************************************************************
+ *                Data constructors (protected)                      *
+ *********************************************************************/
+
+
+/*
+ * Resize keyset.
+ *
+ * For internal useage only.
+ *
+ * Don't use that function to be portable. You can give an hint
+ * how large the keyset should be in ksNew().
+ *
+ * Subsequent is the description of the implementation with array.
+ * ksResize() enlarge or shrink the internal array to wished
+ * size alloc.
+ *
+ * If you resize it to n, you can be sure to fill in n-1 elements,
+ * the n-th element will do an automatic resize to n*2. So give
+ * some spare to avoid wasteful duplication.
+ *
+ * @param ks the keyset which should be resized
+ * @param alloc the size to which the array will be resized
+ * @return 1 on success
+ * @return 0 on nothing done because keyset would be too small.
+ * @return -1 if alloc is smaller then current size of keyset.
+ * @return -1 on memory error
+ */
+int ksResize (KeySet *ks, size_t alloc)
+{
+       if (alloc == ks->alloc) return 1;
+       if (alloc < ks->size) return 0;
+       if (alloc < KEYSET_SIZE)
+       {
+               if (ks->alloc != KEYSET_SIZE) alloc = KEYSET_SIZE;
+               else return 0;
+       }
+
+       if (ks->array == NULL)
+       {       /* Not allocated up to now */
+               ks->alloc = alloc;
+               ks->size = 0;
+               ks->array = kdbiMalloc (sizeof(struct _Key *) * ks->alloc);
+               if (!ks->array)
+               {
+                       /*errno = KDB_ERR_NOMEM;*/
+                       return -1;
+               }
+       }
+
+#if DEBUG && VERBOSE
+       printf ("Resize from %d to %d\n",(int) ks->alloc,(int) alloc);
+#endif
+       ks->alloc=alloc;
+
+
+       if (kdbiRealloc((void**) &ks->array, sizeof(struct _Key *) * ks->alloc) == -1)
+       {
+#if DEBUG
+               fprintf (stderr, "Reallocation error\n");
+#endif
+               kdbiFree (ks->array);
+               ks->array = 0;
+               /*errno = KDB_ERR_NOMEM;*/
+               return -1;
+       }
+
+       return 1;
+}
+
+/*
+ * Returns current allocation size.
+ *
+ * @param ks the keyset object to work with
+ * @return allocated size*/
+size_t ksGetAlloc (const KeySet *ks)
+{
+       return ks->alloc;
+}
+
+
+
+/*
+ * KeySet object initializer.
+ *
+ * You should always use ksNew() instead of ksInit().
+ *
+ * Every KeySet object that will be used must be initialized first, to setup
+ * pointers, counters, etc. After use, all ksInit()ialized KeySets must be
+ * cleaned with ksClear().
+ * 
+ * @deprecated Thus you can't get a Keyset without ksNew, this function is useless.
+ * @see ksNew(), ksClose(), keyInit()
+ * @return 1 on success
+ */
+int ksInit(KeySet *ks) {
+       ks->array = 0;
+       ks->flags = KS_FLAG_DIRTY;
+
+       ks->size=0;
+       ks->rsize=0;
+       ks->alloc=0;
+
+       ksRewind(ks);
+
+       return 1;
+}
+
+
+/*
+ * KeySet object initializer.
+ *
+ * @deprecated Thus you can't get a Keyset without ksNew, this function is useless.
+ * @see ksDel(), ksNew(), keyInit()
+ * @return 1 on success
+ */
+int ksClose(KeySet *ks)
+{
+       Key * k;
+
+       ksRewind(ks);
+       while ((k = ksNext(ks)) != 0)
+       {
+               keyDecRef (k);
+               keyDel (k);
+       }
+
+       if (ks->array) kdbiFree (ks->array);
+       ks->array = 0;
+       ks->alloc = 0;
+
+       ks->size = 0;
+       ks->rsize= 0;
+
+       return 0;
+}
+
+
+/**
+ * @}
+ */
+
diff --git a/src/libelektra/keytest.c b/src/libelektra/keytest.c
new file mode 100644 (file)
index 0000000..7caad19
--- /dev/null
@@ -0,0 +1,524 @@
+ /***************************************************************************
+                      keytest.c  -  Methods for Key manipulation
+                             -------------------
+    begin                : Fri Sep 26 2008
+    copyright            : (C) 2008 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/**
+ * @defgroup keytest Key :: Methods for Making Tests
+ * @brief Methods to do various tests on Keys
+ *
+ * To use them:
+ * @code
+#include <kdb.h>
+ * @endcode
+ *
+ *
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+
+/**
+ * Ask if key is marked for stat only.
+ *
+ * Ask if the key will be stat instead of get it from the key database
+ * completely doing kdbGetKey() or kdbGet(). This is useful
+ * if you are not interested in the value, comment or key type.
+ *
+ * @see keyStat(), kdbGet()
+ * @param key the key object to work with
+ * @return 1 if it is marked, 0 otherwise
+ * @return -1 on NULL pointer
+ * @ingroup keytest
+ **/
+int keyNeedStat(const Key *key)
+{
+       if (!key) return -1;
+
+       return (key->flags & KEY_FLAG_STAT) == KEY_FLAG_STAT;
+}
+
+
+
+/**
+ * Test if a key needs to be synced to backend storage.
+ *
+ * If any key modification took place the key will be flagged with
+ * KEY_FLAG_SYNC so that kdbSet() knows which keys were modified
+ * and which not.
+ *
+ * After keyNew() the flag will normally be set, but after kdbGet()
+ * and kdbSet() the flag will be removed. When you modify the key
+ * the flag will be set again.
+ *
+ * In your application you can make use of that flag to know
+ * if you changed something in a key after a kdbGet() or kdbSet().
+ *
+ * @note Note that also changes in the meta data will set that flag.
+ *
+ * @see keyNew()
+ * @param key the key object to work with
+ * @return 1 if @p key was changed in memory, 0 otherwise
+ * @return -1 on NULL pointer
+ * @ingroup keytest
+ */
+int keyNeedSync(const Key *key)
+{
+       if (!key) return -1;
+
+       return (key->flags & KEY_FLAG_SYNC) == KEY_FLAG_SYNC;
+}
+
+
+/**
+ * Ask if key is marked for permanent remove.
+ *
+ * Ask if the key will be removed instead of writing in the key database
+ * when doing kdbSetKey() or kdbSet().
+ *
+ * @see keyRemove()
+ * @see kdbSet(), kdbSetKey(), kdbRemove()
+ * @param key the key object to work with
+ * @return 1 if it is marked, 0 otherwise
+ * @return -1 on NULL pointer
+ * @ingroup keytest*/
+int keyNeedRemove(const Key *key)
+{
+       if (!key) return -1;
+
+       return (key->flags & KEY_FLAG_REMOVE) == KEY_FLAG_REMOVE;
+}
+
+
+
+/**
+ * Check whether a key is under the @p system namespace or not
+ *
+ * @param key the key object to work with
+ * @return 1 if key name begins with @p system, 0 otherwise
+ * @return -1 on NULL pointer
+ * @see keyIsUser(), keySetName(), keyName()
+ * @ingroup keytest
+ *
+ */
+int keyIsSystem(const Key *key)
+{
+       if (!key) return -1;
+
+       if (key->key) return keyNameIsSystem(key->key);
+       else return 0;
+}
+
+
+
+/**
+ * Check whether a key is under the @p memory namespace or not
+ *
+ * @param key the key object to work with
+ * @return 1 if key name begins with @p system, 0 otherwise
+ * @return -1 on NULL pointer
+ * @see keyIsUser(), keySetName(), keyName()
+ * @ingroup keytest
+ *
+ */
+int keyIsMemory(const Key *key)
+{
+       if (!key) return -1;
+
+       if (key->key) return keyNameIsMemory(key->key);
+   else return 0;
+}
+
+
+
+/**
+ * Check whether a key is under the @p user namespace or not.
+ *
+ * @param key the key object to work with
+ * @return 1 if key name begins with @p user, 0 otherwise
+ * @return -1 on NULL pointer
+ * @see keyIsSystem(), keySetName(), keyName()
+ * @ingroup keytest
+ *
+ */
+int keyIsUser(const Key *key)
+{
+       if (!key) return -1;
+
+       if (key->key) return keyNameIsUser(key->key);
+       else return 0;
+}
+
+
+
+/**
+ * Check if the key check is below the key key or not.
+ *
+ * @code
+Example:
+key user/sw/app
+check user/sw/app/key
+
+returns true because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+returns also true because check is indirect below key
+ * @endcode
+ *
+ * @param key the key object to work with
+ * @param check the key to find the relative position of
+ * @return 1 if check is below key
+ * @return 0 if it is not below or if it is the same key
+ * @see keySetName(), keyGetName(), keyIsDirectBelow()
+ * @ingroup keytest
+ *
+ */
+int keyIsBelow(const Key *key, const Key *check)
+{
+       const char * keyname = 0;
+       const char * checkname = 0;
+       ssize_t keysize = 0;
+       ssize_t checksize = 0;
+
+       if (!key || !check) return -1;
+
+       keyname = keyName(key);
+       checkname = keyName(check);
+       keysize = keyGetNameSize(key);
+       checksize = keyGetNameSize(check);
+
+       if (keysize > checksize + 1) return 0;
+       if (strncmp (keyname, checkname, keysize - 1)) return 0;
+       if (checkname[keysize - 1] != '/') return 0;
+       return 1;
+}
+
+/**
+ * Check if the key check is direct below the key key or not.
+ *
+ * @code
+Example:
+key user/sw/app
+check user/sw/app/key
+
+returns true because check is below key
+
+Example:
+key user/sw/app
+check user/sw/app/folder/key
+
+does not return true, because there is only a indirect relation
+ * @endcode
+ *
+ * @param key the key object to work with
+ * @param check the key to find the relative position of
+ * @return 1 if check is below key
+ * @return 0 if it is not below or if it is the same key
+ * @return -1 on null pointer
+ * @see keyIsBelow(), keySetName(), keyGetName()
+ * @ingroup keytest
+ *
+ */
+int keyIsDirectBelow(const Key *key, const Key *check)
+{
+       const char * checkname = 0;
+       ssize_t keysize = 0;
+
+       if (!key || !check) return -1;
+
+       checkname = keyName(check);
+       keysize = keyGetNameSize(key);
+
+       if (!keyIsBelow(key, check)) return 0;
+       if (strchr(checkname + keysize, '/')) return 0;
+       return 1;
+}
+
+
+/**
+ * Check whether a key is inactive or not.
+ *
+ * In elektra terminology any key is inactive if the
+ * it's basename starts with '.'. Inactive keys
+ * must not have any meaning to applications, they
+ * are reserved for users and administrators.
+ *
+ * To remove a whole hierarchy in elektra, don't
+ * forget to pass option_t::KDB_O_INACTIVE to
+ * kdbGet() to receive the inactive keys in order
+ * to remove them.
+ *
+ * Otherwise you should not fetch these keys.
+ *
+ * @param key the key object to work with
+ * @return 1 if the key is inactive, 0 otherwise
+ * @return -1 on NULL pointer or when key has no name
+ * @ingroup keytest
+ *
+ */
+int keyIsInactive (const Key *key)
+{
+       char *name = 0;
+
+       if (!key) return -1;
+
+       name = strrchr (keyName(key), '/');
+
+       if (!name) return -1;
+
+       /* the slash can't be a trailing slash
+       but there might be no name at all! */
+       if (name[1] == '.') return 1;
+       else return 0;
+}
+
+
+
+
+/**
+ * Check if a key is directory key.
+ *
+ * Folder keys may also have value and comment.
+ * They are discern by having a executable bit
+ * set.
+ *
+ * If any executable bit is set it will be recognized
+ * as a directory.
+ *
+ * @note keyIsDir may return true even though you
+ *   can't access the directory.
+ *
+ * To know if you can access the directory, you
+ * need to check, if your
+ * - user ID is equal the key's
+ *   user ID and the mode & 100 is true
+ * - group ID is equal the key's
+ *   group ID and the mode & 010 is true
+ * - mode & 001 is true
+ *
+ * Accessing does not mean that you can get any value or
+ * comments below, see @ref mode for more information.
+ *
+ * @param key the key object to work with
+ * @return 1 if key is a directory, 0 otherwise
+ * @return -1 on NULL pointer
+ * @see keySetDir(), keySetMode()
+ * @ingroup keytest
+ */
+int keyIsDir(const Key *key)
+{
+       if (!key) return -1;
+
+       return ((key->mode & KEY_DEF_DIR) != 0);
+}
+
+
+/**
+ * Check if a key is binary type.
+ *
+ * The function checks if the keytype is in the range between KEY_TYPE_BINARY and
+ * less than excluding KEY_TYPE_STRING. Then it will be interpreted as binary.
+ *
+ * Make sure to use this function and don't test the binary type another way to
+ * ensure compatibility and to write less error prone programs.
+ *
+ * @return 1 if it is binary
+ * @return 0 if it is not
+ * @return -1 on NULL pointer
+ * @see keySetType() for more information on types
+ * @see keyGetBinary(), keySetBinary()
+ * @param key the key to check
+ * @ingroup keytest
+ */
+int keyIsBinary(const Key *key)
+{
+       if (!key) return -1;
+
+       return (KEY_TYPE_BINARY <= key->type && key->type < KEY_TYPE_STRING);
+}
+
+
+/**
+ * Check if a key is string type.
+ *
+ * The function checks if the keytype is larger or equal KEY_TYPE_STRING.
+ * Then it will be considered as string type.
+ *
+ * Make sure to use this function and don't test the string type another way to
+ * ensure compatibility and to write less error prone programs.
+ *
+ * @return 1 if it is string
+ * @return 0 if it is not
+ * @return -1 on NULL pointer
+ * @see keySetType for more information on types
+ * @see keyGetString(), keySetString()
+ * @param key the key to check
+ * @ingroup keytest
+ */
+int keyIsString(const Key *key)
+{
+       if (!key) return -1;
+
+       return (key->type >= KEY_TYPE_STRING);
+}
+
+
+
+
+
+/*
+ * Compare 2 keys.
+ *
+ * The returned flags bit array has 1s (differ) or 0s (equal) for each key
+ * meta info compared, that can be logically ORed using @c #keyswitch_t flags.
+ * The flags you can use are @link keyswitch_t::KEY_TYPE KEY_TYPE
+ * @endlink, @link keyswitch_t::KEY_NAME KEY_NAME @endlink,
+ * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink,
+ * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink,
+ * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink,
+ * @link keyswitch_t::KEY_UID KEY_UID @endlink,
+ * @link keyswitch_t::KEY_GID KEY_GID @endlink,
+ * @link keyswitch_t::KEY_MODE KEY_MODE @endlink and
+ *
+ * @par A very simple example would be
+ * @code
+Key *key1, *key;
+uint32_t changes;
+
+// omited key1 and key2 initialization and manipulation
+
+changes=keyCompare(key1,key2);
+
+if (changes == 0) printf("key1 and key2 are identicall\n");
+
+if (changes & KEY_VALUE)
+       printf("key1 and key2 have different values\n");
+if (changes & KEY_UID)
+       printf("key1 and key2 have different UID\n");
+ *
+ * @endcode
+ *
+ * 
+ * @par Example of very powerfull specific Key lookup in a KeySet:
+ * @code
+KDB *handle = kdbOpen();
+KeySet *ks=ksNew(0);
+Key *base = keyNew ("user/sw/MyApp/something", KEY_END);
+Key *current;
+uint32_t match;
+uint32_t interests;
+
+
+kdbGetByName(handle, ks, "user/sw/MyApp", 0);
+
+// we are interested only in key type and access permissions
+interests=(KEY_TYPE | KEY_MODE);
+
+ksRewind(ks);   // put cursor in the begining
+while ((curren=ksNext(ks))) {
+       match=keyCompare(current,base);
+       
+       if ((~match & interests) == interests)
+               printf("Key %s has same type and permissions of base key",keyName(current));
+
+       // continue walking in the KeySet....
+}
+
+// now we want same name and/or value
+interests=(KEY_NAME | KEY_VALUE);
+
+// we don't really need ksRewind(), since previous loop achieved end of KeySet
+ksRewind(ks);
+while ((current=ksNext(ks))) {
+       match=keyCompare(current,base);
+
+       if ((~match & interests) == interests) {
+               printf("Key %s has same name, value, and sync status
+                       of base key",keyName(current));
+       }
+       // continue walking in the KeySet....
+}
+
+keyDel(base);
+ksDel(ks);
+kdbClose (handle);
+ * @endcode
+ * 
+ * @return a bit array pointing the differences
+ * @param key1 first key
+ * @param key2 second key
+ * @see #keyswitch_t
+ * @ingroup keytest
+ */
+keyswitch_t keyCompare(const Key *key1, const Key *key2)
+{
+       keyswitch_t ret=0;
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+       const char *comment1 = keyComment(key1);
+       const char *comment2 = keyComment(key2);
+       const char *owner1 = keyOwner(key1);
+       const char *owner2 = keyOwner(key2);
+       const void *value1 = keyValue(key1);
+       const void *value2 = keyValue(key2);
+       int remove1 = keyNeedRemove(key1);
+       int remove2 = keyNeedRemove(key2);
+       ssize_t size1 = keyGetValueSize(key1);
+       ssize_t size2 = keyGetValueSize(key2);
+
+
+       if (key1->uid != key2->uid)        ret|=KEY_UID;
+       if (key1->gid != key2->gid)        ret|=KEY_GID;
+       if (key1->type != key2->type)      ret|=KEY_TYPE;
+       if ((key1->mode) != (key2->mode))  ret|=KEY_MODE;
+       if (remove1 != remove2)            ret|=KEY_REMOVE;
+       if (strcmp(name1, name2))          ret|=KEY_NAME;
+       if (strcmp(comment1, comment2))    ret|=KEY_COMMENT;
+       if (strcmp(owner1, owner2))        ret|=KEY_OWNER;
+       if (size1 != size2)                ret|=KEY_VALUE;
+       if (memcmp(value1, value2, size1)) ret|=KEY_VALUE;
+
+       return ret;
+}
+
+
diff --git a/src/libelektra/keyvalue.c b/src/libelektra/keyvalue.c
new file mode 100755 (executable)
index 0000000..792b6fe
--- /dev/null
@@ -0,0 +1,723 @@
+ /***************************************************************************
+                      keyvalue.c  -  Methods for Key manipulation
+                             -------------------
+    begin                : Fri Sep 26 2008
+    copyright            : (C) 2008 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+/**
+ * @defgroup keyvalue Key :: Value Manipulation Methods
+ * @brief Methods to do various operations on Key values.
+ *
+ * A key can contain a value in different format. The most
+ * likely situation is, that the value is interpreted as
+ * text. Use keyGetString() for that.
+ * You can save any Unicode Symbols and Elektra will
+ * take care that you get the same back, independent of
+ * your current environment.
+ *
+ * In some situations this idea fails. When you need exactly
+ * the same value back without any interpretation of the
+ * characters, there is keySetBinary(). If you use that, its
+ * very likely that your Configuration is not according
+ * to the standard. Also for Numbers, Booleans and Date you
+ * should use keyGetString(). To do so, you might use strtod()
+ * strtol() and then atol() or atof() to convert back.
+ *
+ * To use them:
+ * @code
+#include <kdb.h>
+ * @endcode
+ *
+ *
+ */
+
+
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+
+/******************************************* 
+ *    General value manipulation methods   *
+ *******************************************/
+
+
+
+
+
+/**
+ * Return a pointer to the real internal @p key value.
+ *
+ * This is a much more efficient version of keyGetString()
+ * keyGetBinary(), and you should use it if you are responsible enough to
+ * not mess up things. You are not allowed to modify anything
+ * in the returned string. If you need a copy of the Value, consider
+ * to use keyGetString() or keyGetBinary() instead.
+ *
+ * @section string String Handling
+ *
+ * If @p key is string (keyIsString()), you may cast the returned as a
+ * @c "char *" because you'll get a NULL terminated regular string.
+ *
+ * keyValue() returns "" in string mode when there is no value. The reason is
+ * @code
+key=keyNew(0);
+keySetString(key,"");
+keyValue(key); // you would expect "" here
+keyDel(key);
+ * @endcode
+ *
+ * @section binary Binary Data Handling
+ *
+ * If the data is binary, the size of the value must be determined by
+ * keyGetValueSize(), any strlen() operations are not suitable to determine
+ * the size.
+ *
+ * keyValue() returns 0 in binary mode when there is no value. The reason is
+ * @code
+int i=23;
+key=keyNew(0);
+keySetBinary(key, 0, 0);
+keyValue(key); // you would expect 0 here
+
+keySetBinary(key,"", 1);
+keyValue(key); // you would expect "" (a pointer to '\0') here
+
+keySetBinary(key, (void*)&i, 4);
+(int*)keyValue(key); // you would expect a pointer to (int)23 here
+keyDel(key);
+ * @endcode
+ *
+ * @note Note that the Key structure keeps its own size field that is calculated
+ * by library internal calls, so to avoid inconsistencies, you
+ * must never use the pointer returned by keyValue() method to set a new
+ * value. Use keySetString() or keySetBinary() instead.
+ *
+ * @warning Binary keys will return a NULL pointer when there is no data in contrast
+ * to keyName(), keyBaseName(), keyOwner() and keyComment(). For string value the
+ * behaviour is the same.
+ *
+ * @par Example:
+ * @code
+KDB *handle = kdbOpen();
+KeySet *ks=ksNew(0);
+Key *current=0;
+
+kdbGetByName(handle,ks,"system/sw/my",KDB_O_SORT|KDB_O_RECURSIVE);
+
+ksRewind(ks);
+while(current=ksNext(ks)) {
+       size_t size=0;
+       
+       if (keyIsBin(current)) {
+               size=keyGetValueSize(current);
+               printf("Key %s has a value of size %d bytes. Value: <BINARY>\nComment: %s",
+                       keyName(current),
+                       size,
+                       keyComment(current));
+       } else {
+               size=kdbiStrLen((char *)keyValue(current));
+               printf("Key %s has a value of size %d bytes. Value: %s\nComment: %s",
+                       keyName(current),
+                       size,
+                       (char *)keyValue(current),
+                       keyComment(current));
+       }
+}
+
+ksDel (ks);
+kdbClose (handle);
+ * @endcode
+ *
+ * @param key the key object to work with
+ * @return a pointer to internal value
+ * @return "" when there is no data and key is not binary
+ * @return 0 where there is no data and key is binary
+ * @return 0 on NULL pointer
+ * @see keyGetValueSize(), keyGetString(), keyGetBinary()
+ * @ingroup keyvalue
+ */
+const void *keyValue(const Key *key)
+{
+       if (!key) return 0;
+
+       if (!key->data)
+       {
+               /*errno=KDB_ERR_NOKEY;*/
+               if (keyIsBinary(key)) return 0;
+               else return "";
+       }
+
+       return key->data;
+}
+
+
+const void *takeout_keyValue(Key *key)
+{
+   void *ret;
+       if (!key) return 0;
+
+       if (!key->data)
+       {
+               /*errno=KDB_ERR_NOKEY;*/
+               if (keyIsBinary(key)) return 0;
+               else return strdup("");
+       }
+
+   ret = key->data;
+   key->data = NULL;
+       return ret;
+}
+
+
+
+
+/**
+ * Returns the number of bytes needed to store the key value, including the
+ * NULL terminator.
+ *
+ * It returns the correct size, independent of the Key Type.
+ * If it is a binary there might be '\\0' values in it.
+ *
+ * For an empty string you need one byte to store the ending NULL.
+ * For that reason 1 is returned. This is not true for binary data,
+ * so there might be returned 0 too.
+ *
+ * A binary key has no '\\0' termination. String types have it, so to there
+ * length will be added 1 to have enough space to store it.
+ *
+ * This method can be used with malloc() before keyGetString() or keyGetBinary()
+ * is called.
+ *
+ * @code
+char *buffer;
+buffer = malloc (keyGetValueSize (key));
+// use this buffer to store the value (binary or string)
+// pass keyGetValueSize (key) for maxSize
+ * @endcode
+ *
+ * @param key the key object to work with
+ * @return the number of bytes needed to store the key value
+ * @return 1 when there is no data and type is not binary
+ * @return 0 when there is no data and type is binary
+ * @return -1 on null pointer
+ * @see keyGetString(), keyGetBinary(), keyValue()
+ * @ingroup keyvalue
+ */
+ssize_t keyGetValueSize(const Key *key)
+{
+       if (!key) return -1;
+
+       if (!key->data)
+       {
+               /*errno=KDB_ERR_NODATA;*/
+               if (keyIsBinary(key)) return 0;
+               else return 1;
+       }
+
+       return key->dataSize;
+}
+
+
+
+
+/**
+ * Get the value of a key as a string.
+ *
+ * When there is no value inside the string, 1 will
+ * be returned and the returnedString will be empty
+ * "" to avoid programming errors that old strings are
+ * shown to the user.
+ *
+ * For binary values see keyGetBinary() and keyIsBinary().
+ *
+ * @par Example:
+ * @code
+Key *key = keyNew ("user/keyname", KEY_END);
+char buffer[300];
+
+if (keyGetString(key,buffer,sizeof(buffer)) == -1)
+{
+       // handle error
+} else {
+       printf ("buffer: %s\n", buffer);
+}
+ * @endcode
+ *
+ * @param key the object to gather the value from
+ * @param returnedString pre-allocated memory to store a copy of the key value
+ * @param maxSize number of bytes of allocated memory in @p returnedString
+ * @return the number of bytes actually copied to @p returnedString, including
+ *     final NULL
+ * @return 1 if the string is empty
+ * @return -1 on NULL pointer
+ * @return -1 on type mismatch
+ * @return maxSize is 0, too small for string or is larger than SSIZE_MAX
+ * @see keyValue(), keyGetValueSize(), keySetString()
+ * @see keyGetBinary() for working with binary data
+ * @ingroup keyvalue
+ */
+ssize_t keyGetString(const Key *key, char *returnedString, size_t maxSize)
+{
+       if (!key) return -1;
+
+       if (!maxSize) return -1;
+       if (!returnedString) return -1;
+       if (maxSize > SSIZE_MAX) return -1;
+
+       if (!keyIsString(key))
+       {
+               /*errno=KDB_ERR_TYPEMISMATCH;*/
+               return -1;
+       }
+
+       if (!key->data) {
+               returnedString[0]=0;
+               return 1;
+       }
+
+       if (key->dataSize > maxSize) {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       }
+
+
+       strncpy(returnedString,key->data, maxSize);
+       return key->dataSize;
+}
+
+
+
+/**
+ * Set the value for @p key as @p newStringValue.
+ *
+ * The function will allocate and save a private copy of @p newStringValue, so
+ * the parameter can be freed after the call.
+ *
+ * String values will be saved in backend storage, when kdbSetKey() will be
+ * called, in UTF-8 universal encoding, regardless of the program's current
+ * encoding, when compiled with --enable-iconv.
+ *
+ * The type will be set to KEY_TYPE_STRING.
+ * When the type of the key is already a string type it won't be changed.
+ *
+ * @param key the key to set the string value
+ * @param newStringValue NULL-terminated text string to be set as @p key's
+ *     value
+ * @return the number of bytes actually saved in private struct including final
+ *     NULL
+ * @return -1 on NULL pointer
+ * @see keyGetString(), keyValue()
+ * @ingroup keyvalue
+ */
+ssize_t keySetString(Key *key, const char *newStringValue)
+{
+       ssize_t ret=0;
+
+       if (!key) return -1;
+
+       if (!newStringValue || newStringValue[0] == '\0') ret=keySetRaw(key,0,0);
+       else ret=keySetRaw(key,newStringValue,kdbiStrLen(newStringValue));
+
+       if (!keyIsString(key)) keySetType(key, KEY_TYPE_STRING);
+
+       return ret;
+}
+
+
+
+
+
+/**
+ * Get the value of a key as a binary.
+ *
+ * If the type is not binary -1 will be returned.
+ *
+ * When the binary data is empty (this is not the same as ""!)
+ * 0 will be returned and the returnedBinary will not be changed.
+ *
+ * For string values see keyGetString() and keyIsString().
+ *
+ * When the returnedBinary is to small to hold the data
+ * (its maximum size is given by maxSize),
+ * the returnedBinary will not be changed and -1 is returned.
+ *
+ * @par Example:
+ * @code
+Key *key = keyNew ("user/keyname", KEY_TYPE, KEY_TYPE_BINARY, KEY_END);
+char buffer[300];
+
+if (keyGetBinary(key,buffer,sizeof(buffer)) == -1)
+{
+       // handle error
+}
+ * @endcode
+ *
+ * @param key the object to gather the value from
+ * @param returnedBinary pre-allocated memory to store a copy of the key value
+ * @param maxSize number of bytes of pre-allocated memory in @p returnedBinary
+ * @return the number of bytes actually copied to @p returnedBinary
+ * @return 0 if the binary is empty
+ * @return -1 on NULL pointers
+ * @return -1 when maxSize is 0, too small to hold the value or larger than SSIZE_MAX
+ * @return -1 on typing error when the key is not binary
+ * @see keyValue(), keyGetValueSize(), keySetBinary()
+ * @see keyGetString() and keySetString() as preferred alternative to binary
+ * @see keyIsBinary() to see how to check for binary type
+ * @ingroup keyvalue
+ */
+ssize_t keyGetBinary(const Key *key, void *returnedBinary, size_t maxSize)
+{
+       if (!key) return -1;
+       if (!returnedBinary) return -1;
+       if (!maxSize) return -1;
+
+       if (maxSize > SSIZE_MAX) return -1;
+
+       if (!keyIsBinary(key))
+       {
+               /*errno=KDB_ERR_TYPEMISMATCH;*/
+               return -1;
+       }
+
+       if (!key->data)
+       {
+               return 0;
+       }
+
+       if (key->dataSize > maxSize)
+       {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       }
+
+
+       memcpy(returnedBinary,key->data,key->dataSize);
+       return key->dataSize;
+}
+
+
+
+/**
+ * Set the value of a key as a binary.
+ *
+ * A private copy of @p newBinary will allocated and saved inside @p key,
+ * so the parameter can be deallocated after the call.
+ *
+ * The @c filesys backend, when used through a kdbSetKey(), will make the
+ * value be kdbbEncoded into a human readable hex-digit text format.
+ *
+ * Consider using a string key instead.
+ *
+ * When newBinary is a NULL pointer the binary will be freed and 0 will
+ * be returned.
+ *
+ * @note When the type of the key is already a binary type it won't be changed.
+ *
+ * @param key the object on which to set the value
+ * @param newBinary is a pointer to any binary data or NULL to free the previous set data
+ * @param dataSize number of bytes to copy from @p newBinary
+ * @return the number of bytes actually copied to internal struct storage
+ * @return 0 when the internal binary was freed
+ * @return -1 on NULL pointer
+ * @return -1 when dataSize is 0 (but newBinary not NULL) or larger than SSIZE_MAX
+ * @see keyGetBinary()
+ * @see keyIsBinary() to check if the type is binary
+ * @see keyGetString() and keySetString() as preferred alternative to binary
+ * @ingroup keyvalue
+ */
+ssize_t keySetBinary(Key *key, const void *newBinary, size_t dataSize)
+{
+       ssize_t ret=0;
+
+       if (!key) return -1;
+       if (!dataSize && newBinary) return -1;
+       if (dataSize > SSIZE_MAX) return -1;
+
+       ret = keySetRaw(key,newBinary,dataSize);
+
+       if (!keyIsBinary(key)) keySetType(key, KEY_TYPE_BINARY);
+
+       return ret;
+}
+
+/*
+ * Set raw  data as the value of a key.
+ * If NULL pointers are passed, key value is cleaned.
+ * This method will not change or set the key type, and should not be
+ * used unless working with user-defined value types.
+ *
+ * @param key the key object to work with
+ * @param newBinary array of bytes to set as the value
+ * @param dataSize number bytes to use from newBinary, including the final NULL
+ * @return The number of bytes actually set in internall buffer.
+ * @see keySetType(), keySetString(), keySetBinary()
+ * @ingroup keyvalue
+ */
+ssize_t keySetRaw(Key *key, const void *newBinary, size_t dataSize)
+{
+       if (!dataSize || !newBinary) {
+               if (key->data) {
+                       free(key->data);
+                       key->data=0;
+               }
+               key->dataSize = 0;
+               key->flags |= KEY_FLAG_SYNC;
+               if (keyIsBinary(key)) return 0;
+               return 1;
+       }
+
+       key->dataSize=dataSize;
+       if (key->data) {
+               char *p=0;
+               p=realloc(key->data,key->dataSize);
+               if (NULL==p) return -1;
+               key->data=p;
+       } else {
+               key->data=malloc(key->dataSize);
+        }
+
+       if (!key->data) return -1;
+
+       memcpy(key->data,newBinary,key->dataSize);
+       key->flags |= KEY_FLAG_SYNC;
+       return keyGetValueSize (key);
+}
+
+
+
+
+
+/********************************************* 
+ *    General comment manipulation methods   *
+ *********************************************/
+
+
+
+
+/**
+ * Return a pointer to the real internal @p key comment.
+ *
+ * This is a much more efficient version of keyGetComment() and you
+ * should use it if you are responsible enough to not mess up things.
+ * You are not allowed to change anything in the memory region the
+ * returned pointer points to.
+ *
+ * keyComment() returns "" when there is no keyComment. The reason is
+ * @code
+key=keyNew(0);
+keySetComment(key,"");
+keyComment(key); // you would expect "" here
+keyDel(key);
+ * @endcode
+ *
+ * See keySetComment() for more information on comments.
+ *
+ * @note Note that the Key structure keeps its own size field that is calculated
+ * by library internal calls, so to avoid inconsistencies, you
+ * must never use the pointer returned by keyComment() method to set a new
+ * value. Use keySetComment() instead.
+ *
+ * @param key the key object to work with
+ * @return a pointer to the internal managed comment
+ * @return "" when there is no comment
+ * @return 0 on NULL pointer
+ * @see keyGetCommentSize() for size and keyGetComment() as alternative
+ * @ingroup keyvalue
+ */
+const char *keyComment(const Key *key)
+{
+       if (!key) return 0;
+
+       if (!key->comment)
+       {
+               /*errno=KDB_ERR_NOKEY;*/
+               return "";
+       }
+
+       return key->comment;
+}
+
+
+
+
+/**
+ * Calculates number of bytes needed to store a key comment, including
+ * final NULL.
+ *
+ * Use this method to know to size for allocated memory to retrieve
+ * a key comment.
+ *
+ * See keySetComment() for more information on comments.
+ *
+ * For an empty key name you need one byte to store the ending NULL.
+ * For that reason 1 is returned.
+ *
+ * @code
+char *buffer;
+buffer = malloc (keyGetCommentSize (key));
+// use this buffer to store the comment
+// pass keyGetCommentSize (key) for maxSize
+ * @endcode
+ *
+ * @param key the key object to work with
+ * @return number of bytes needed
+ * @return 1 if there is no comment
+ * @return -1 on NULL pointer
+ * @see keyGetComment(), keySetComment()
+ * @ingroup keyvalue
+ */
+ssize_t keyGetCommentSize(const Key *key)
+{
+       if (!key) return -1;
+
+       if (!key->comment)
+       {
+               /*errno=KDB_ERR_NODESC;*/
+               return 1;
+       }
+
+       return key->commentSize;
+}
+
+
+
+/**
+ * Get the key comment.
+ *
+ * @section comment Comments
+ *
+ * A Key comment is description for humans what this key is for. It may be a
+ * textual explanation of valid values, when and why a user or administrator
+ * changed the key or any other text that helps the user or administrator related
+ * to that key.
+ *
+ * Don't depend on a comment in your program. A user is
+ * always allowed to remove or change it in any way he wants to. But you are
+ * allowed or even encouraged to always show the content of the comment
+ * to the user and allow him to change it.
+ *
+ * @param key the key object to work with
+ * @param returnedComment pre-allocated memory to copy the comments to
+ * @param maxSize number of bytes that will fit returnedComment
+ * @return the number of bytes actually copied to @p returnedString, including
+ *     final NULL
+ * @return 1 if the string is empty
+ * @return -1 on NULL pointer
+ * @return -1 if maxSize is 0, not enough to store the comment or when larger then SSIZE_MAX
+ * @see keyGetCommentSize(), keySetComment()
+ * @ingroup keyvalue
+ */
+ssize_t keyGetComment(const Key *key, char *returnedComment, size_t maxSize)
+{
+       if (!key) return -1;
+
+       if (!maxSize) return -1;
+       if (!returnedComment) return -1;
+       if (maxSize > SSIZE_MAX) return -1;
+
+
+       if (!key->comment)
+       {
+               /*errno=KDB_ERR_NODESC;*/
+               returnedComment[0]=0;
+               return 1;
+       }
+
+       strncpy(returnedComment,key->comment,maxSize);
+       if (maxSize < key->commentSize) {
+               /*errno=KDB_ERR_TRUNC;*/
+               return -1;
+       }
+       return key->commentSize;
+}
+
+
+
+
+
+/**
+ * Set a comment for a key.
+ *
+ * A key comment is like a configuration file comment.
+ * See keySetComment() for more information.
+ *
+ * @param key the key object to work with
+ * @param newComment the comment, that can be freed after this call.
+ * @return the number of bytes actually saved including final NULL
+ * @return 1 when the comment was freed
+ * @return -1 on NULL pointer or memory problems
+ * @see keyGetComment()
+ * @ingroup keyvalue
+ */
+ssize_t keySetComment(Key *key, const char *newComment)
+{
+       ssize_t size=0;
+
+       if (!key) return -1;
+
+       if (newComment && (size=kdbiStrLen(newComment)) > 1)
+       {
+               if (key->comment) {
+                       char *p=0;
+                       p=realloc(key->comment,size);
+                       if (NULL==p) {
+                               key->commentSize = 0;
+                               /*errno=KDB_ERR_NOMEM;*/
+                               return -1;
+                       }
+                       key->comment=p;
+               } else {
+                       key->comment=malloc(size);
+                       if (!key->comment) {
+                               key->commentSize = 0;
+                               /*errno=KDB_ERR_NOMEM;*/
+                               return -1;
+                       }
+               }
+
+               strcpy(key->comment,newComment);
+               key->flags |= KEY_FLAG_SYNC;
+               key->commentSize=size;
+       } else if (key->comment) {
+               free(key->comment);
+               key->comment=0;
+               key->commentSize=1;
+               key->flags |= KEY_FLAG_SYNC;
+       }
+       return key->commentSize;
+}
+
diff --git a/src/libelektra/serialize.c b/src/libelektra/serialize.c
new file mode 100644 (file)
index 0000000..ab64691
--- /dev/null
@@ -0,0 +1,201 @@
+/***************************************************************************
+                 serialze.c  -  Serializing Methods
+                             -------------------
+    begin                : Sat Nov 23 2007
+    copyright            : (C) 2007 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "kdb.h"
+#include "kdbprivate.h"
+
+
+/**
+ * Returns the size of a key.
+ *
+ * This is the space needed for metainfo,
+ * data, comment, name and owner.
+ *
+ * @param key the key object to work with
+ */
+size_t keyGetSerializedSize(const Key *key) {
+       return sizeof (Key) +
+               key->dataSize +
+               key->commentSize +
+               key->keySize +
+               key->ownerSize;
+}
+
+
+/**
+ * Writes a block of memory with the entire key serialized, including
+ * metainfo, value, comment and full name.
+ *
+ * @param key the key object to work with
+ * @param serialized where to store the serialized data
+ * @param maxSize size in buffer reserved for serialized data
+ * @return -1 on failure
+ * @return 0 on success
+ * @see keyUnserialize()
+ * @ingroup keymisc
+ */
+int keySerialize(const Key *key, void *serialized, size_t maxSize)
+{
+       size_t recordSize=0;
+
+       recordSize=keyGetSerializedSize(key);
+
+       if (recordSize > maxSize)
+       {
+               /*errno = KDB_ERR_TRUNC;*/
+               return -1;
+       }
+
+       memset(serialized,0,recordSize);
+
+       /* First part: the struct */
+       memcpy(serialized,key,sizeof(Key));
+
+       /* Second part: the comment */
+       if (key->comment) memcpy((char *)(serialized)+sizeof(Key),
+                       key->comment,key->commentSize);
+
+       /* Third part: the value */
+       if (key->data) memcpy((char *)(serialized)+sizeof(Key)+key->commentSize,
+                       key->data,key->dataSize);
+
+       /* Fourth part: the name */
+       if (key->key) memcpy((char *)(serialized)+sizeof(Key)+key->commentSize+key->dataSize,
+                       key->key,key->keySize);
+
+       /* Fifth part: the owner */
+       if (key->owner) memcpy((char *)(serialized)+sizeof(Key)+key->commentSize+key->dataSize+key->keySize,
+                       key->owner,key->ownerSize);
+
+       return recordSize;
+}
+
+/**
+ * Given a membory block created by keySerialize(), unserialize it into
+ * a Key structure and return it
+ *
+ * The @p serialized can be freed after this call, because memory will be
+ * allocated for all elements of the new key;
+ *
+ * keyDel() the key.
+ *
+ * @param serialized points to memory block where serialized key resides
+ * @see keySerialize()
+ * @ingroup keymisc
+ */
+Key *keyUnserialize(const void *serialized) {
+       Key *key=0;
+       
+       if (!serialized) return 0;
+       
+       key=malloc(sizeof(Key));
+       
+       /* First part: the struct */
+       memcpy(key,serialized,sizeof(Key));
+       
+       /* Second part: the comment */
+       if (key->commentSize) {
+               key->comment=malloc(key->commentSize);
+               memcpy(key->comment,
+                       (char *)(serialized)+sizeof(Key),
+                       key->commentSize);
+       }
+       
+       /* Third part: the value */
+       if (key->dataSize) {
+               key->data=malloc(key->dataSize);
+               memcpy(key->data,
+                       (char *)(serialized)+sizeof(Key)+key->commentSize,
+                       key->dataSize);
+       }
+       
+       /* Fourth part: the key name */
+       if (key->keySize) {
+               key->key=malloc(key->keySize);
+               memcpy(key->key,
+                       (char *)(serialized)+sizeof(Key)+key->commentSize+key->dataSize,
+                       key->keySize);
+       }
+
+       /* Fifth part: the owner */
+       if (key->ownerSize) {
+               key->owner=malloc(key->ownerSize);
+               memcpy(key->owner,
+                       (char *)(serialized)+sizeof(Key)+key->commentSize+key->dataSize+key->keySize,
+                       key->ownerSize);
+       }
+
+       return key;
+}
+
+/**
+ * Compose a key out of a memoryblock created by keySerialize().
+ *
+ * No additional memory will be allocated. Make sure not to free or munmap
+ * @p serialized.
+ *
+ * keyDel() actually does not del the key, so it can be saftely be used in keySets.
+ * To get rid of the key just delete @p serialized, but be sure that all references
+ * are gone: remove it from any keyset and no pointer should be left pointing on
+ * the key.
+ *
+ * @param serialized points to memory block where serialized key resides
+ * @see keySerialize()
+ * @ingroup keymisc
+ */
+Key *keyCompose(const void *serialized) {
+       Key *key=0;
+
+       if (!serialized) return 0;
+
+       key=(Key *)serialized;
+
+       /* First part: the metainfo */
+       if (key->commentSize) key->comment=(char *)(serialized)+sizeof(Key);
+
+       /* Third part: the value */
+       if (key->dataSize) key->data= (char *)(serialized)+sizeof(Key)+key->commentSize;
+
+       /* Fourth part: the name */
+       if (key->keySize) key->key=(char *)(serialized)+sizeof(Key)+key->commentSize+key->dataSize;
+
+       /* Fifth part: the owner*/
+       if (key->ownerSize) key->owner=(char *)(serialized)+sizeof(Key)+key->commentSize+key->dataSize+key->keySize;
+
+       key->ksReference = SSIZE_MAX;
+
+       return key;
+}
+
diff --git a/src/libelektra/split.c b/src/libelektra/split.c
new file mode 100644 (file)
index 0000000..aa7b78b
--- /dev/null
@@ -0,0 +1,157 @@
+/***************************************************************************
+            split.c  -  Functions for splitting keysets for kdbSet
+                             -------------------
+    begin                : Fri 21 Mar 2008
+    copyright            : (C) 2008 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <kdbbackend.h>
+
+
+void free_splitted_keysets(Split *keysets)
+{
+       int i;
+       for (i=0;i<keysets->no;i++) {
+               ksDel(keysets->keysets[i]);
+       }
+       free (keysets->keysets);
+       free (keysets->handles);
+       free (keysets->parents);
+       free (keysets->syncbits);
+       free (keysets->belowparents);
+       free (keysets);
+}
+
+void init_splitted_keysets(Split *ret)
+{
+       ret->no=0;
+       ret->keysets=malloc(sizeof(KeySet*));
+       ret->keysets[0]=NULL;
+       ret->handles=malloc(sizeof(KDB *));
+       ret->handles[0]=NULL;
+       ret->parents=malloc(sizeof(Key *));
+       ret->parents[0]=NULL;
+       ret->syncbits=malloc(sizeof(int));
+       ret->syncbits[0] = 0;
+       ret->belowparents=malloc(sizeof(int));
+       ret->belowparents[0] = 0;
+}
+
+void resize_splitted_keysets(Split *ret)
+{
+       ret->no++;
+       ret->keysets=realloc(ret->keysets,(ret->no+1)*sizeof(KeySet *));
+       ret->keysets[ret->no]=NULL;
+       ret->handles=realloc(ret->handles,(ret->no+1)*sizeof(KDB *));
+       ret->handles[ret->no]=NULL;
+       ret->parents=realloc(ret->parents,(ret->no+1)*sizeof(Key *));
+       ret->parents[ret->no]=NULL;
+       ret->syncbits=realloc(ret->syncbits,(ret->no+1)*sizeof(int));
+       ret->syncbits[ret->no]=0;
+       ret->belowparents=realloc(ret->belowparents,(ret->no+1)*sizeof(int));
+       ret->belowparents[ret->no]=0;
+}
+
+static int keyIsBelowOrSame (Key *key1, Key *key2)
+{
+       const char *name1 = keyName(key1);
+       const char *name2 = keyName(key2);
+
+       if (keyIsBelow (key1, key2)) return 1;
+       else if (!strcmp (name1, name2)) return 1;
+       return 0;
+}
+
+/* Split keysets.
+ * Make sure that parentKey has a name or is a null pointer*/
+Split *split_keyset(KDB *handle, KeySet *ks,
+       Key *parentKey, unsigned long options)
+{
+       Split *ret;
+
+       Key *curKey;
+       KDB *curHandle;
+       int curFound;
+
+       int i;
+
+       ret = malloc (sizeof (Split));
+       init_splitted_keysets (ret);
+
+       ksRewind (ks);
+       while ((curKey = ksNext (ks)) != 0)
+       {
+               curHandle = kdbGetBackend(handle, curKey);
+               curFound = 0;
+
+               if (options & KDB_O_SYNC) curKey->flags |= KEY_FLAG_SYNC;
+               if (options & KDB_O_NOREMOVE) curKey->flags &= ~KEY_FLAG_REMOVE;
+               else if (options & KDB_O_REMOVEONLY) keyRemove(curKey);
+
+               for (i=0; i<ret->no; i++)
+               {
+                       if (curHandle == ret->handles[i] && 
+                               (!parentKey || keyIsBelowOrSame(ret->parents[i], curKey)))
+                       {
+                               curFound = 1;
+                               ksAppendKey(ret->keysets[i],curKey);
+                               if (keyNeedSync (curKey) == 1) ret->syncbits[i]=1;
+                       }
+               }
+
+               if (!curFound)
+               {
+                       resize_splitted_keysets (ret);
+
+                       ret->keysets[ret->no-1] = ksNew (ksGetSize (ks) / APPROXIMATE_NR_OF_BACKENDS + 2, KS_END);
+                       ksAppendKey(ret->keysets[ret->no-1],curKey);
+                       ret->handles[ret->no-1] = curHandle;
+                       ret->parents[ret->no-1] = curKey;
+                       if (parentKey)
+                       {
+                               ret->belowparents[ret->no-1] = keyIsBelowOrSame (parentKey, curKey);
+                       } else ret->belowparents[ret->no-1] = 1;
+                       if (keyNeedSync (curKey) == 1) ret->syncbits[ret->no-1]=1;
+               }
+       }
+
+       return ret;
+}
+
diff --git a/src/libelektra/trie.c b/src/libelektra/trie.c
new file mode 100644 (file)
index 0000000..424c371
--- /dev/null
@@ -0,0 +1,456 @@
+/***************************************************************************
+            trie.c  -  low level functions for backend mounting.
+                             -------------------
+    begin                : Sat Nov 3
+    copyright            : (C) 2007 by Patrick Sabin
+    email                : patricksabin@gmx.at
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#include "kdbbackend.h"
+
+static char *getEntryName(const char *path);
+static void* prefix_lookup(Trie *trie, const char *name);
+static int isOfEntryMountpoint(Key *key, char *entry);
+static int isOfEntryBackend(Key *key, char *entry);
+static int isOfEntryConfig(Key *key, char *entry);
+static int isOfEntry(Key *key, char *entry);
+static char* starts_with(const char *str, char *substr);
+static void* prefix_lookup(Trie *trie, const char *name);
+
+/**
+ * Lookup a backend handle for a specific key.
+ *
+ * The required canonical name is ensured by using a key as parameter,
+ * which will transform the key to canonical representation.
+ *
+ * Will return handle when no more specific KDB could be
+ * found.
+ *
+ * @param handle is the data structure, where the mounted directories are saved.
+ * @param key the key, that should be looked up.
+ * @return the backend handle associated with the key
+ */
+KDB* kdbGetBackend(KDB *handle, const Key *key)
+{
+       char *s;
+       KDB *ret;
+       int len;
+       if (kdbhGetTrie(handle)==NULL) {
+               return handle;
+       }
+
+       len=strlen(keyName(key))+2;
+       s=malloc(len);
+       strncpy(s,keyName(key),len);
+       s[len-2]='/';
+       s[len-1]=0;
+
+       ret = prefix_lookup(kdbhGetTrie(handle),s);
+       free (s);
+       if (!ret) return handle;
+       return ret;
+}
+
+/** Delete a complete trie, close backends and free memory.
+ *
+ * @param trie the data structure, that should be freed
+ * @returns always 0
+ */
+int kdbDelTrie(Trie *trie, CloseMapper close_backend)
+{
+       int i;
+       if (trie==NULL) return 0;
+       for (i=0;i<MAX_UCHAR;i++) {
+               if (trie->text[i]!=NULL) {
+                       kdbDelTrie(trie->childs[i],close_backend);
+                       if (trie->value[i])
+                               close_backend(trie->value[i]);
+                       free(trie->text[i]);
+               }
+       }
+       if (trie->empty_value)
+               close_backend(trie->empty_value);
+       free(trie);
+       return 0;
+}
+
+
+/** Creates a trie from a keyset.
+ *
+ * @param handle data structure where the trie will be stored.
+ * @param ks is a keyset, that contains name/value-pairs. The name is the 
+ * directory where the backend will mounted to, prefixed by KDB_KEY_MOUNTPOINTS "/"; value is a pointer to the backend.
+ * @param mapper is a funtion, that maps the string of the backend name plus the mountpoint to a new created
+ * KDB
+ * return created trie on success, null on failure
+ */
+Trie* createTrie(KeySet *ks, OpenMapper mapper)
+{
+       Key *key;
+       cursor_t current;
+       Trie *trie=0;
+       char *entry_name; /* KDB_KEY_MOUNTPOINTS "/", e.g. system/elektra/mountpoints/fstab" */
+
+       /* Sort the keys alphapetically  */
+       ksSort(ks);
+       current=ksGetCursor(ks);
+
+       ksRewind(ks);
+       key=ksNext(ks);
+
+       for (;key;) {
+               KeySet *config;
+               char *mountpoint=0;
+               const char *backend_name=0;
+
+               if (!strncmp(KDB_KEY_MOUNTPOINTS, keyName(key),KDB_KEY_MOUNTPOINTS_LEN+1)) {
+                       key=ksNext(ks);
+                       continue;
+               }
+               config=ksNew(0);
+               entry_name = getEntryName(keyName(key));
+
+               for (;key;key=ksNext(ks)) {
+                       if (!isOfEntry(key,entry_name)) {
+                               break;
+                       }
+                       if (isOfEntryBackend(key,entry_name)) {
+                               backend_name=keyValue(key);
+                       } else if (isOfEntryMountpoint(key,entry_name)) {
+                               if (*(const char*)keyValue(key)=='\0') {
+                                       mountpoint=kdbiStrDup("");
+                               } else {
+                                       Key *mnt_pnt_key = keyNew (keyValue(key), KEY_END);
+                                       if (! mnt_pnt_key) {
+                                               key=ksNext(ks);
+                                               ksDel(config);
+                                               break;
+                                       }
+                                       mountpoint = kdbiMalloc (keyGetNameSize(mnt_pnt_key)+1);
+                                       sprintf(mountpoint,"%s/",keyName(mnt_pnt_key));
+                                       keyDel(mnt_pnt_key);
+                               }
+                       } else if (isOfEntryConfig(key,entry_name)) {
+                               ksAppendKey(config,key);
+                       }
+               }
+
+               if (backend_name && mountpoint) {
+                       void *p;
+                       p=mapper(backend_name,mountpoint,config);
+                       if (p) {
+                               trie=insert_trie(trie,mountpoint,p);
+                       } else {
+                               ksDel(config);
+                       }
+               }
+               free(entry_name);
+               if (mountpoint) {
+                       free(mountpoint);
+               }
+       }
+
+       ksSetCursor(ks,current);
+       return trie;
+}
+
+/** Creates a trie from a keyset.
+ *
+ * @param handle data structure where the trie will be stored.
+ * @param ks is a keyset, that contains name/value-pairs. The name is the 
+ * directory where the backend will mounted to, prefixed by KDB_KEY_MOUNTPOINTS; value is a pointer to the backend.
+ * @param mapper is a function, that maps the string of the backend name plus the mountpoint to a new created
+ * KDB
+ * return 0 on success, -1 on failure
+ */
+int kdbCreateTrie(KDB *handle, KeySet *ks, OpenMapper mapper)
+{
+       Trie *trie=0;
+       if (kdbhGetTrie(handle)!=0) {
+               return -1;
+       }
+
+       trie=createTrie(ks,mapper);
+       kdbhSetTrie(handle,trie);
+       if (trie==0) {
+               return -1;
+       }
+       return 0;
+}
+
+Trie* insert_trie(Trie *trie, const char *name, const void *value)
+{
+       char* p;
+       int i;
+       unsigned int idx;
+
+       if (name==0) name="";
+       idx=(unsigned int) name[0];
+
+       if (trie==NULL) {
+               trie=malloc(sizeof(Trie));
+               trie->empty_value=0;
+               for (i=0;i<MAX_UCHAR;i++) {
+                       trie->childs[i]=0;
+                       trie->text[i]=0;
+                       trie->textlen[i]=0;
+                       trie->value[i]=0;
+               }
+
+               if (!strcmp("",name)) {
+                       trie->empty_value=(void*)value;
+                       return trie;
+               }
+
+               trie->textlen[idx]=strlen(name);
+
+               trie->text[idx]=kdbiStrDup(name);
+
+               trie->value[idx]=(void*)value;
+               return trie;
+       }
+
+       if (!strcmp("",name)) {
+               trie->empty_value=(void*)value;
+               return trie;
+       }
+
+       if (trie->text[idx]) {
+               /* there exists an entry with the same first character */
+               if ((p=starts_with(name, trie->text[idx]))==0) {
+                       /* the name in the trie is part of the searched name --> continue search */
+                       trie->childs[idx]=insert_trie(trie->childs[idx],name+trie->textlen[idx],value);
+               } else {
+                       /* name in trie doesn't match name --> split trie */
+                       char *newname;
+                       Trie *child;
+                       unsigned int idx2;
+
+                       newname=kdbiStrDup(p);
+                       *p=0; /* shorten the old name in the trie */
+                       trie->textlen[idx]=strlen(trie->text[idx]);
+
+                       child=trie->childs[idx];
+
+                       /* insert the name given as a parameter into the new trie entry */
+                       trie->childs[idx]=insert_trie(NULL, name+(p-trie->text[idx]), value);
+
+                       /* insert the splitted try into the new trie entry */
+
+                       idx2=(unsigned int) newname[0];
+                       trie->childs[idx]->text[idx2]=newname;
+                       trie->childs[idx]->textlen[idx2]=strlen(newname);
+                       trie->childs[idx]->value[idx2]=trie->value[idx];
+                       trie->childs[idx]->childs[idx2]=child;
+
+                       trie->value[idx]=0;
+
+               }
+       } else {
+               /* there doesn't exist an entry with the same first character */
+               trie->text[idx]=kdbiStrDup(name);
+               trie->value[idx]=(void*)value;
+               trie->textlen[idx]=strlen(name);
+       }
+
+       return trie;
+}
+
+Trie *delete_trie(Trie *trie, char *name, CloseMapper closemapper)
+{
+       Trie *tr;
+       unsigned int idx;
+       if (trie==NULL) {
+               return NULL;
+       }
+
+       idx=(unsigned int) name[0];
+
+       if (trie->text[idx]==NULL) {
+               return NULL;
+       }
+
+       if (starts_with(name,trie->text[idx])==0) {
+
+               tr=delete_trie(trie->childs[idx],name+trie->textlen[idx],closemapper);
+
+               if (tr==NULL) {
+                       /* child trie has been deleted */
+                       trie->childs[idx]=NULL;
+                       free(trie->text[idx]);
+                       closemapper(trie->value[idx]);
+                       trie->text[idx]=NULL;
+               }
+
+               return trie;
+       }
+       return NULL;
+}
+
+/******************
+ * Private static declarations
+ ******************/
+
+static char *getEntryName(const char *path)
+{
+       int len;
+       char *ret,*p,*q;
+       len=strlen(path);
+
+       if (len<=KDB_KEY_MOUNTPOINTS_LEN) return 0;
+       p=(char*)path+KDB_KEY_MOUNTPOINTS_LEN;
+       for (q=p;!(*q==0 ||*q=='/');q++);
+
+       ret = malloc (q-p+1);
+       strncpy (ret, p, q-p);
+       ret[q-p] = 0;
+
+       return ret;
+}
+
+static int isOfEntryMountpoint(Key *key, char *entry)
+{
+       const char *s;
+       int len;
+       int entrylen;
+       s=keyName(key);
+       len=strlen(s);
+       entrylen=strlen(entry);
+       if (len<=KDB_KEY_MOUNTPOINTS_LEN) return 0;
+
+       if (strncmp(s,KDB_KEY_MOUNTPOINTS "/",KDB_KEY_MOUNTPOINTS_LEN)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN, entry, entrylen)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN+entrylen, "/mountpoint", sizeof("/mountpoint"))) return 0;
+
+       return 1;
+}
+
+static int isOfEntryBackend(Key *key, char *entry)
+{
+       const char *s;
+       int len;
+       int entrylen;
+       s=keyName(key);
+       len=strlen(s);
+       entrylen=strlen(entry);
+       if (len<=KDB_KEY_MOUNTPOINTS_LEN) return 0;
+
+       if (strncmp(s,KDB_KEY_MOUNTPOINTS "/",KDB_KEY_MOUNTPOINTS_LEN)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN, entry, entrylen)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN+entrylen, "/backend", sizeof("/backend"))) return 0;
+
+       return 1;
+}
+
+static int isOfEntryConfig(Key *key, char *entry)
+{
+       const char *s;
+       int len;
+       int entrylen;
+       s=keyName(key);
+       len=strlen(s);
+       entrylen=strlen(entry);
+       if (len<=KDB_KEY_MOUNTPOINTS_LEN) return 0;
+
+       if (strncmp(s,KDB_KEY_MOUNTPOINTS "/",KDB_KEY_MOUNTPOINTS_LEN)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN, entry, entrylen)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN+entrylen, "/config", sizeof("/config")-1)) return 0;
+
+       return 1;
+}
+
+static int isOfEntry(Key *key, char *entry)
+{
+       const char *s;
+       int len;
+       int entrylen;
+       s=keyName(key);
+       len=strlen(s);
+       entrylen=strlen(entry);
+       if (len<=KDB_KEY_MOUNTPOINTS_LEN) return 0;
+
+       if (strncmp(s,KDB_KEY_MOUNTPOINTS "/",KDB_KEY_MOUNTPOINTS_LEN)) return 0;
+
+       if (strncmp(s+KDB_KEY_MOUNTPOINTS_LEN, entry, entrylen)) return 0;
+
+       if (*(s+KDB_KEY_MOUNTPOINTS_LEN+entrylen)!=0 && *(s+KDB_KEY_MOUNTPOINTS_LEN+entrylen) !='/') return 0;
+
+       return 1;
+}
+
+/* return NULL if string starts with substring, except for the terminating '\0',
+ * otherwise return a pointer to the first mismatch in substr.
+ */
+static char* starts_with(const char *str, char *substr)
+{
+       int i = 0;
+       int sublen = strlen(substr);
+
+       for (i=0;i<sublen;i++) {
+               if (substr[i]!=str[i])
+                       return substr+i;
+       }
+       return 0;
+}
+
+static void* prefix_lookup(Trie *trie, const char *name)
+{
+       unsigned int idx;
+       void * ret=NULL;
+       if (trie==NULL) return NULL;
+
+       idx=(unsigned int) name[0];
+
+       if (trie->text[idx]==NULL) {
+               return trie->empty_value;
+       }
+
+       if (starts_with((char*)name, (char*)trie->text[idx])==0) {
+               ret=prefix_lookup(trie->childs[idx],name+trie->textlen[idx]);
+       } else {
+               return trie->empty_value;
+       }
+
+       if (ret==NULL && trie->value[idx]==NULL) {
+               return trie->empty_value;
+       }
+       if (ret==NULL) return trie->value[idx];
+       return ret;
+}
diff --git a/src/libelektratools/.deps/libelektratools_a-kdbtools.Po b/src/libelektratools/.deps/libelektratools_a-kdbtools.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/libelektratools/.deps/libelektratools_a-stream.Po b/src/libelektratools/.deps/libelektratools_a-stream.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/libelektratools/.deps/libelektratools_la-kdbtools.Plo b/src/libelektratools/.deps/libelektratools_la-kdbtools.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/libelektratools/.deps/libelektratools_la-stream.Plo b/src/libelektratools/.deps/libelektratools_la-stream.Plo
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/libelektratools/Makefile.am b/src/libelektratools/Makefile.am
new file mode 100644 (file)
index 0000000..73c1c02
--- /dev/null
@@ -0,0 +1,27 @@
+# NOTE: on INCLUDES i added $(XML_CFLAGS) too because
+# it seems previous version of libxml macro export
+# path in XML_CFLAGS rather than XML_CPPFLAGS
+# -- Yannick
+
+if HAVE_XML
+
+AM_CPPFLAGS = -DDATADIR=\"$(datadir)\" \
+ -I$(top_srcdir)/src/include $(XML_CPPFLAGS) $(XML_CFLAGS)
+
+elektratools_sources = kdbtools.c stream.c ../include/kdbtools.h ../include/kdbprivate.h ../include/kdb.h
+
+hlvlbackend_LIBRARIES = libelektratools.a
+libelektratools_a_SOURCES = $(elektratools_sources)
+libelektratools_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+hlvlbackend_LTLIBRARIES = libelektratools.la
+libelektratools_la_SOURCES = $(elektratools_sources)
+libelektratools_la_LDFLAGS = -version-info $(ELEKTRATOOLS_VERSION_API)
+libelektratools_la_LIBADD = ../libelektra/libelektra.la $(XML_LIBS)
+libelektratools_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
+
+endif
+
diff --git a/src/libelektratools/README b/src/libelektratools/README
new file mode 100644 (file)
index 0000000..dc3bc4c
--- /dev/null
@@ -0,0 +1,18 @@
+Warning!
+This library is part of the stable libelektra 0.7.0 suite.
+Incompatible work on it is not prohibited, you are encouraged
+to do so.
+
+Note that in the static case, that is when the macro ELEKTRA_STATIC
+is set you need to rename all symbols to libelektratools_LTX_symbols.
+This must only take place in the source files, not in the header,
+otherwise the users symbols will be renamed too, leading to name
+clashes.
+
+In addition to that you must add all symbols in the exportsymbols.sh
+script, located at src/libelektra.
+
+When you do that, the same code of the user can compile easily to a
+static and dynamic variant. The static variant just "renames" the
+symbols, while the dynamic really loads the library and lookup the
+symbols.
diff --git a/src/libelektratools/kdbtools.c b/src/libelektratools/kdbtools.c
new file mode 100644 (file)
index 0000000..fd1da1d
--- /dev/null
@@ -0,0 +1,550 @@
+/***************************************************************************
+            kdbtools.c  -  Elektra High Level Methods
+                             -------------------
+    begin                : Sat Jan 22 2005
+    copyright            : (C) 2005 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+
+
+/* Subversion stuff
+
+$Id$
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <libxml/xmlreader.h>
+#include <libxml/xmlschemas.h>
+
+#include <kdbtools.h>
+#include <kdbbackend.h>
+
+#ifdef ELEKTRA_STATIC
+
+#define ksFromXMLfile libelektratools_LTX_ksFromXMLfile
+#define ksFromXML libelektratools_LTX_ksFromXML
+
+#endif /* ELEKTRA_STATIC */
+
+
+/*
+ * Processes the current <key> node from reader, converting from XML
+ * to a Key object, and ksAppendKey() it to ks.
+ *
+ * See keyToStream() for an example of a <key> node.
+ *
+ * This function is completelly dependent on libxml.
+ * 
+ * @param ks where to put the resulting reded key
+ * @param context a prent key name, so a full name can be calculated
+ *        if the XML node for the current key only provides a basename
+ * @param reader where to read from
+ */
+static int consumeKeyNode(KeySet *ks, const char *context, xmlTextReaderPtr reader)
+{
+       xmlChar *nodeName=0;
+       xmlChar *keyNodeName=0;
+       xmlChar *buffer=0;
+       xmlChar *privateContext=0;
+       Key *newKey=0;
+       int appended=0;
+
+       /* printf("%s", KDB_SCHEMA_PATH); */
+       
+       keyNodeName=xmlTextReaderName(reader);
+       if (!strcmp((char *)keyNodeName,"key")) {
+               type_t type=KEY_TYPE_STRING; /* default type */
+               mode_t isdir=0;
+               int end=0;
+               
+               newKey=keyNew(0);
+
+               /* a <key> must have one of the following:
+                  - a "name" attribute, used as an absolute name overriding the context
+                  - a "basename" attribute, that will be appended to the current context
+                  - a "parent" plus "basename" attributes, both appended to current context
+                  - only a "parent", appended to current context
+               */
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"name");
+               if (buffer) {
+                       /* set absolute name */
+                       keySetName(newKey,(char *)buffer);
+                       xmlFree(buffer); buffer=0;
+               } else {
+                       /* logic for relative name calculation */
+                       
+                       privateContext=xmlTextReaderGetAttribute(reader,
+                               (const xmlChar *)"parent");
+                       buffer=xmlTextReaderGetAttribute(reader,
+                               (const xmlChar *)"basename");
+
+                       if (context) keySetName(newKey,context);
+                       if (privateContext) keyAddBaseName(newKey, (char *)privateContext);
+                       if (buffer) keyAddBaseName(newKey,(char *)buffer);
+
+                       xmlFree(privateContext); privateContext=0;
+                       xmlFree(buffer); buffer=0;
+               }
+
+
+               /* test for a short value attribute, instead of <value> bellow */
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"value");
+               if (buffer) {
+                       keySetRaw(newKey,buffer,kdbiStrLen((char *)buffer));
+                       xmlFree(buffer); buffer=0;
+               }
+
+               /* Parse UID */
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"uid");
+               if (buffer) {
+                       int errsave = errno;
+                       char * endptr;
+                       long int uid = strtol ((const char *)buffer, &endptr, 10);
+                       errno = errsave;
+                       if (endptr != '\0' && *endptr == '\0')
+                       {
+                               keySetUID(newKey,uid);
+                       }
+                       xmlFree(buffer); buffer=0;
+               }
+
+               /* Parse GID */
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"gid");
+               if (buffer) {
+                       int errsave = errno;
+                       char * endptr;
+                       long int gid = strtol ((const char *)buffer, &endptr, 10);
+                       errno = errsave;
+                       if (endptr != '\0' && *endptr == '\0')
+                       {
+                               keySetGID(newKey,gid);
+                       }
+                       xmlFree(buffer); buffer=0;
+               }
+
+               /* Parse mode permissions */
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"mode");
+               int errsave = errno;
+               if (buffer) keySetMode(newKey,strtol((char *)buffer,0,0));
+               errno = errsave;
+               xmlFree(buffer);
+
+
+
+               if (xmlTextReaderIsEmptyElement(reader)) {
+                       /* we have a <key ..../> element */
+                       if (newKey && !appended) {
+                               ksAppendKey(ks,newKey);
+                               appended=1;
+                               end=1;
+                       }
+               }
+
+
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"type");
+               if (buffer) {
+                       if (!strcmp((char *)buffer,"string"))
+                               type=KEY_TYPE_STRING;
+                       else if (!strcmp((char *)buffer,"directory"))
+                               isdir=1;
+                       else if (!strcmp((char *)buffer,"binary"))
+                               type=KEY_TYPE_BINARY;
+                       else if (!strcmp((char *)buffer,"undefined"))
+                               type=KEY_TYPE_UNDEFINED;
+                       else { /* special numerical user-defined value types */
+                               void *converter=0;
+
+                               type=strtol((char *)buffer,(char **)&converter,10);
+                               if ((void *)buffer==converter)
+                                       /* in case of error, fallback to undefined type again */
+                                       type=KEY_TYPE_UNDEFINED;
+                       }
+               }
+               xmlFree(buffer);
+
+               /* If "isdir" appears, everything different from "0", "false" or "no"
+               marks it as a dir key */
+               buffer=xmlTextReaderGetAttribute(reader,(const xmlChar *)"isdir");
+               if (!isdir && buffer)
+               {
+                       if (    strcmp((char *)buffer,"0") &&
+                               strcmp((char *)buffer,"false") &&
+                               strcmp((char *)buffer,"no"))
+                               isdir = 1;
+                       else    isdir = 0;
+               }
+               xmlFree(buffer);
+
+               if (isdir) keySetDir(newKey);
+               keySetType(newKey,type);
+
+               /* Parse everything else */
+               while (!end) {
+                       xmlTextReaderRead(reader);
+                       nodeName=xmlTextReaderName(reader);
+
+                       if (!strcmp((char *)nodeName,"value")) {
+                               if (xmlTextReaderIsEmptyElement(reader) ||
+                                       xmlTextReaderNodeType(reader)==15)
+                               {
+                                       xmlFree (nodeName);
+                                       continue;
+                               }
+                                       
+                               xmlTextReaderRead(reader);
+                               buffer=xmlTextReaderValue(reader);
+                               
+                               if (buffer) {
+                                       /* Key's value type was already set above */
+                                       if (keyIsBinary(newKey)) {
+                                               char *unencoded=0;
+                                               size_t unencodedSize;
+                                               
+                                               unencodedSize=kdbiStrLen((char *)buffer)/2;
+                                               unencoded=malloc(unencodedSize);
+                                               unencodedSize=kdbbDecode((char *)buffer,unencoded);
+                                               if (!unencodedSize) return -1;
+                                                       keySetRaw(newKey,unencoded,unencodedSize);
+                                               free(unencoded);
+                                       } else keySetRaw(newKey,buffer,kdbiStrLen((char *)buffer));
+                               }
+                               xmlFree(buffer);
+                       } else if (!strcmp((char *)nodeName,"comment")) {
+                               ssize_t commentSize=0;
+                               
+                               if (xmlTextReaderIsEmptyElement(reader) ||
+                                       xmlTextReaderNodeType(reader)==15)
+                               {
+                                       xmlFree (nodeName);
+                                       continue;
+                               }
+                                       
+                               xmlTextReaderRead(reader);
+                               buffer=xmlTextReaderValue(reader);
+                               
+                               if ((commentSize=keyGetCommentSize(newKey)) > 1) {
+                                       /*Multiple line comment*/
+                                       char *tmpComment=0;
+                                       tmpComment=malloc(commentSize+
+                                               xmlStrlen(buffer)*sizeof(xmlChar)+1);
+
+                                       if (tmpComment) {
+                                               keyGetComment(newKey,tmpComment,commentSize);
+
+                                               strcat(tmpComment,"\n");
+                                               strcat(tmpComment,(char *)buffer);
+
+                                               keySetComment(newKey,tmpComment);
+
+                                               free(tmpComment); tmpComment=0;
+                                       }
+                               } else keySetComment(newKey,(char *)buffer);
+                               xmlFree(buffer);
+                       } else if (!strcmp((char *)nodeName,"key")) {
+                               /* Here we found </key> or a sub <key>.
+                                  So include current key in the KeySet. */
+                               if (newKey && !appended) {
+                                       ksAppendKey(ks,newKey);
+                                       appended=1;
+                               }
+                               
+                               if (xmlTextReaderNodeType(reader)==15)
+                                       /* found a </key> */
+                                       end=1;
+                               else {
+                                       /* found a sub <key> */
+                                       if (! keyIsDir(newKey)) {
+                                               keySetDir(newKey);
+                                       }
+                                       /* prepare the context (parent) */
+                                       consumeKeyNode(ks,newKey->key,reader);
+                               }
+                       }
+
+                       xmlFree (nodeName);
+               }
+
+               if (privateContext) xmlFree(privateContext);
+
+               /* seems like we forgot the key, lets delete it */
+               if (newKey && !appended) {
+                       keyDel (newKey);
+                       appended=1;
+               }
+       }
+
+       xmlFree(keyNodeName);
+       
+       return 0;
+}
+
+
+
+
+static int consumeKeySetNode(KeySet *ks, const char *context, xmlTextReaderPtr reader)
+{
+       xmlChar *nodeName=0;
+       xmlChar *keySetNodeName=0;
+       xmlChar *privateContext=0;
+       xmlChar fullContext[800]="";
+       
+       keySetNodeName=xmlTextReaderName(reader);
+       if (!strcmp((char *)keySetNodeName,"keyset")) {
+               int end=0;
+
+               privateContext=xmlTextReaderGetAttribute(reader,(const xmlChar *)"parent");
+               if (context && privateContext) {
+                       xmlStrPrintf(fullContext,sizeof(fullContext),
+                               (const xmlChar *)"%s/%s", context, privateContext);
+               }
+
+               /* Parse everything else */
+               while (!end) {
+                       xmlTextReaderRead(reader);
+                       nodeName=xmlTextReaderName(reader);
+
+                       if (!strcmp((char *)nodeName,"key")) {
+                               if (privateContext) consumeKeyNode(ks,(char *)(*fullContext?fullContext:privateContext),reader);
+                               else consumeKeyNode(ks,context,reader);
+                       } else if (!strcmp((char *)nodeName,"keyset")) {
+                               /* A <keyset> can have nested <keyset>s */
+                               if (xmlTextReaderNodeType(reader)==15)
+                                       /* found a </keyset> */
+                                       end=1;
+                               else if (privateContext)
+                                       consumeKeySetNode(ks, (char *)(*fullContext?fullContext:privateContext), reader);
+                               else consumeKeySetNode(ks, context, reader);
+                       }
+                       xmlFree(nodeName);
+               }
+               if (privateContext) xmlFree(privateContext),privateContext=0;
+       }
+       xmlFree (keySetNodeName);
+       return 0;
+}
+
+
+
+/*
+ * This is the workhorse behind for ksFromXML() and ksFromXMLfile().
+ * It will process the entire XML document in reader and convert and
+ * save it in ks KeySet. Each node is processed by the processNode() function.
+ *
+ * This function is completelly dependent on libxml.
+ */
+static int ksFromXMLReader(KeySet *ks,xmlTextReaderPtr reader)
+{
+       int ret;
+       xmlChar *nodeName=0;
+
+       ret = xmlTextReaderRead(reader); /* go to first node */
+       while (ret == 1) {
+               /* walk node per node until the end of the stream */
+               nodeName=xmlTextReaderName(reader);
+               
+               if (!strcmp((char *)nodeName,"key"))
+                       consumeKeyNode(ks, 0, reader);
+               else if (!strcmp((char *)nodeName,"keyset"))
+                       consumeKeySetNode(ks, 0, reader);
+               
+               ret = xmlTextReaderRead(reader);
+
+               xmlFree (nodeName);
+       }
+       
+       if (ret) fprintf(stderr,"kdb: Failed to parse XML input\n");
+
+       return ret;
+}
+
+/*
+static int isValidXML(xmlDocPtr doc,char *schemaPath)
+{
+       xmlSchemaPtr wxschemas = NULL;
+       xmlSchemaValidCtxtPtr ctxt;
+       xmlSchemaParserCtxtPtr ctxt2=NULL;
+       int ret=0;
+
+       ctxt2 = xmlSchemaNewParserCtxt(schemaPath);
+
+
+       if (ctxt2==NULL) {
+               xmlFreeDoc(doc);
+               return 1;
+       }
+       
+       xmlSchemaSetParserErrors(ctxt2,
+               (xmlSchemaValidityErrorFunc) fprintf,
+               (xmlSchemaValidityWarningFunc) fprintf,
+               stderr);
+       wxschemas = xmlSchemaParse(ctxt2);
+       
+       if (wxschemas==NULL) {
+               xmlSchemaFreeParserCtxt(ctxt2);
+               xmlFreeDoc(doc);
+               return 1;
+       }
+       
+       ctxt = xmlSchemaNewValidCtxt(wxschemas);
+       xmlSchemaSetValidErrors(ctxt,
+               (xmlSchemaValidityErrorFunc) fprintf,
+               (xmlSchemaValidityWarningFunc) fprintf,
+               stderr);
+       
+       if (ctxt==NULL) {
+               xmlSchemaFree(wxschemas);
+               xmlSchemaFreeParserCtxt(ctxt2);
+               xmlFreeDoc(doc);
+               return 1;
+       }
+       
+       ret = xmlSchemaValidateDoc(ctxt, doc);
+       xmlSchemaFreeValidCtxt(ctxt);
+       xmlSchemaFree(wxschemas);
+       xmlSchemaFreeParserCtxt(ctxt2);
+
+       return ret;
+}
+*/
+
+
+
+/**
+ * Given an XML @p filename, open it, validate schema, process nodes,
+ * convert and save it in the @p ks KeySet.
+ *
+ * Currently, the XML file can have many root @c @<keyset@> and @c @<key@> nodes.
+ * They will all be reduced to simple keys returned in @p ks.
+ *
+ * To check if the xml file is valid (best before you read from it):
+ * @code
+#include 
+char schemaPath[513];
+schemaPath[0]=0;
+ret=kdbGetString(handle, KDB_SCHEMA_PATH_KEY,schemaPath,sizeof(schemaPath));
+
+if (ret==0) ret = isValidXML(filename,schemaPath);
+else ret = isValidXML(filename,KDB_SCHEMA_PATH); 
+ * @endcode
+ *
+ * @param ks the keyset
+ * @param filename the file to parse
+ * @ingroup stream
+ */
+int ksFromXMLfile(KeySet *ks,const char *filename)
+{
+       xmlTextReaderPtr reader;
+       xmlDocPtr doc;
+       int ret=0;
+
+       doc = xmlParseFile(filename);
+       if (doc==NULL) return 1;
+
+       if (!ret) {
+               reader=xmlReaderWalker(doc);
+               if (reader) ret=ksFromXMLReader(ks,reader);
+               else {
+                       perror("kdb");
+                       return 1;
+               }
+               xmlFreeTextReader (reader);
+       }
+       xmlFreeDoc(doc);
+
+       xmlCleanupParser();
+       return ret;
+}
+
+
+
+
+
+/**
+ * FIXME: not working when fd is stdin
+ * Given a file descriptor (that can be @p stdin) for an XML file, validate
+ * schema, process nodes, convert and save it in the @p ks KeySet.
+ *
+ * @param ks keyset
+ * @param fd Filedeskriptor?? should be FILE*
+ * @ingroup stream
+ */
+int ksFromXML(KeySet *ks, int fd)
+{
+       /* Start of support for old XML library (no xmlReaderForFd()) */
+       char filename[]="/var/tmp/kdbeditXXXXXX";
+       FILE *xmlfile=0;
+
+       /* xmlfile=fopen(tmpnam("/var/tmp/kdbedit"),"rw+"); */
+       xmlfile = tmpfile();
+       while (! feof(xmlfile)) {
+               char buffer[1000];
+               ssize_t readed, writen;
+
+               readed=read(fd,buffer,sizeof(buffer));
+               if (readed<0) {
+                       perror("kdb");
+                       fclose(xmlfile);
+                       remove(filename);
+                       return 1;
+               }
+
+               writen=fwrite(buffer,sizeof(char),readed,xmlfile);
+               if (writen<0) {
+                       perror("kdb");
+                       fclose(xmlfile);
+                       remove(filename);
+                       return 1;
+               }
+       }
+       fclose(xmlfile);
+       return ksFromXMLfile(ks,filename);
+
+       /* This code requires a newer version of XML library, not present in all
+          Linux/BSD/Unix distros. Don't use it yet.
+       // a complete XML document is expected
+       xmlTextReaderPtr reader=0;
+       int ret;
+       reader=xmlReaderForFd(fd,"file:/tmp/imp.xml",0,0);
+       if (reader) {
+               ret=ksFromXMLReader(ks,reader);
+       } else {
+               printf("kdb: Unable to open file descriptor %d for XML reading\n", fd);
+               return 1;
+       }
+       return ret;
+       // end of newer code */
+}
+
diff --git a/src/libelektratools/stream.c b/src/libelektratools/stream.c
new file mode 100644 (file)
index 0000000..c66b114
--- /dev/null
@@ -0,0 +1,761 @@
+/***************************************************************************
+                 stream.c  -  Streaming Methods
+                             -------------------
+    begin                : Sat Nov 23 2007
+    copyright            : (C) 2007 by Markus Raab
+    email                : elektra@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#else
+#error need unistd for getuid and getgid
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include <kdbtools.h>
+#include <kdbbackend.h>
+
+#ifdef ELEKTRA_STATIC
+
+#define ksToStream libelektratools_LTX_ksToStream
+#define ksOutput libelektratools_LTX_ksOutput
+#define ksGenerate libelektratools_LTX_ksGenerate
+#define keyToStream libelektratools_LTX_keyToStream
+#define keyOutput libelektratools_LTX_keyOutput
+#define keyGenerate libelektratools_LTX_keyGenerate
+
+#endif /* ELEKTRA_STATIC */
+
+
+
+/**
+ * @defgroup stream Streaming
+ * @brief Methods to output, generate and toXML Keys and Keysets.
+ *
+ * Here are some functions that are in a separate library because they
+ * depend on non-basic libraries as libxml. Link against kdbtools library if your
+ * program won't be installed in /bin, or is not essential in early boot
+ * stages.
+ *
+ * It is also possible to have a dynamic linkage, see the
+ * sourcecode from kdb-tool or testing framework how to achieve
+ * that.
+ *
+ * Output prints keys line per line, meaned to be read by humans.
+ * - keyOutput()
+ * - ksOutput()
+ *
+ * Generate prints keys and keysets as C-Structs, meaned to be
+ * read by a C-Compiler.
+ * - keyGenerate()
+ * - ksGenerate()
+ *
+ * toXML prints keys and keysets as XML, meaned to be used
+ * as exchange format.
+ * - keyToStream()
+ * - ksToStream()
+ *
+ * To use them:
+ * @code
+#include <kdbtools.h>
+ * @endcode
+ *
+ *
+ */
+
+
+
+
+
+
+/*********************************************
+ *    Textual XML methods                    *
+ *********************************************/
+
+
+/**
+ * Prints an XML representation of the key.
+ *
+ * String generated is of the form:
+ * @verbatim
+       <key name="system/sw/xorg/Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>@endverbatim
+
+
+
+ * @verbatim
+       <key parent="system/sw/xorg/Monitor/Monitor0" basename="Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>@endverbatim
+ *
+ * @param key the key object to work with
+ * @param stream where to write output: a file or stdout
+ * @param options Some #option_t ORed:
+ * - @p option_t::KDB_O_NUMBERS \n
+ *   Do not convert UID and GID into user and group names
+ * - @p option_t::KDB_O_CONDENSED \n
+ *   Less human readable, more condensed output
+ * - @p option_t::KDB_O_FULLNAME \n
+ *   The @p user keys are exported with their full names (including
+ *   user domains)
+ *
+ * @see ksToStream()
+ * @return number of bytes written to output
+ * @ingroup stream
+ */
+ssize_t keyToStream(const Key *key, FILE* stream, option_t options)
+{
+       return keyToStreamBasename(key,stream,0,0,options);
+}
+
+
+
+
+
+/**
+ * Same as keyToStream() but tries to strip @p parentSize bytes from
+ * @p key name if it matches @p parent .
+ *
+ * Taking the example from keyToStream(), if @p parent is
+ * @c "system/sw/xorg", the generated string is of the form:
+ * @verbatim
+       <key basename="Monitor/Monitor0/Name"
+               type="string" uid="root" gid="root" mode="0660">
+
+               <value>Samsung TFT panel</value>
+               <comment>My monitor</comment>
+       </key>@endverbatim
+ *
+ * It usefull to produce more human readable XML output of a key when
+ * it is being represented in a context that defines the parent key name.
+ * For example:
+ *
+ * @verbatim
+       <keyset parent="user/sw">
+               <key basename="kdbedit"..../>
+               <key basename="phototools"..../>
+               <key basename="myapp"..../>
+       </keyset>@endverbatim
+ *
+ * In the bove example, each @p @<key@> entry was generated by a call to 
+ * keyToStreamBasename() having @c "user/sw" as @p parent .
+ * 
+ * This method is used when ksToStream() is called with
+ * KDBOption::KDB_O_HIER option.
+ *
+ * @param key the key object to work with
+ * @param stream the FILE where to send the stream
+ * @param parentSize the maximum size of @p parent that will be used.
+ *        If 0, the entire @p parent will be used.
+ * @param parent the string (or part of it, defined by @p parentSize ) that
+ *        will be used to strip from the key name.
+ * @param options Some #option_t ORed:
+ * - @p option_t::KDB_O_NUMBERS \n
+ *   Do not convert UID and GID into user and group names
+ * - @p option_t::KDB_O_CONDENSED \n
+ *   Less human readable, more condensed output
+ * - @p option_t::KDB_O_FULLNAME \n
+ *   The @p user keys are exported with their full names (including
+ *   user domains)
+ *
+ * @return number of bytes written to output
+ * @ingroup stream
+ */
+ssize_t keyToStreamBasename(const Key *key, FILE *stream, const char *parent,
+               const size_t parentSize, option_t options) {
+       ssize_t written=0;
+       char buffer[MAX_PATH_LENGTH];
+
+       uid_t uid = getuid();
+       gid_t gid = getgid();
+
+       /* Write key name */
+       if (parent) {
+               /* some logic to see if we should print only the relative basename */
+               int found;
+               size_t skip=parentSize ? parentSize : kdbiStrLen(parent)-1;
+
+               found=memcmp(parent,key->key,skip);
+               if (found == 0) {
+                       while (*(key->key+skip) == PATH_SEPARATOR) ++skip;
+
+                       if (*(key->key+skip) != 0) /* we don't want a null basename */
+                               written+=fprintf(stream,"<key basename=\"%s\"",
+                                       key->key+skip);
+               }
+       }
+
+       if (written == 0) { /* no "<key basename=..." was written so far */
+               if (options & KDB_O_FULLNAME) {
+                       keyGetFullName(key,buffer,sizeof(buffer));
+                       written+=fprintf(stream,"<key name=\"%s\"", buffer);
+               } else written+=fprintf(stream,"<key name=\"%s\"", key->key);
+       }
+
+
+       if (options & KDB_O_CONDENSED) written+=fprintf(stream," ");
+       else written+=fprintf(stream,"\n       ");
+
+
+
+       /* Key type
+       TODO: xml schema does not output type
+       if (options & KDB_O_NUMBERS) {
+               written+=fprintf(stream," type=\"%d\"", key->type);
+       } else {
+               buffer[0]=0;
+
+               if (key->type & KEY_TYPE_DIR) written+=fprintf(stream, " isdir=\"yes\"");
+               if (key->type & KEY_TYPE_REMOVE) written+=fprintf(stream, " isremove=\"yes\"");
+               if (key->type & KEY_TYPE_BINARY) written+=fprintf(stream, " isbinary=\"yes\"");
+       }
+       */
+
+       if (keyGetUID (key) != uid) written+=fprintf(stream," uid=\"%d\"", (int)keyGetUID (key));
+       if (keyGetGID (key) != gid) written+=fprintf(stream," gid=\"%d\"", (int)keyGetGID (key));
+
+#if defined(S_IRWXU) && defined(S_IRWXG) && defined(S_IRWXO)
+       written+=fprintf(stream," mode=\"0%o\"",
+               key->mode & (S_IRWXU|S_IRWXG|S_IRWXO));
+#else
+       written+=fprintf(stream," mode=\"0%o\"",
+               key->mode);
+#endif
+
+
+       if (!key->data && !key->comment) { /* no data AND no comment */
+               written+=fprintf(stream,"/>");
+               if (!(options & KDB_O_CONDENSED))
+                       written+=fprintf(stream,"\n\n");
+               
+               return written; /* end of <key/> */
+       } else {
+               if (key->data) {
+                       if ((key->dataSize <= 16) && keyIsString (key) && /*TODO: is this for string?*/
+                                       !strchr(key->data,'\n')) {
+
+                               /* we'll use a "value" attribute instead of a <value> node,
+                                  for readability, so the cut size will be 16, which is
+                                  the maximum size of an IPv4 address */
+
+                               if (options & KDB_O_CONDENSED) written+=fprintf(stream," ");
+                               else written+=fprintf(stream,"\n\t");
+                               
+                               written+=fprintf(stream,"value=\"%s\"",(char *)key->data);
+                               
+                               if (key->comment) written+=fprintf(stream,">\n");
+                               else {
+                                       written+=fprintf(stream,"/>");
+                                       if (!(options & KDB_O_CONDENSED))
+                                               written+=fprintf(stream,"\n");
+                               
+                                       return written;
+                               }
+                       } else { /* value is bigger than 16 bytes: deserves own <value> */
+                               written+=fprintf(stream,">");
+                               if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n     ");
+                               
+                               written+=fprintf(stream,"<value>");
+                               if (keyIsString(key)) { /*TODO: is this for string?*/
+                                       written+=fprintf(stream,"<![CDATA[");
+                                       fflush(stream);
+                                       /* must chop ending \\0 */
+                                       written+=fwrite(key->data,sizeof(char),key->dataSize-1,stream);
+                                       written+=fprintf(stream,"]]>");
+                               } else {
+                                       /* Binary values */
+                                       char *encoded=malloc(3*key->dataSize);
+                                       size_t encodedSize;
+
+                                       written+=fprintf(stream,"\n");
+                                       encodedSize=kdbbEncode(key->data,key->dataSize,encoded);
+                                       fflush(stream);
+                                       written+=fwrite(encoded,sizeof(char),encodedSize,stream);
+                                       free(encoded);
+                                       written+=fprintf(stream,"\n");
+                               }
+                               /* fflush(stream); */
+                               written+=fprintf(stream,"</value>");
+                       }
+               } else { /* we have no data */
+                       if (key->comment) {
+                               written+=fprintf(stream,">");
+                               if (!(options & KDB_O_CONDENSED))
+                                       written+=fprintf(stream,"\n");
+                       } else {
+                               written+=fprintf(stream,"/>");
+                               if (!(options & KDB_O_CONDENSED))
+                                       written+=fprintf(stream,"\n\n");
+                       
+                               return written;
+                       }
+               }
+       }
+
+       if (!(options & KDB_O_CONDENSED)) {
+               written+=fprintf(stream,"\n");
+               if (key->comment) written+=fprintf(stream,"     ");
+       }
+
+       if (key->comment) {
+               written+=fprintf(stream,"<comment><![CDATA[%s]]></comment>", key->comment);
+               if (!(options & KDB_O_CONDENSED))
+                       written+=fprintf(stream,"\n");
+       }
+
+       written+=fprintf(stream,"</key>");
+
+       if (!(options & KDB_O_CONDENSED))
+               written+=fprintf(stream,"\n\n");
+
+       return written;
+}
+
+/**
+ * Writes to @p stream an XML version of the @p ks object.
+ *
+ * String generated is of the form:
+ * @verbatim
+<keyset>
+<key name=...>...</key>
+<key name=...>...</key>
+<key name=...>...</key>
+
+</keyset>
+ * @endverbatim
+ *
+ * or if KDB_O_HIER is used, the form will be:
+ * @verbatim
+<keyset parent="user/smallest/parent/name">
+
+<key basename=...>...</key>
+<key name=...>...</key> <!-- a key thats not under this keyset's parent -->
+<key basename=...>...</key>
+
+</keyset>
+ * @endverbatim
+ *
+ * KDB_O_HEADER will additionally generate a header like:
+ * @verbatim
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of n keys. -->
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd">
+ * @endverbatim
+ *
+ * @param ks the KeySet to serialize
+ * @param stream where to write output: a file or stdout
+ * @param options accepted #option_t ORed:
+ * - @p option_t::KDB_O_NUMBERS \n
+ *   Do not convert UID and GID into user and group names.
+ * - @p option_t::KDB_O_FULLNAME \n
+ *   The @c user keys are exported with their full names (including
+ *   user domains)
+ * - @p option_t::KDB_O_CONDENSED \n
+ *   Less human readable, more condensed output.
+ * - @p option_t::KDB_O_XMLHEADERS \n
+ *   Exclude the correct XML headers in the output. If not used, the
+ *   &lt;?xml?&gt; and schema info inside the &lt;keyset&gt; object will not be generated.
+ * - @p option_t::KDB_O_HIER \n
+ *   Will generate a &lt;keyset&gt; node containing a @c parent attribute, and
+ *   &lt;key&gt; nodes with a @c basename relative to that @c parent. The @c parent
+ *   is calculated by taking the smallest key name in the keyset, so it is a
+ *   good idea to have only related keys on the keyset. Otherwise, a valid
+ *   consistent XML document still will be generated with regular absolute
+ *   @c name attribute for the &lt;key&gt; nodes, due to a
+ *   clever keyToStreamBasename() implementation.
+ *
+ * @see keyToStream()
+ * @see commandList() for usage example
+ * @return number of bytes written to output, or -1 if some error occurs
+ * @ingroup stream
+ * @param ks The keyset to output
+ * @param stream the file pointer where to send the stream
+ * @param options see above text
+ */
+ssize_t ksToStream(const KeySet *ks, FILE* stream, option_t options) {
+       size_t written=0;
+       Key *key=0;
+       char *codeset = "UTF-8";
+       KeySet *cks = ksDup (ks);
+
+       if (ksNeedSort(cks)) ksSort (cks);
+       ksRewind (cks);
+
+       if (options & KDB_O_HEADER) {
+               written+=fprintf(stream,"<?xml version=\"1.0\" encoding=\"%s\"?>",
+                       codeset);
+               if (~options & KDB_O_CONDENSED)
+                       written+=fprintf(stream,
+                                       "\n<!-- Generated by Elektra API. Total of %d keys. -->\n",(int)cks->size);
+               if (~options & KDB_O_CONDENSED)
+                       written+=fprintf(stream,"<keyset xmlns=\"http://www.libelektra.org\"\n"
+                                       "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+                                       "\txsi:schemaLocation=\"http://www.libelektra.org elektra.xsd\"\n");
+               else
+                       written+=fprintf(stream,"<keyset xmlns=\"http://www.libelektra.org\""
+                                       " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+                                       " xsi:schemaLocation=\"http://www.libelektra.org elektra.xsd\"");
+       } else written+=fprintf(stream,"<keyset");
+
+       if (options & KDB_O_HIER) {
+               char commonParent[MAX_PATH_LENGTH];
+
+               ksGetCommonParentName(cks,commonParent,sizeof(commonParent));
+       
+               if (commonParent[0]) {
+                       written+=fprintf(stream,"        parent=\"%s\">\n",
+                               commonParent);
+                       ksRewind (cks);
+                       while ((key=ksNext (cks)) != 0)
+                               written+=keyToStreamBasename(key,stream,commonParent,0,options);
+               } else {
+                       written+=fprintf(stream,">\n");
+                       ksRewind (cks);
+                       while ((key=ksNext (cks)) != 0)
+                               written+=keyToStream(key,stream,options);
+               }
+       } else { /* No KDB_O_HIER*/
+               written+=fprintf(stream,">\n");
+               ksRewind (cks);
+               while ((key=ksNext (cks)) != 0)
+                       written+=keyToStream(key,stream,options);
+       }
+       
+       written+=fprintf(stream,"</keyset>\n");
+       ksDel (cks);
+       return written;
+}
+
+/**
+ * Output every information of a single key depending on options.
+ *
+ * The format is not very strict and only intend to be read
+ * by human eyes for debugging purposes. Don't rely on the
+ * format in your applications.
+ *
+ * @param k the key object to work with
+ * @param stream the file pointer where to send the stream
+ * @param options see text above
+ * @see ksOutput()
+ * @return 1 on success
+ * @return -1 on allocation errors
+ * @ingroup stream
+ */
+int keyOutput (const Key * k, FILE *stream, option_t options)
+{
+       time_t t;
+       size_t s;
+       char * tmc;
+       char * str;
+
+       size_t c;
+       char * com;
+
+       size_t n;
+       char * nam;
+
+       n = keyGetNameSize (k);
+       if (n>1)
+       {
+               nam = (char*) malloc (n);
+               if (nam == NULL) return -1;
+               keyGetName (k, nam, n);
+
+               fprintf(stream,"Name[%d]: %s : ", (int)n, nam);
+
+               free (nam);
+       }
+
+       s = keyGetValueSize (k);
+       if (options & KEY_VALUE && s>1)
+       {
+               str = (char*) malloc (s);
+               if (str == NULL) return -1;
+               if (keyIsBinary(k))
+               {
+                       /*
+                       char * bin;
+                       bin = (char*) malloc (s*3+1);
+                       keyGetBinary(k, str, s);
+                       kdbbEncode (str, s, bin);
+                       free (bin);
+                       */
+                       keyGetBinary (k, str, s);
+                       fprintf(stream,"Binary[%d]: %s : ", (int)s, str);
+               } else {
+                       keyGetString (k, str, s);
+                       fprintf(stream,"String[%d]: %s : ", (int)s, str);
+               }
+
+               free (str);
+       }
+
+       c = keyGetCommentSize (k);
+       if (options & KEY_COMMENT && c>1)
+       {
+               com = (char*) malloc (c);
+               if (com == NULL) return -1;
+               keyGetComment (k, com, c);
+
+               fprintf(stream,"Comment[%d]: %s : ", (int)c, com);
+
+               free (com);
+       }
+
+
+       if (options & KDB_O_SHOWMETA) fprintf(stream," : ");
+       if (options & KEY_TYPE) fprintf(stream,"Type: %d : ", keyGetType (k));
+       if (options & KEY_UID) fprintf(stream,"UID: %d : ", (int)keyGetUID (k));
+       if (options & KEY_GID) fprintf(stream,"GID: %d : ", (int)keyGetGID (k));
+       if (options & KEY_MODE) fprintf(stream,"Mode: %o : ", (int)keyGetMode (k));
+
+       if (options & KEY_ATIME)
+       {
+               t=keyGetATime(k);
+               tmc = ctime (& t);
+               tmc[24] = '\0';
+               fprintf(stream,"ATime: %s : ", tmc);
+       }
+
+       if (options & KEY_MTIME)
+       {
+               t=keyGetMTime(k);
+               tmc = ctime (& t);
+               tmc[24] = '\0';
+               fprintf(stream,"MTime: %s : ", tmc);
+       }
+
+       if (options & KEY_CTIME)
+       {
+               t=keyGetCTime(k);
+               tmc = ctime (& t);
+               tmc[24] = '\0';
+               fprintf(stream,"CTime: %s : ", tmc);
+       }
+
+       if (options & KDB_O_SHOWFLAGS)
+       {
+               if (!(options & KDB_O_SHOWMETA)) fprintf(stream, " ");
+               fprintf (stream,"Flags: ");
+               if (keyIsBinary(k)) fprintf(stream,"b");
+               if (keyIsString(k)) fprintf(stream,"s");
+               if (keyIsDir(k)) fprintf(stream,"d");
+               if (keyIsInactive(k)) fprintf(stream,"i");
+               if (keyNeedRemove(k)) fprintf(stream,"r");
+               if (keyNeedSync(k)) fprintf(stream,"s");
+       }
+
+       fprintf(stream,"\n");
+       return 1;
+}
+
+
+/**
+ * Output all information of a keyset.
+ *
+ * The format is not very strict and only intend to be read
+ * by human eyes for debugging purposes. Don't rely on the
+ * format in your applications.
+ *
+ * Keys are printed line per line with keyOutput().
+ *
+ * The same options as keyOutput() are taken and passed
+ * to them.
+ *
+ * Additional KDB_O_HEADER will print the number of keys
+ * as first line.
+ *
+ * @param ks the keyset to work with
+ * @param stream the file pointer where to send the stream
+ * @param options
+ * @see keyOutput()
+ * @return 1 on success
+ * @return -1 on allocation errors
+ * @ingroup stream
+ */
+int ksOutput(const KeySet *ks, FILE *stream, option_t options)
+{
+       Key     *key;
+       KeySet *cks = ksDup (ks);
+       size_t size = 0;
+
+       if (ksNeedSort(cks)) ksSort (cks);
+       ksRewind (cks);
+
+       if (KDB_O_HEADER & options) {
+               fprintf(stream,"Output keyset of size %d\n", (int)ksGetSize(cks)); 
+       }
+       while ( (key = ksNext(cks)) != NULL)
+       {
+               if (options & KDB_O_SHOWINDICES) fprintf(stream, "[%d] ", (int)size);
+               keyOutput (key,stream,options);
+               size ++;
+       }
+
+       ksDel (cks);
+       return 1;
+}
+
+/**
+ * Generate a C-Style key and stream it.
+ *
+ * This keyset can be used to include as c-code for
+ * applikations using elektra.
+ *
+ * @param key the key object to work with
+ * @param stream the file pointer where to send the stream
+ * @param options KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO
+ * @return 1 on success
+ * @ingroup stream
+ */
+int keyGenerate(const Key * key, FILE *stream, option_t options)
+{
+       size_t s;
+       char * str;
+
+       size_t c;
+       char * com;
+
+       size_t n;
+       char * nam;
+
+       n = keyGetNameSize (key);
+       if (n>1)
+       {
+               nam = (char*) malloc (n);
+               if (nam == NULL) return -1;
+               keyGetName (key, nam, n);
+               fprintf(stream,"\n\tkeyNew (\"%s\"", nam);
+               free (nam);
+       }
+
+       if (keyIsDir(key)) fprintf(stream,"\n\t\t, KEY_DIR");
+
+       s = keyGetValueSize (key);
+       if (s>1)
+       {
+               str = (char*) malloc (s);
+               if (str == NULL) return -1;
+               if (keyIsBinary(key)) keyGetBinary(key, str, s);
+               else keyGetString (key, str, s);
+               fprintf(stream,"\n\t\t, KEY_VALUE, \"%s\"", str);
+               free (str);
+       }
+
+       c = keyGetCommentSize (key);
+       if (c>1)
+       {
+               com = (char*) malloc (c);
+               if (com == NULL) return -1;
+               keyGetComment (key, com, c);
+               fprintf(stream,"\n\t\t, KEY_COMMENT, \"%s\"", com);
+               free (com);
+       }
+
+       if (keyGetType(key) == KEY_TYPE_BINARY) fprintf(stream,"\n\t\t, KEY_TYPE, KEY_TYPE_BINARY");
+       else if (keyGetType(key) == KEY_TYPE_STRING) fprintf(stream,"\n\t\t, KEY_TYPE, KEY_TYPE_STRING");
+       else fprintf(stream,"\n\t\t, KEY_TYPE, %d", keyGetType(key));
+
+       // if (keyIsInactive(key)) fprintf(stream,"inactive");
+
+       // if (keyIsSystem(key)) fprintf(stream,"system");
+       // if (keyIsUser(key)) fprintf(stream,"user");
+
+       if (keyNeedRemove(key)) fprintf(stream, "\n\t\t, KEY_REMOVE");
+       if (keyNeedStat(key)) fprintf(stream, "\n\t\t, KEY_STAT");
+
+       if (! (keyGetMode(key) == 0664 || (keyGetMode(key) == 0775 && keyIsDir(key))))
+       {
+               fprintf(stream,"\n\t\t, KEY_MODE, 0%3o", keyGetMode(key));
+       }
+
+       fprintf(stream,"\n\t, KEY_END)");
+
+       if (options == 0) return 1; /* dummy to make icc happy */
+       return 1;
+}
+
+
+/**
+ * Generate a C-Style keyset and stream it.
+ *
+ * This keyset can be used to include as c-code for
+ * applikations using elektra.
+ *
+ * The options takes the same options as kdbGet()
+ * and kdbSet().
+ *
+ * @param ks the keyset to work with
+ * @param stream the file pointer where to send the stream
+ * @param options which keys not to output
+ * @return 1 on success
+ * @ingroup stream
+ */
+int ksGenerate (const KeySet *ks, FILE *stream, option_t options)
+{
+       Key     *key;
+       size_t  s = 0;
+       KeySet *cks = ksDup (ks);
+
+       if (ksNeedSort(cks)) ksSort (cks);
+       ksRewind (cks);
+
+       fprintf(stream,"ksNew( %d ,", (int)ksGetSize(cks)+10);
+       while ((key=ksNext(cks)) != 0)
+       {
+               if (options & KDB_O_NODIR) if (key && keyIsDir (key)) continue;
+               if (options & KDB_O_DIRONLY) if (key && !keyIsDir (key)) continue;
+               if (options & KDB_O_INACTIVE) if (key && keyIsInactive (key)) continue;
+               if (options & KDB_O_STATONLY)
+               {
+                       keySetRaw (key, "", 0);
+                       keySetComment (key, "");
+               }
+
+               s++;
+
+               keyGenerate(key, stream, options);
+               fprintf(stream,",");
+       }
+       fprintf(stream,"KS_END);\n"); 
+
+       ksDel (cks);
+       return 1;
+}
+
diff --git a/src/libhelper/Makefile.am b/src/libhelper/Makefile.am
new file mode 100644 (file)
index 0000000..2fcca1d
--- /dev/null
@@ -0,0 +1,15 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+sources = internal.c helper.c helper.h ../include/kdb.h ../include/kdbbackend.h
+
+noinst_LIBRARIES = libhelper-static.a
+libhelper_static_a_SOURCES = $(sources)
+libhelper_static_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+
+noinst_LTLIBRARIES = libhelper-dynamic.la
+libhelper_dynamic_la_SOURCES = $(sources)
+libhelper_dynamic_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
diff --git a/src/libhelper/helper.c b/src/libhelper/helper.c
new file mode 100755 (executable)
index 0000000..b97dc4b
--- /dev/null
@@ -0,0 +1,786 @@
+/***************************************************************************
+            helper.c  -  Helpers for backends
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include "helper.h"
+
+/**
+ * @defgroup backendhelper KDB Backends :: Backend Helper for Elektra
+ * @brief Backend helper Methods for Elektra and Backends.
+ *
+ * To use them:
+ * @code
+ * #include <kdbbackend.h>
+ * @endcode
+ *
+ * These backend helper methods provide
+ * functionality commonly used by backends to make backend
+ * development easier and to provide the same behaviour
+ * between backends.
+ *
+ */
+
+
+/**
+ * Locks file for exclusive write mode.
+ *
+ * This function will block until all reader
+ * and writer have left the file.
+ *
+ * @param f is a valid filedescriptor
+ * @return 0 on success
+ * @return -1 on failure
+ * @ingroup backendhelper
+ * @err sets KDB_ERR_NOLOCK when locking failed
+ */
+int kdbbWriteLock (FILE *f)
+{
+#ifdef HAVE_FCNTL_H
+       int fd = fileno(f);
+       struct flock l;
+       int ret=0;
+       l.l_type = F_WRLCK; /*Do exclusive Lock*/
+       l.l_start= 0;   /*Start at begin*/
+       l.l_whence = SEEK_SET;
+       l.l_len = 0;    /*Do it with whole file*/
+       ret = fcntl (fd, F_SETLKW, &l);
+       return ret;
+#else
+       return 0;
+#endif
+}
+
+/**
+ * Locks file for read mode.
+ *
+ * Other processes and threads are allowed to read the
+ * file too simultaneous.
+ *
+ * @param f is a valid filedescriptor
+ * @return 0 on success
+ * @return -1 on failure
+ * @ingroup backendhelper
+ * @err sets KDB_ERR_NOLOCK when locking failed
+ */
+int kdbbReadLock (FILE *f)
+{
+#ifdef HAVE_FCNTL_H
+       int ret=0;
+       int fd = fileno(f);
+       struct flock l;
+       l.l_type = F_WRLCK; /*Do exclusive Lock*/
+       l.l_start= 0;   /*Start at begin*/
+       l.l_whence = SEEK_SET;
+       l.l_len = 0;    /*Do it with whole file*/
+       ret = fcntl (fd, F_SETLKW, &l);
+       return ret;
+#else
+       return 0;
+#endif
+}
+
+
+/**
+ * Unlocks file.
+ *
+ * @param f is a valid filedescriptor
+ * @return 0 on success
+ * @return -1 on failure
+ * @ingroup backendhelper
+ * @err sets KDB_ERR_NOLOCK when locking failed
+ */
+int kdbbUnlock (FILE *f)
+{
+#ifdef HAVE_FCNTL_H
+       int ret=0;
+       int fd = fileno(f);
+       struct flock l;
+       l.l_type = F_UNLCK; /*Give Lock away*/
+       l.l_start= 0;   /*Start at begin*/
+       l.l_whence = SEEK_SET;
+       l.l_len = 0;    /*Do it with whole file*/
+       ret = fcntl (fd, F_SETLKW, &l);
+       return ret;
+#else
+       return 0;
+#endif
+}
+
+
+
+/**
+ * Encodes a buffer of data onto hexadecimal ASCII.
+ *
+ * The resulting data is made up of pairs of ASCII hex-digits,
+ * space- and newline-separated. This is the counterpart of
+ * kdbbDecode().
+ *
+ * The @c returned must allocated prior you call this function and won't
+ * be bigger than 3 times the size of the source @c kdbbDecoded + 1 byte.
+ *
+ *
+ * @param kdbbDecoded the source buffer.
+ * @param size the size of the source buffer in bytes.
+ * @param returned the preallocated destination for the ASCII-kdbbEncoded data.
+ * @return the amount of bytes used in the resulting kdbbEncoded buffer.
+ * @see kdbbDecode()
+ * @ingroup backendhelper
+ */
+ssize_t kdbbEncode(void *kdbbDecoded, size_t size, char *returned)
+{
+       char *readCursor=kdbbDecoded;
+       char *writeCursor=returned;
+       int blockStep=4; /* 4 bytes per block */
+       int lineStep=8*blockStep; /* 8 blocks per line */
+       int currentInBlock=0;
+       int currentInLine=0;
+
+       if ( size == 0 )
+               return 0;
+       
+       while ((readCursor-(char *)kdbbDecoded)<size) {
+               sprintf(writeCursor,"%02x",*(unsigned char *)readCursor);
+               readCursor++;
+               writeCursor+=2;
+               currentInBlock++;
+               currentInLine++;
+               if (currentInLine==lineStep) {
+                       *writeCursor='\n'; writeCursor++;
+                       currentInLine=0;
+                       currentInBlock=0;
+               }
+               if (currentInBlock==blockStep) {
+                       *writeCursor=' '; writeCursor++;
+                       currentInBlock=0;
+               }
+       }
+       *writeCursor='\n';
+       *++writeCursor=0;
+       return writeCursor-returned;
+}
+
+
+/**
+ * UnkdbbEncodes a buffer of ASCII hexadecimal values into a byte stream.
+ *
+ * The allowed format for the hexadecimal values is just
+ * a stream of pairs of plain hex-digits, all together or
+ * space-separated.
+ * 
+ * The @c returned data won't be bigger than half the size of the
+ * source @c kdbbEncoded data.
+ *
+ * @param kdbbEncoded the source of ASCII hexadecimal digits.
+ * @param returned preallocated destination for the kdbbDecoded data.
+ * @return the amount of bytes kdbbDecoded
+ * @return -1 on failure
+ * @see kdbbEncode()
+ * @ingroup backendhelper
+ */
+ssize_t kdbbDecode(char *kdbbEncoded,void *returned)
+{
+       char byteInHexa[5]="0x";
+       char *readCursor=kdbbEncoded;
+       char *writeCursor=returned;
+
+       if (!kdbbEncoded) {
+               if (returned) *(char *)returned=0;
+               return 0;
+       }
+
+       byteInHexa[4]=0;
+       while (*readCursor) {
+               if (isspace((int)*readCursor)) 
+               {
+               readCursor++;
+               continue;
+               }
+               if (isxdigit((int)*readCursor)) {
+                       long int converted;
+                       byteInHexa[2]=readCursor[0];
+                       byteInHexa[3]=readCursor[1];
+                       converted=strtol(byteInHexa,0,16); /* convert from hexa to a byte */
+                       *writeCursor=(unsigned char)converted;
+
+                       readCursor+=2;
+                       writeCursor++;
+               } else {
+                       /* This is suposed to be a hex-digit stream. But is not, so return. */
+                       /*errno=KDB_ERR_TYPEMISMATCH;*/
+                       return -1;
+               }
+       }
+       return (long int)writeCursor-(long int)returned;
+}
+
+/**
+ * Checks if UTF-8 conversion is needed in current context.
+ * if nl_langinfo() is not available, no conversion is ever needed.
+ * If iconv usage is disabled there is no need to check if we need to convert.
+ * Furthermore, some systems have nl_langinfo(), but lacks ability to get
+ * CODESET through it.
+ * Look at the comments by the kdbbUTF8Engine() function for more information.
+ *
+ * @return 0 if not needed
+ * @return anything else if needed
+ * @ingroup backendhelper
+ */
+int kdbbNeedsUTF8Conversion()
+{
+#if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV) && defined(CODESET)
+       return strcmp(nl_langinfo(CODESET),"UTF-8");
+#else
+       return 0;
+#endif
+}
+
+
+/**
+ * Converts string to (@p direction = @c UTF8_TO) and from
+ * (@p direction = @c UTF8_FROM) UTF-8.
+ * 
+ * Since Elektra provides portability for key names and string values between
+ * different codesets, you should use this helper in your backend to convert
+ * to and from universal UTF-8 strings, when storing key names, values and
+ * comments.
+ *
+ * Broken locales in applications can cause problems too. Make sure to load
+ * the environment locales in your application using
+ * @code
+setlocale (LC_ALL, "");
+ * @endcode
+ *
+ * Otherwise kdbbUTF8Engine will quit with -1 leading that backends return
+ * with error when non-ascii characters appear. Binary values are not effected.
+ *
+ * If iconv() or nl_langinfo() is not available on your system, or if iconv()
+ * usage is disabled (--disable-iconv on build time) simply return 0
+ * immediately.
+ *
+ * @param direction must be @c UTF8_TO (convert from current non-UTF-8 to
+ *     UTF-8) or @c UTF8_FROM (convert from UTF-8 to current non-UTF-8)
+ * @param string before the call: the string to be converted; after the call:
+ *     reallocated to carry the converted string
+ * @param inputOutputByteSize before the call: the size of the string including
+ *     leading NULL; after the call: the size of the converted string including
+ *     leading NULL
+ * @return 0 on success
+ * @return -1 on failure
+ * @ingroup backendhelper
+ *
+ */
+int kdbbUTF8Engine(int direction, char **string, size_t *inputOutputByteSize)
+{
+/* Current solution is not very complete.
+ * Iconv might well be available when a usable nl_langinfo is not.
+ * In this case we it should be possible to determine charset through other means
+ * See http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate for more info on a possible solution */
+#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
+       char *currentCharset=0;
+       char *converted=0;
+       char *readCursor, *writeCursor;
+       size_t bufferSize;
+       iconv_t converter;
+       
+       if (kdbbNeedsUTF8Conversion() && *inputOutputByteSize) currentCharset=nl_langinfo(CODESET);
+       else return 0;
+
+       if (direction==UTF8_TO) converter=iconv_open("UTF-8",currentCharset);
+       else converter=iconv_open(currentCharset,"UTF-8");
+
+       if (converter == (iconv_t)(-1)) return -1;
+
+       /* work with worst case, when all chars are wide */
+       bufferSize=*inputOutputByteSize * 4;
+       converted=malloc(bufferSize);
+       if (!converted) return -1;
+
+       readCursor=*string;
+       writeCursor=converted;
+       /* On some systems and with libiconv, arg1 is const char **. 
+        * ICONV_CONST is defined by configure if the system needs this */
+       if (iconv(converter,
+                       (ICONV_CONST char **)&readCursor,inputOutputByteSize,
+                       &writeCursor,&bufferSize) == (size_t)(-1)) {
+               free(converted);
+               iconv_close(converter);
+               return -1;
+       }
+
+       /* calculate the UTF-8 string byte size, that will be returned */
+       *inputOutputByteSize=writeCursor-converted;
+       /* store the current kdbbDecoded string for future free */
+       readCursor=*string;
+       /* allocate an optimal size area to store the converted string */
+       *string=malloc(*inputOutputByteSize);
+       /* copy all that matters for returning */
+       memcpy(*string,converted,*inputOutputByteSize);
+       /* release memory used by passed string */
+       free(readCursor);
+       /* release buffer memory */
+       free(converted);
+       /* release the conversor engine */
+       iconv_close(converter);
+#endif
+       return 0;
+}
+
+
+
+/**
+ * Char encoding.
+ *
+ * Encode '/', '\', '%', '+', ' ' char following
+ * RFC 2396 or copy char untouched if different.
+ *
+ * @param c Char to kdbbEncode
+ * @param buffer string wich will contain kdbbEncoded char
+ * @param bufSize Size of the buffer
+ * @return: Size of the kdbbEncoded string if success or -1
+ * if error  * (then buffer is untouched)
+ * @ingroup backendhelper
+ * @see kdbiDecodeChar
+ *
+ * NOTE: No '\\0' is added at the end of buffer.
+ *
+ */
+int kdbbEncodeChar(char c, char *buffer, size_t bufSize)
+{
+       switch(c) {
+               case '%':
+                       if ( bufSize >= (3*sizeof(char)) ) {
+                               memcpy(buffer, "%25", sizeof("%25"));
+                               return (3*sizeof(char));
+                       }
+                       return -1;
+
+               case '+':
+                       if ( bufSize >= (3*sizeof(char)) ) {
+                               memcpy(buffer, "%2B", sizeof("%2B"));
+                               return (3*sizeof(char));
+                       }
+                       return -1;
+
+               case ' ':
+                       if ( bufSize >= 1*sizeof(char) ) {
+                               *(buffer) = '+';
+                               return (1*sizeof(char));
+                       }
+                       return -1;
+
+               case '/':
+                       if ( bufSize >= (3*sizeof(char)) ) {
+                               memcpy(buffer, "%2F", sizeof("%2F"));
+                               return (3*sizeof(char));
+                       }
+                       return -1;
+
+               case '\\':
+                       if ( bufSize >= (3*sizeof(char)) ) {
+                               memcpy(buffer, "%5C", sizeof("%5C"));
+                               return (3*sizeof(char));
+                       }
+                       return -1;
+
+               default:
+                       if ( bufSize >= (1*sizeof(char)) ) {
+                               *(buffer++) = c;
+                               return (1*sizeof(char));
+                       }
+                       return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Char decoding.
+ *
+ * Decode one char from %25, %2B, %2F, %2C following
+ * RFC 2396 or copy char untouched if different.
+ *
+ * @param from String containing sequence to decode
+ * @param into Decoded char
+ * @return: Positive size of byte read from "from" for decoding
+ * the sequence if sucess or -1 if error (into untouched)
+ * @ingroup backendhelper
+ * @see kdbbEncodeChar
+ *
+ * NOTE: No '\\0' is added at the end of buffer.
+ *
+ */
+int kdbbDecodeChar(const char *from, char *into)
+{
+       switch(*from) {
+               case '%':
+                       if ( strlen(from) >= (3*sizeof(char)) ) {
+                               switch(*(from+2)) {
+                                       case '5':       *into = '%';    break;
+                                       case 'B':       *into = '+';    break;
+                                       case 'F':       *into = '/';    break;
+                                       case 'C':       *into = '\\';   break;
+
+                                       default:
+                                               return -1;
+                               }
+
+                               return (3*sizeof(char));
+                       }
+                       return -1;
+
+               case '+':
+                       *into = ' ';
+                       return (1*sizeof(char));
+
+               default:
+                       *into = *from;
+                       return (1*sizeof(char));
+       }
+
+       return 0;
+}
+
+/**
+ * Translate a relative file name to a key name
+ * applying decoding.
+ *
+ * @param string Filename
+ * @param buffer decoded keyName
+ * @param bufSize Size of buffer
+ * @return 0 on success
+ * @return -1 on failure
+ * @ingroup backendhelper
+ * @see kdbbKeyNameToRelativeFilename
+ *
+ */
+int kdbbFilenameToKeyName(const char *string, char *buffer, int bufSize)
+{
+       char decoded;
+       int j;
+
+       while (*string != '\0')
+       {
+               if (bufSize <= sizeof(char))
+               {
+                       /*errno = KDB_ERR_TOOLONG;*/
+                       return -1;
+               }
+
+               if ( *string == PATH_SEPARATOR ) {
+                       /* Translate PATH_SEPARATOR into KEY_SEPARATOR */
+                       *(buffer++) = KEY_SEPARATOR;
+                       bufSize -= sizeof(char);
+                       string++;
+               } else {
+                       /* Decode char */
+                       if ( (j = kdbbDecodeChar(string, &decoded)) != -1 ) {
+                               string += j;
+                               *(buffer++) = decoded;
+                               bufSize -= sizeof(char);
+                       } else {
+                               *(buffer) = '\0';
+                               /*errno = KDB_ERR_CONVERT;*/
+                               return -1;
+                       }
+               }
+       }
+
+       *buffer = '\0';
+
+       return 0;
+}
+
+/**Calculates the keyname out of a relative filename.
+ *
+ * @param handle The kdb handle to work with
+ * @param forFilename needs to be the a null terminated string containing the relative filename
+ * @param parentKey is the key above the key which will be returned
+ * @param returned The proper keyname and owner will be stored in returned. A valid key must be passed.
+ * @return number of bytes written to the buffer, or 0 on error
+ * @ingroup backendhelper
+ * @return length of returned string on success
+ * @return -1 on failure
+ * @see kdbbKeyNameToRelativeFilename()
+ */
+ssize_t kdbbGetFullKeyName (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned)
+{
+       size_t size=0;
+       char *transformedName=0;
+       char *name;
+
+       /* Next 2 ifs are required to transform filename from UTF-8 */
+       transformedName = malloc(size=kdbiStrLen(forFilename));
+       strcpy(transformedName,forFilename);
+
+       if (kdbbUTF8Engine(UTF8_FROM,&transformedName,&size)) {
+               free(transformedName);
+               /*errno = KDB_ERR_CONVERT;*/
+               return -1; 
+       }
+
+       /* Translate from filename -> keyname and concat it into name */
+       name = (char *) malloc(size*3 + keyGetNameSize(parentKey));
+       strcpy (name, keyName(parentKey));
+       name[keyGetNameSize(parentKey)-1] = '/';
+       kdbbFilenameToKeyName(transformedName, name+keyGetNameSize(parentKey), size*3);
+
+       /* Now set the name and owner */
+       keySetName (returned, name);
+       keySetOwner(returned, keyOwner(parentKey));
+
+       free(transformedName);
+       free(name);
+       return 0;
+}
+
+/**
+ * Translate a key name to a relative file name
+ * applying encoding.
+ *
+ * @param string Keyname
+ * @param buffer kdbbEncoded filename
+ * @param bufSize Size of buffer
+ * @return Number of byte written in buffer on success,
+ * @return -1 on failure
+ * @ingroup backendhelper
+ * @see kdbbKeyNameToRelativeFilename
+ *
+ **/
+int kdbbKeyNameToRelativeFilename(const char *string, char *buffer, size_t bufSize)
+{
+       size_t  written;
+       int     j;
+
+       written = 0;
+       while (*string != '\0')
+       {
+               if (bufSize <= sizeof(char))
+               {
+                       /*errno = KDB_ERR_TOOLONG;*/
+                       return -1;
+               }
+
+               if ( *string == ESCAPE_CHAR && *(string+1) == PATH_SEPARATOR ) {
+                       /* Key delimiter escaped, kdbbEncode these two (escape + delim) */
+                       if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
+                               bufSize -= j*sizeof(char);
+                               buffer += j;
+                               written += j*sizeof(char);
+                       } else {
+                               /*errno = KDB_ERR_CONVERT;*/
+                               return -1;
+                       }
+
+                       if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
+                               bufSize -= j*sizeof(char);
+                               written += j*sizeof(char);
+                               buffer += j;
+                       } else {
+                               /*errno = KDB_ERR_CONVERT;*/
+                               return -1;
+                       }
+
+               } else if ( *string == PATH_SEPARATOR ) {
+                       /* Replace unescaped KEY_DELIM to PATH_SEPARATOR */
+                       *(buffer++) = PATH_SEPARATOR;
+                       bufSize -= sizeof(char);
+                       written += sizeof(char);
+                       string++;
+
+               } else {
+                       /* Encode ... */
+                       if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
+                               bufSize -= j*sizeof(char);
+                               written += j*sizeof(char);
+                               buffer += j;
+                       } else {
+                               /*errno = KDB_ERR_CONVERT;*/
+                               return -1;
+                       }
+               }
+       }
+       *buffer = '\0';
+       written++;
+
+       return written;
+}
+
+
+
+/**
+ * This is a helper to kdbGetFullFilename()
+ *
+ * @param key has the relevant name for the relative filename
+ * @param relativeFilename the buffer to return the calculated filename
+ * @param maxSize maximum number of bytes that fit the buffer
+ * @see kdbGetFullFilename()
+ * @return number of bytes written to the buffer
+ * @return -1 on failure
+ * @ingroup backendhelper
+ */
+ssize_t kdbbKeyCalcRelativeFilename(const Key *key,char *relativeFilename,size_t maxSize)
+{
+       if (kdbbNeedsUTF8Conversion()) {
+               char *converted;
+               size_t size;
+
+               if (!(size=keyGetNameSize(key))) return -1;
+
+               converted = (char *) malloc(MAX_PATH_LENGTH);
+               size = kdbbKeyNameToRelativeFilename(keyName(key), converted,
+                       MAX_PATH_LENGTH);
+
+/*             memcpy(converted,relativeFilename,convertedSize); */
+
+               if (kdbbUTF8Engine(UTF8_TO,&converted,&size)) {
+                       free(converted);
+                       /*errno = KDB_ERR_CONVERT;*/
+                       return -1;
+               }
+
+               if (size>maxSize) {
+                       free(converted);
+                       /*errno=KDB_ERR_TOOLONG;*/
+                       return -1;
+               }
+
+               memcpy(relativeFilename,converted,size);
+               free(converted);
+
+               return size;
+       } else {
+               return kdbbKeyNameToRelativeFilename(keyName(key), relativeFilename, maxSize);
+       }
+
+       return -1;
+}
+
+
+
+
+/**
+ * Calculate the real file name for a key.
+ *
+ * system/ keys will get the prefix KDB_DB_SYSTEM
+ *
+ * For the user/ keys the algorithm works as follow:
+ * 1.) When the override environment KDB_HOME exists the configuration will be searched below KDB_HOME/KDB_DB_USER
+ * 2.) When the owner of the key exists in the elektra user database steps a.) and b.) will be tested:
+ *    a.) The specific value for configuration storage of the user below system/users/\<owner\>/kdb
+ *    b.) The home variable in system/users/\<owner\>/home will be merged together with KDB_DB_USER
+ * 3.) When the environment HOME exists the configuration will be searched below HOME/KDB_DB_USER
+ * 4.) Otherwise the KDB_DB_HOME/\<owner\>/KDB_DB_USER will be used
+ *
+ * @param forKey the key object to work with
+ * @param handle the kdb handle to work with
+ * @param returned the buffer to return the calculated filename
+ * @param maxSize maximum number of bytes that fit the buffer
+ * @see kdbCalcRelativeFilename()
+ * @return number of bytes written to the buffer, or 0 on error
+ * @ingroup backendhelper
+ * @return length of returned string on success
+ * @return -1 on failure
+ */
+ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize) {
+       ssize_t length=0;
+       char * home;
+       char buffer [MAX_PATH_LENGTH] = KDB_KEY_USERS;
+       ssize_t rc;
+
+       switch (keyGetNamespace(forKey)) {
+               case KEY_NS_MEMORY: {
+                       /* Prepare to use the 'system/ *' database */
+                       strncpy(returned,KDB_DB_MEMORY,maxSize);
+                       length=strlen(returned);
+                       break;
+               }
+               case KEY_NS_SYSTEM: {
+                       /* Prepare to use the 'system/ *' database */
+                       strncpy(returned,KDB_DB_SYSTEM,maxSize);
+                       length=strlen(returned);
+                       break;
+               }
+               /* If we lack a usable concept of users we simply let the default handle it
+                * and hence disable the entire user/ hiarchy. */
+               case KEY_NS_USER: {
+#ifdef TUNNING_ELEKTRA_0_7
+                       strncpy(returned,KDB_DB_USER,maxSize);
+                       length=strlen(returned);
+                       break;
+#endif
+                       if ((home = getenv("KDB_DIR")) != 0)
+                       {
+                               length=snprintf(returned,maxSize,"%s",home);
+                               break;
+                       }
+
+                       if ((home = getenv("KDB_HOME")) != 0)
+                       {
+                               length=snprintf(returned,maxSize,"%s/%s",home,KDB_DB_USER);
+                               break;
+                       }
+
+                       strcat (buffer, "/");
+                       strcat (buffer, keyOwner(forKey));
+                       strcat (buffer, "/kdb");
+                       length=kdbGetString (handle, buffer, returned, maxSize)-1;
+                       if (length > 0)
+                       {
+                               break;
+                       }
+
+                       strcpy (buffer, KDB_KEY_USERS);
+                       strcat (buffer, "/");
+                       strcat (buffer, keyOwner(forKey));
+                       strcat (buffer, "/home");
+                       length=kdbGetString (handle, buffer, returned, maxSize)-1;
+                       if (length > 0)
+                       {
+                               strcpy (buffer, returned);
+                               length=snprintf(returned,maxSize,"%s/%s",buffer, KDB_DB_USER);
+                               break;
+                       }
+
+                       if ((home = getenv("HOME")) != 0)
+                       {
+                               length=snprintf(returned,maxSize,"%s/%s",home,KDB_DB_USER);
+                               break;
+                       }
+
+                       length=snprintf(returned,maxSize,"%s/%s/%s",KDB_DB_HOME, keyOwner(forKey), KDB_DB_USER);
+                       break;
+               }
+
+               default: {
+                       /*errno=KDB_ERR_INVALIDKEY;*/
+                       return -1;
+               }
+       }
+
+       returned[length]='/'; length++;
+       if (maxSize <= length)
+       {
+               /*errno=KDB_ERR_TOOLONG;*/
+               return -1;
+       }
+       rc=kdbbKeyCalcRelativeFilename(forKey,returned+length,maxSize-length);
+
+       if (rc == -1) return -1;
+       else length += rc;
+
+       return length;
+}
+
diff --git a/src/libhelper/helper.h b/src/libhelper/helper.h
new file mode 100644 (file)
index 0000000..669d9b7
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef HELPERS_H
+#define HELPERS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <kdbbackend.h>
+
+#endif
+
diff --git a/src/libhelper/internal.c b/src/libhelper/internal.c
new file mode 100644 (file)
index 0000000..eb2ecb3
--- /dev/null
@@ -0,0 +1,206 @@
+/***************************************************************************
+                      internal.c  -  Helper functions for elektra
+                             -------------------
+    begin                : Mon Dec 29 2003
+    copyright            : (C) 2003 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+/**
+ * @defgroup internal KDB Backends :: Internal Helper for Elektra
+ * @brief Internal Methods for Elektra and Backends.
+ *
+ * To use them:
+ * @code
+ * #include <kdbbackend.h>
+ * @endcode
+ *
+ * There are some areas where libraries have to reimplement
+ * some basic functions to archive support for non-standard
+ * systems, for testing purposes or to provide a little more
+ * convenience.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if DEBUG && HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include "kdbbackend.h"
+
+
+/**Compare Strings ignoring case.
+ *
+ * @param s1 The first string to be compared
+ * @param s2 The second string to be compared
+ *
+ * @ingroup internal
+ * @return a negative number if s1 is less than s2
+ * @return 0 if s1 matches s2
+ * @return a positive number if s1 is greater than s2
+ **/
+int kdbiStrCaseCmp (const char *s1, const char *s2)
+{
+       const unsigned char *p1 = (const unsigned char *)s1;
+       const unsigned char *p2 = (const unsigned char *)s2;
+       int result;
+
+       if (p1 == p2) return 0;
+
+       while ((result = tolower(*p1)-tolower(*p2 ++)) == 0)
+       {
+               if (*p1++ == '\0') break;
+       }
+
+       return result;
+}
+
+/**Reallocate Storage in a save way.
+ *
+ *@code
+if (kdbiRealloc ((void **) & buffer, new_length) < 0) {
+       // here comes the failure handler
+       // you can still use the old buffer
+#if DEBUG
+       fprintf (stderr, "Reallocation error\n");
+#endif
+       free (buffer);
+       buffer = 0;
+       // return with error
+}
+ *@endcode
+ *
+ * @param buffer is a pointer to a malloc
+ * @param size is the new size for the memory
+ * @return -1 on failure
+ * @return 0 on success
+ * @ingroup internal
+ */
+int kdbiRealloc (void ** buffer, size_t size)
+{
+       void * ptr;
+       void * svr = *buffer;
+       ptr = realloc(*buffer, size);
+       if (ptr == NULL)
+       {
+               *buffer = svr;  /* restore old buffer*/
+               return -1;
+       } else {
+               *buffer = ptr;
+               return 0;
+       }
+}
+
+/**Allocate memory for elektra or backends.
+ *
+ *@code
+if ((buffer = kdbiMalloc (length)) == 0) {
+       // here comes the failure handler
+       // no allocation happened here, so dont use buffer
+#if DEBUG
+       fprintf (stderr, "Allocation error");
+#endif
+       // return with error
+}
+ *@endcode
+ *
+ *@param size the requested size
+ *
+ * This function is compatible to ANSI-C malloc
+ *@see kdbiFree*/
+void* kdbiMalloc (size_t size)
+{
+       return malloc (size);
+}
+
+/**Free memory of elektra or its backends.
+ *
+ *@param ptr the pointer to free
+ *
+ * @ingroup internal
+ *@see kdbiMalloc
+ */
+void kdbiFree (void *ptr)
+{
+       free (ptr);
+}
+
+
+/**Copy string into new allocated memory.
+ *
+ * You need to free the memory yourself.
+ *
+ * @param s the null-terminated string to duplicate
+ *
+ * @ingroup internal
+ * @see kdbiFree
+ * @see kdbiStrLen
+ */
+char *kdbiStrDup (const char *s)
+{
+       void *tmp = 0;
+       if (s)
+       {
+               tmp = kdbiMalloc (kdbiStrLen(s));
+               if (tmp) strcpy (tmp, s);
+       }
+
+       return tmp;
+}
+
+
+/**
+ * Calculates the length in bytes of a string.
+ *
+ * This function differs from strlen() because it is Unicode and multibyte
+ * chars safe. While strlen() counts characters and ignores the final NULL,
+ * kdbiStrLen() count bytes including the ending NULL.
+ *
+ * @ingroup internal
+ * @param s the string to get the length from
+ * @return number of bytes used by the string, including the final NULL.
+ * @ingroup internal
+ */
+size_t kdbiStrLen(const char *s)
+{
+       char *found=strchr(s,0);
+       if (found) return found-s+1;
+       return 0;
+}
diff --git a/src/libloader/Makefile.am b/src/libloader/Makefile.am
new file mode 100644 (file)
index 0000000..9df7861
--- /dev/null
@@ -0,0 +1,22 @@
+# $Id$
+
+AM_CPPFLAGS = -DBACKEND_LIBDIR="\"$(backenddir):$(hlvlbackenddir):$(ulibdir):$(libdir)\""
+
+INCLUDES = -I$(top_srcdir)/src/include
+
+if HAVE_LIBLTDL
+INCLUDES += $(INCLTDL)
+endif
+
+noinst_LIBRARIES = libloader-static.a
+libloader_static_a_SOURCES = kdbLibLoader.c ../include/kdbloader.h ../include/ltdl.h
+libloader_static_a_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+noinst_LTLIBRARIES = libloader-dynamic.la
+libloader_dynamic_la_SOURCES = kdbLibLoader.c ../include/kdbloader.h ../include/ltdl.h
+libloader_dynamic_la_LIBADD = $(LIBLTDL)
+libloader_dynamic_la_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
+
diff --git a/src/libloader/kdbLibLoader.c b/src/libloader/kdbLibLoader.c
new file mode 100644 (file)
index 0000000..085a859
--- /dev/null
@@ -0,0 +1,134 @@
+/***************************************************************************
+            kdbloader.c  -  Dynamically loading backends
+                             -------------------
+    begin                : Sun Mar 19 2006
+    copyright            : (C) 2004 by Avi Alkalay
+    email                : avi@unix.sh
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "kdbloader.h"
+
+#ifdef ELEKTRA_STATIC
+/* Static case */
+
+int kdbLibInit(void)
+{
+       return 0;
+}
+
+kdbLibHandle kdbLibLoad(const char *module)
+{
+       kdblib_symbol   *current;
+       current = kdb_exported_syms;
+       while ( current->name != NULL ) {
+               /* Skip symbols, we're searching for
+                * the module name */
+               if ( current->function == NULL && strcmp(current->name, module) == 0 ) {
+                       /* Go to the first symbol for this file */
+                       current++;
+                       return current;
+               }
+
+               current++;
+       }
+
+       return NULL;
+}
+
+kdbLibFunc kdbLibSym(kdbLibHandle handle, const char *symbol)
+{
+       kdblib_symbol   *current;
+
+       current = handle;
+       /* For each symbol about this module */
+       while ( current->function != NULL ) {
+               if ( strcmp(current->name, symbol) == 0 )
+                       return current->function;
+
+               current++;
+       }
+       
+       return NULL;
+}
+
+int kdbLibClose(kdbLibHandle handle)
+{
+       return 0;
+}
+
+#else
+#ifdef WIN32
+/* Windows dynamic case */
+
+int kdbLibInit(void)
+{
+  return 0;
+}
+
+kdbLibHandle kdbLibLoad(const char *module)
+{
+  char *modulename = malloc((sizeof(char)*strlen(module))+sizeof(".dll"));
+  kdbLibHandle handle;
+  strcpy(modulename, module);
+  strcat(modulename, ".dll");
+  handle = LoadLibrary(modulename);
+  free(modulename);
+  return handle;
+}
+
+kdbLibFunc kdbLibSym(kdbLibHandle handle, const char *symbol)
+{
+  return GetProcAddress(handle, symbol);
+}
+
+int kdbLibClose(kdbLibHandle handle)
+{
+  return FreeLibrary(handle);
+}
+
+#else
+/* Generic case using libltdl */
+int kdbLibInit(void)
+{
+  int init_errors = 0;
+  init_errors = lt_dlinit();
+  if (init_errors)
+    return init_errors;
+  return lt_dladdsearchdir(BACKEND_LIBDIR);
+}
+
+kdbLibHandle kdbLibLoad(const char *module)
+{
+       kdbLibHandle    handle;
+       handle = lt_dlopenext(module);
+       return handle;
+}
+
+kdbLibFunc kdbLibSym(kdbLibHandle handle, const char *symbol)
+{
+  return (kdbLibFunc) lt_dlsym(handle, symbol);
+}
+
+int kdbLibClose(kdbLibHandle handle)
+{
+  return lt_dlclose(handle);
+}
+
+#endif
+#endif
diff --git a/src/preload/.deps/preload-preload.Po b/src/preload/.deps/preload-preload.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/preload/.deps/preload_static-preload.Po b/src/preload/.deps/preload_static-preload.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/src/preload/Makefile.am b/src/preload/Makefile.am
new file mode 100644 (file)
index 0000000..78b59f9
--- /dev/null
@@ -0,0 +1,19 @@
+AM_CPPFLAGS =  -DDATADIR=\"$(datadir)\" \
+       -I$(top_srcdir)/src/include $(LTDLINCL)
+
+preload_source = preload.c ../include/kdb.h
+
+bin_PROGRAMS = preload preload_static
+preload_SOURCES = $(preload_source)
+preload_LDADD = ../libelektra/libelektra.la $(LIBLTDL) $(LIBICONV)
+preload_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+
+preload_static_SOURCES =  $(preload_source)
+preload_static_LDADD = $(privatelibs) ../libelektra/libelektra.a $(LIBICONV)
+preload_static_CFLAGS = -DELEKTRA_STATIC $(COPTFLAGS) $(CDBGFLAGS)
+
+../libelektra/libelektra.a:
+       cd ../libelektra && $(MAKE) libelektra.a
+
+clean-local:
+       rm -f *.gcno *.gcda *.gcno
diff --git a/src/preload/preload.c b/src/preload/preload.c
new file mode 100644 (file)
index 0000000..9be7f1a
--- /dev/null
@@ -0,0 +1,397 @@
+/***************************************************************************
+               preload.c - Preload your system with configuration
+                             -------------------
+    begin                : Mon 12 Nov 2007
+    copyright            : (C) 2007 by Markus Raab
+    email                : registry@markus-raab.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <kdb.h>
+#include <config.h>
+#include <string.h>
+#include <stdio.h>
+
+int preload_user (KDB *handle);
+int preload_system (KDB *handle);
+int preload_tests (KDB *handle);
+int preload_mountpoint(KDB *handle, char* name, char *mountpoint, char *backend, char *path);
+
+int preload_user (KDB *handle)
+{
+       KeySet *ks = ksNew (100,
+               keyNew( "user",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "User configuration",
+                       KEY_END),
+               keyNew( "user/env",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "User environment",
+                       KEY_END),
+               keyNew( "user/env/PATH",
+                       KEY_VALUE, "/usr/local/bin:/usr/bin:/bin",
+                       KEY_COMMENT, "User environment",
+                       KEY_END),
+               keyNew( "user/bindkey",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "User bindkey configuration",
+                       KEY_END),
+               keyNew( "user/bindkey/menu",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Menu shortcuts",
+                       KEY_END),
+               keyNew( "user/bindkey/menu/open",
+                       KEY_VALUE, "CTRL_O",
+                       KEY_COMMENT, "Open a new file",
+                       KEY_END),
+               keyNew( "user/bindkey/menu/quit",
+                       KEY_VALUE, "CTRL_Q",
+                       KEY_COMMENT, "Open a new file",
+                       KEY_END),
+               keyNew( "user/bindkey/menu/find",
+                       KEY_VALUE, "CTRL_F",
+                       KEY_COMMENT, "Open a new file",
+                       KEY_END),
+               keyNew( "user/bindkey/clipboard",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Clipboard configuration",
+                       KEY_END),
+               keyNew( "user/bindkey/clipboard/paste",
+                       KEY_VALUE, "CTRL_V",
+                       KEY_COMMENT, "Paste clipboard content",
+                       KEY_END),
+               keyNew( "user/bindkey/clipboard/copy",
+                       KEY_VALUE, "CTRL_C",
+                       KEY_COMMENT, "Copy clipboard content",
+                       KEY_END),
+               keyNew( "user/bindkey/clipboard/cut",
+                       KEY_VALUE, "CTRL_C",
+                       KEY_COMMENT, "Cut clipboard content",
+                       KEY_END),
+               keyNew( "user/sw",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "User software configuration",
+                       KEY_END),
+               keyNew( "user/sw/lib",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Library configuration",
+                       KEY_END),
+               keyNew( "user/sw/sound",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Sound configuration",
+                       KEY_END),
+               keyNew( "user/sw/gnome",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Gnome configuration",
+                       KEY_END),
+               keyNew( "user/sw/kde",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Kde configuration",
+                       KEY_END),
+               keyNew( "user/sw/science",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Science configuration",
+                       KEY_END),
+               keyNew( "user/sw/shells",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Shells configuration",
+                       KEY_END),
+               keyNew( "user/sw/shells/zsh",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Zsh configuration",
+                       KEY_END),
+               keyNew( "user/sw/shells/zsh/env",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Environment specific"
+                       "for zsh which will be additional to user/env for zsh",
+                       KEY_END),
+               keyNew( "user/sw/shells/bash",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Bash configuration",
+                       KEY_END),
+               KS_END);
+       printf ("Set user configuration... ");
+       if (kdbSet (handle, ks, keyNew("user",0), KDB_O_DEL) == -1)
+       {
+               printf ("failure\n");
+               return 1;
+       } else {
+               printf ("done\n");
+               return 0;
+       }
+}
+
+
+int preload_system (KDB *handle)
+{
+       KeySet *ks = ksNew (100,
+               keyNew( "system",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "System configuration",
+                       KEY_END),
+               keyNew( "system/hosts",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Hosts configuration for this system.\n"
+                       "Setup hosts, ip adresses and aliases.",
+                       KEY_END),
+               keyNew( "system/filesystems",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Filesystem configuration for this system.\n"
+                       "Setup mountpoints, types and options.",
+                       KEY_END),
+               keyNew( "system/users",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "User configuration for this system.\n"
+                       "Add users and set passwords.",
+                       KEY_END),
+               KS_END);
+       printf ("Set system configuration... ");
+       if (kdbSet (handle, ks, keyNew("system",0), KDB_O_DEL) == -1)
+       {
+               printf ("failure\n");
+               return 1;
+       } else {
+               printf ("done\n");
+               return 0;
+       }
+}
+
+
+int preload_tests (KDB *handle)
+{
+       KeySet *ks = ksNew (100,
+               keyNew( "user/tests",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Place for tests.\n"
+                       "Used by elektra testframework.",
+                       KEY_END),
+               keyNew( "user/benchmarks",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Place for benchmarks.\n"
+                       "Used by elektra benchmarkframework.",
+                       KEY_END),
+               KS_END);
+       printf ("Set test directories... ");
+       if (kdbSet (handle, ks, keyNew("user",0), KDB_O_DEL) == -1)
+       {
+               printf ("failure\n");
+               return 1;
+       } else {
+               printf ("done\n");
+               return 0;
+       }
+}
+
+
+int preload_configuration(KDB *handle)
+{
+       KeySet *ks = ksNew (100,
+               keyNew( "system/elektra",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Configuration for libelektra.",
+                       KEY_END),
+               keyNew( "system/elektra/xml",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Configuration for libelektratools.",
+                       KEY_END),
+               keyNew( "system/elektra/xml/schemapath",
+                       KEY_VALUE, KDB_SCHEMA_PATH,
+                       KEY_COMMENT, "Schemapath to elektra xml-schema.\n"
+                       "The schema is needed to validate xml files for in- and export.",
+                       KEY_END),
+               keyNew( "system/elektra/mountpoints",
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "Mountpoints for elektra.\n"
+                       "Place a directory here to make a mountpoint.\n"
+                       "A mountpoint decides to use a different backend for that path and below.\n"
+                       "See system/mountpoints/template how to create it.",
+                       KEY_END),
+               KS_END);
+       printf ("Set mountpoints... ");
+       if (kdbSet (handle, ks, keyNew("system",0), KDB_O_DEL) == -1)
+       {
+               printf ("failure\n");
+               return 1;
+       } else {
+               printf ("done\n");
+               return 0;
+       }
+}
+
+char * location (char *buffer, char *name, char *point)
+{
+       buffer[0] = 0;
+       strcat (buffer, "system/elektra/mountpoints/");
+       strcat (buffer, name);
+       strcat (buffer, point);
+       return buffer;
+}
+
+int preload_mountpoint(KDB *handle, char* name, char *mountpoint, char *backend, char *path)
+{
+       char buffer [MAX_PATH_LENGTH];
+
+       KeySet *ks = ksNew (100,
+               keyNew( mountpoint,
+                       KEY_DIR,
+                       KEY_VALUE, backend,
+                       KEY_COMMENT, "This is a mounted backend.",
+                       KEY_END),
+               keyNew( location (buffer, name, ""),
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "This is a mounted backend, see subkeys for more information",
+                       KEY_END),
+               keyNew( location (buffer, name, "/mountpoint"),
+                       KEY_VALUE, mountpoint,
+                       KEY_COMMENT, "The mountpoint says the location where the backend should be mounted.\n"
+                       "It must be a valid, canonical elektra path. There are no ., .. or multiple slashes allowed.\n"
+                       "You are not allowed to mount inside system/elektra.",
+                       KEY_END),
+               keyNew( location (buffer, name, "/backend"),
+                       KEY_VALUE, backend,
+                       KEY_COMMENT, "The name of the backend library.\n"
+                       "This name describes which .so should be loaded for that backend.\n"
+                       "You are allowed to mount the same backend multiple times.",
+                       KEY_END),
+               keyNew( location (buffer, name, "/config"),
+                       KEY_DIR,
+                       KEY_VALUE, "",
+                       KEY_COMMENT, "The configuration for the specific backend.\n"
+                       "All keys below that directory will be passed to backend.\n"
+                       "These keys have backend specific meaning.\n"
+                       "See documentation http://www.libelektra.org for which keys must or can be set.\n"
+                       "Here the most important keys should be preloaded.",
+                       KEY_END),
+               keyNew( location (buffer, name, "/config/path"),
+                       KEY_VALUE, path,
+                       KEY_COMMENT, "The path where the config file is located."
+                       "This item is often used by backends using configuration in a filesystem"
+                       "to know there relative location of the keys to fetch or write.",
+                       KEY_END),
+               KS_END);
+       printf ("Set %s mountpoint %s in path %s... ", name, mountpoint, path);
+       if (kdbSet (handle, ks, keyNew("system",0), KDB_O_DEL) == -1)
+       {
+               printf ("failure\n");
+               return 1;
+       } else {
+               printf ("done\n");
+               return 0;
+       }
+}
+
+
+/**
+ * See http://www.libelektra.org/Preloading for more information.
+ *
+ * Preloading is a technique to make the first start without
+ * build-in values possible.
+ *
+ * This program will preload the configuration for elektra itself.
+ * It should be executed by the administrator for system and
+ * configuration.
+ *
+ * But every user should also execute it to have a minimum skeleton
+ * to make it more easy for the programs and to have full pathes
+ * when having mountpoints.
+ *
+ */
+int main(int argc, char ** argv)
+{
+       KDB *handle = kdbOpen();
+       int rc=0;
+
+       if (argc == 1)
+       {
+               printf ("Use %s <mode>\n", argv[0]);
+               printf ("\n");
+               printf ("Modes are:\n");
+               printf ("system ... creates the system hierarchy\n");
+               printf ("user   ... creates the user hierarchy\n");
+               printf ("tests  ... creates the skeleton for tests and benchmarks\n");
+               printf ("conf   ... creates the skeleton for configuration for elektra\n");
+               printf ("mount <name> [<root>] [<backend>] [<path>]\n");
+               printf ("\n");
+               printf ("Mount options are:\n");
+               printf ("name   ... test for some predefined mountpoints\n");
+               printf ("           root for the root backend\n");
+               printf ("           otherwise any unique name for personal use\n");
+               printf ("other arguments are empty for test and root\n");
+               printf ("root   ... Where to mount the backend\n");
+               printf ("backend... Name of the backend\n");
+               printf ("path   ... Where the files should be located on the disk\n");
+               printf ("\n");
+               printf ("examples:\n");
+               printf (" preload system\n");
+               printf (" preload mount root ... mounts root backend\n");
+               printf (" preload mount test ... mounts some backends\n");
+               printf (" preload mount fstab system/filesystems/ fstab /tmp/fstab\n");
+               printf (" preload mount passwd system/users/ passwd /tmp/passwd\n");
+               return 0;
+       } else if (argc > 1) {
+               if (!strcmp (argv[1], "system")) rc=preload_system(handle);
+               else if (!strcmp (argv[1], "user")) rc=preload_user(handle);
+               else if (!strcmp (argv[1], "tests")) rc=preload_tests(handle);
+               else if (!strcmp (argv[1], "conf")) rc=preload_configuration(handle);
+               else if (!strcmp (argv[1], "mount"))
+               {
+                       if (argc == 3)
+                       {
+                               if (!strcmp (argv[2], "root"))
+                               {
+                                       rc=preload_mountpoint(handle, "root", "", "filesys", "/etc/kdb");
+                               } else if (!strcmp (argv[2], "test")) {
+                                       preload_mountpoint(handle, "hosts1", "system/hosts/", "hosts", "/etc/hosts");
+                                       preload_mountpoint(handle, "hosts2", "user/hosts/", "hosts", "/tmp/hosts");
+                                       preload_mountpoint(handle, "passwd1", "system/users/", "passwd", "/tmp/passwd");
+                                       preload_mountpoint(handle, "passwd2", "user/users/", "passwd", "/etc/passwd");
+                                       preload_mountpoint(handle, "fstab1", "system/filesystems/", "fstab", "/etc/fstab");
+                                       rc=preload_mountpoint(handle, "fstab2", "user/filesystems/", "fstab", "/tmp/fstab");
+                                       /*
+                                       preload_mountpoint(handle, "template1", "user/template/", "template", "");
+                                       preload_mountpoint(handle, "template2", "system/template/", "template", "");
+                                       */
+                               }
+                               else printf ("unkown mount options: give more arguments or use root or test\n");
+                       }
+                       else if (argc == 6)
+                       {
+                               rc=preload_mountpoint(handle, argv[2], argv[3], argv[4], argv[5]);
+                       }
+                       else printf ("uncorrect number of arguments\n");
+               } else printf ("unknown mode: use system, user, tests, conf or mount\n");
+       }
+       kdbClose (handle);
+       return rc;
+}
diff --git a/tests/.deps/print_info.Po b/tests/.deps/print_info.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_backendhelpers.Po b/tests/.deps/test_backendhelpers.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_cap.Po b/tests/.deps/test_cap.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_getset.Po b/tests/.deps/test_getset.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_internals.Po b/tests/.deps/test_internals.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_kdb.Po b/tests/.deps/test_kdb.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_key.Po b/tests/.deps/test_key.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_ks.Po b/tests/.deps/test_ks.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_mount.Po b/tests/.deps/test_mount.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_serialize.Po b/tests/.deps/test_serialize.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_split.Po b/tests/.deps/test_split.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_stream.Po b/tests/.deps/test_stream.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_trie.Po b/tests/.deps/test_trie.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_type.Po b/tests/.deps/test_type.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/test_xml.Po b/tests/.deps/test_xml.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/.deps/tests.Po b/tests/.deps/tests.Po
new file mode 100644 (file)
index 0000000..9ce06a8
--- /dev/null
@@ -0,0 +1 @@
+# dummy
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644 (file)
index 0000000..1eb122b
--- /dev/null
@@ -0,0 +1,68 @@
+AM_CFLAGS = $(COPTFLAGS) $(CDBGFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LTDLINCL)
+
+TESTS = test_key test_ks test_kdb test_xml test_validate.sh test_getset test_trie test_mount test_cap test_stream test_serialize test_split test_internals test_backendhelpers test_script.sh test_type
+#TESTS = test_backendhelpers
+
+check_PROGRAMS    = test_key test_ks test_kdb test_xml test_getset test_trie test_mount test_cap test_stream test_serialize test_split test_internals test_backendhelpers test_type print_info
+#check_PROGRAMS    = test_backendhelpers
+
+if VALGRINDTESTS
+TESTS_ENVIRONMENT = $(VALGRIND) --quiet --show-reachable=yes --leak-check=yes
+endif
+
+EXTRA_DIST = dbelow.c filesys.xml fstab.xml fstab-cmp.xml hosts.xml keyset.c keyset.xml key.xml key-cmp.xml others.c passwd.xml test_script.sh test_validate.sh test_script.sh tests.c tests.h
+
+clean-local:
+       rm -rf .kdb
+       rm -f *-gen.xml
+       rm -f *.gcno *.gcda *.gcno
+
+test_key_SOURCES = test_key.c tests.h tests.c
+test_key_LDADD = $(privatelibs) ../src/libelektra/libelektra.a 
+
+test_ks_SOURCES = test_ks.c tests.h tests.c
+test_ks_LDADD = $(privatelibs) ../src/libelektra/libelektra.a 
+
+test_kdb_SOURCES = test_kdb.c tests.h tests.c
+test_kdb_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_xml_SOURCES = test_xml.c tests.h tests.c
+test_xml_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_getset_SOURCES = test_getset.c tests.h tests.c
+test_getset_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_trie_SOURCES = test_trie.c tests.h tests.c
+test_trie_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_mount_SOURCES = test_mount.c tests.h tests.c
+test_mount_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_cap_SOURCES = test_cap.c tests.h tests.c
+test_cap_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_stream_SOURCES = test_stream.c tests.h tests.c
+test_stream_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_serialize_SOURCES = test_serialize.c tests.h tests.c
+test_serialize_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_split_SOURCES = test_split.c tests.h tests.c
+test_split_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_internals_SOURCES = test_internals.c tests.h tests.c
+test_internals_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_backendhelpers_SOURCES = test_backendhelpers.c tests.h tests.c
+test_backendhelpers_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+test_type_SOURCES = test_type.c tests.h tests.c
+test_type_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+print_info_SOURCES = print_info.c tests.h tests.c
+print_info_LDADD = $(privatelibs) ../src/libelektra/libelektra.a
+
+../src/libelektra/libelektra.a:
+       cd ../src/libelektra && $(MAKE) libelektra.a
+
diff --git a/tests/dbelow.c b/tests/dbelow.c
new file mode 100644 (file)
index 0000000..b05315e
--- /dev/null
@@ -0,0 +1,82 @@
+ksNew( 28 ,
+       keyNew ("user/test/keyset/dir1"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),KS_END);
diff --git a/tests/filesys.xml b/tests/filesys.xml
new file mode 100644 (file)
index 0000000..7a5b869
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of 9 keys. -->
+<keyset xmlns="http://www.libelektra.org"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+       parent = "user/tests/filesys"
+       >
+       <key type="string" basename="PerfectStringKey" value="StringValue"><comment>String key with standard name</comment></key>
+       <key type="string" isdir="true" basename="PerfectDirectoryKey" value="DirectoryValue"><comment>Directory key with standard name</comment></key>
+       <key type="binary" basename="PerfectBinaryKey" value="BinaryValue"><comment>Binary key with standard name</comment></key>
+
+       <key type="string" basename=".HiddenStringKey" value="StringValue"><comment>String key with hidden name</comment></key>
+       <key type="string" isdir="true" basename=".HiddenDirectoryKey" value="DirectoryValue"><comment>Directory key with hidden name</comment></key>
+       <key type="binary" basename=".HiddenBinaryKey" value="BinaryValue"><comment>Binary key with hidden name</comment></key>
+
+       <key type="string" basename="Ug.ly:St@ri€n.g Key" value="With a string value"><comment>string key with ugly name</comment></key>
+       <key type="string" isdir="true" basename="Ug.ly:Dir@ect€ory Key"><comment>Directory with ugly name</comment></key>
+       <key type="binary" basename="Ug.ly:Bin@a€ry Key"><comment>Binary key with ugly name</comment></key>
+</keyset>
diff --git a/tests/fstab-cmp.xml b/tests/fstab-cmp.xml
new file mode 100644 (file)
index 0000000..3f03f9a
--- /dev/null
@@ -0,0 +1,23 @@
+<keyset>
+<key name="user/tests/fstab/rootfs"
+        mode="0775"/>
+
+<key name="user/tests/fstab/rootfs/device"
+        mode="0664"
+       value="LABEL=/"/>
+<key name="user/tests/fstab/rootfs/dumpfreq"
+        mode="0664"
+       value="1"/>
+<key name="user/tests/fstab/rootfs/mpoint"
+        mode="0664"
+       value="/"/>
+<key name="user/tests/fstab/rootfs/options"
+        mode="0664"
+       value="defaults"/>
+<key name="user/tests/fstab/rootfs/passno"
+        mode="0664"
+       value="1"/>
+<key name="user/tests/fstab/rootfs/type"
+        mode="0664"
+       value="ext3"/>
+</keyset>
diff --git a/tests/fstab.xml b/tests/fstab.xml
new file mode 100644 (file)
index 0000000..166552b
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+       parent = "user/tests/fstab"
+       >
+
+       <key basename="rootfs">
+               <!-- This keyset is equivalent to the following /etc/fstab line:
+                         
+                         LABEL=/  /   ext3  defaults  1 1
+                         
+               -->
+               <key basename="device"   value="LABEL=/"/>
+               <key basename="mpoint"   value="/"/>
+               <key basename="type"     value="ext3"/>
+               <key basename="options"  value="defaults"/>
+               <key basename="dumpfreq" value="1"/>
+               <key basename="passno"   value="1"/>
+       </key>
+</keyset>
diff --git a/tests/hosts.xml b/tests/hosts.xml
new file mode 100644 (file)
index 0000000..ee7b58d
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of 18 keys. -->
+<keyset xmlns="http://www.libelektra.org"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+        parent="user/tests/hosts">
+
+       <key basename="gateway" mode="0664" value="192.168.0.1"/>
+       <key basename="home" mode="0664" value="86.59.55.224"/>
+       <key basename="ip6-allhosts" mode="0664" value="ff02::3">
+               <comment>All hosts for ipv6</comment>
+       </key>
+       <key basename="ip6-allnodes" mode="0664" value="ff02::1"/>
+       <key basename="ip6-allrouters" mode="0664" value="ff02::2"/>
+       <key basename="ip6-localhost" mode="0775" value="::1"/>
+       <key basename="ip6-localhost/alias00" mode="0664" value="ost"/>
+       <key basename="ip6-localhost/alias01" mode="0664" value="ip6-loopback"/>
+       <key basename="ip6-localnet" mode="0664" value="fe00::0"/>
+       <key basename="ip6-mcastprefix" mode="0664" value="ff00::0"/>
+       <key basename="localhost" mode="0664" value="127.0.0.1"/>
+       <key basename="markusbyte" mode="0664" value="192.168.0.3">
+               <comment>This is my home</comment>
+       </key>
+       <key basename="mobilebyte.sil.at" mode="0775" value="192.168.0.4"/>
+       <key basename="mobilebyte.sil.at/alias00" mode="0664" value="mobilebyte"/>
+       <key basename="superbyte" mode="0775" value="192.168.0.2"/>
+       <key basename="superbyte/alias00" mode="0664" value="printer"/>
+       <key basename="superbyte/alias01" mode="0664" value="laser"/>
+       <key basename="superbyte/alias02" mode="0664" value="news"/>
+</keyset>
diff --git a/tests/key-cmp.xml b/tests/key-cmp.xml
new file mode 100644 (file)
index 0000000..8cb55a5
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of 9 keys. -->
+<keyset xmlns="http://www.libelektra.org"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+>
+<key name="user/tests/filesys/.HiddenBinaryKey"
+        mode="0440">
+
+     <value>
+42696e61 72795661 6c756500 
+
+</value>
+     <comment><![CDATA[Binary key with hidden name]]></comment>
+</key>
+
+<key name="user/tests/filesys/.HiddenDirectoryKey"
+        mode="0775"
+       value="DirectoryValue">
+
+     <comment><![CDATA[Directory key with hidden name]]></comment>
+</key>
+
+<key name="user/tests/filesys/.HiddenStringKey"
+        uid="0" gid="20" mode="0664"
+       value="StringValue">
+
+     <comment><![CDATA[String key with hidden name]]></comment>
+</key>
+
+<key name="user/tests/filesys/PerfectBinaryKey"
+        gid="40" mode="0664">
+
+     <value>
+42696e61 72795661 6c756500 
+
+</value>
+     <comment><![CDATA[Binary key with standard name]]></comment>
+</key>
+
+<key name="user/tests/filesys/PerfectDirectoryKey"
+        mode="0775"
+       value="DirectoryValue">
+
+     <comment><![CDATA[Directory key with standard name]]></comment>
+</key>
+
+<key name="user/tests/filesys/PerfectStringKey"
+        uid="20" mode="0664"
+       value="StringValue">
+
+     <comment><![CDATA[String key with
+standard name]]></comment>
+</key>
+
+<key name="user/tests/filesys/Ug.ly:Bin@a€ry Key"
+        mode="0346">
+
+     <comment><![CDATA[Binary key with ugly name]]></comment>
+</key>
+
+<key name="user/tests/filesys/Ug.ly:Dir@ect€ory Key"
+        mode="0775">
+
+     <comment><![CDATA[Directory with ugly name]]></comment>
+</key>
+
+<key name="user/tests/filesys/Ug.ly:St@ri€n.g Key"
+        mode="0664">
+
+     <value><![CDATA[With a string value]]></value>
+     <comment><![CDATA[string key with ugly name]]></comment>
+</key>
+
+</keyset>
diff --git a/tests/key.xml b/tests/key.xml
new file mode 100644 (file)
index 0000000..be4fcba
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of 9 keys. -->
+<keyset xmlns="http://www.libelektra.org"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+       parent = "user/tests/filesys"
+       >
+       <key type="string" uid="20" basename="PerfectStringKey" value="StringValue">
+               <comment>String key with</comment>
+               <comment>standard name</comment>
+       </key>
+       <key type="string" isdir="true" basename="PerfectDirectoryKey" value="DontCare">
+               <value>DirectoryValue</value>
+               <comment>Directory key with standard name</comment>
+       </key>
+       <key type="binary" gid="40" basename="PerfectBinaryKey" value="BinaryValue" isdir="0">
+               <comment>Binary key with standard name</comment>
+       </key>
+
+       <key type="string" uid="0" gid="20" basename=".HiddenStringKey" value="StringValue"><comment>String key with hidden name</comment></key>
+       <key type="string" isdir="true" basename=".HiddenDirectoryKey" value="DirectoryValue"><comment>Directory key with hidden name</comment></key>
+       <key type="30" mode="0440" basename=".HiddenBinaryKey" value="BinaryValue"><comment>Binary key with hidden name</comment></key>
+
+       <key type="string" basename="Ug.ly:St@ri€n.g Key" value="With a string value"><comment>string key with ugly name</comment></key>
+       <key type="string" isdir="true" basename="Ug.ly:Dir@ect€ory Key"><comment>Directory with ugly name</comment></key>
+       <key type="binary" mode="230" basename="Ug.ly:Bin@a€ry Key"><comment>Binary key with ugly name</comment></key>
+</keyset>
diff --git a/tests/keyset.c b/tests/keyset.c
new file mode 100644 (file)
index 0000000..a9ed79b
--- /dev/null
@@ -0,0 +1,105 @@
+ksNew(128,
+       keyNew ("user", KEY_DIR,0),
+       keyNew ("user/test", KEY_DIR,0),
+       keyNew ("user/test/keyset", KEY_DIR,0),
+       keyNew ("user/test/keyset/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir1", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir1/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir1/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir1/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir1/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir1/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir1/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir1/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir1/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir1/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir2", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir2/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir2/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir2/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir2/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir2/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir2/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir2/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir2/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir2/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir3", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir3/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir3/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir3/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir3/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir3/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir3/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir3/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir3/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir3/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir4", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir4/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir4/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir4/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir4/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir4/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir4/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir4/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir4/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir4/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir5", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir5/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir5/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir5/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir5/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir5/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir5/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir5/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir5/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir5/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir6", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir6/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir6/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir6/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir6/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir6/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir6/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir6/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir6/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir6/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir7", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir7/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir7/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir7/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir7/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir7/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir7/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir7/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir7/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir7/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir8", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir8/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir8/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir8/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir8/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir8/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir8/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir8/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir8/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir8/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       keyNew ("user/test/keyset/dir9", KEY_DIR,0),
+       keyNew ("user/test/keyset/dir9/key1", KEY_VALUE, "value1", KEY_COMMENT, "comment1",0),
+       keyNew ("user/test/keyset/dir9/key2", KEY_VALUE, "value2", KEY_COMMENT, "comment2",0),
+       keyNew ("user/test/keyset/dir9/key3", KEY_VALUE, "value3", KEY_COMMENT, "comment3",0),
+       keyNew ("user/test/keyset/dir9/key4", KEY_VALUE, "value4", KEY_COMMENT, "comment4",0),
+       keyNew ("user/test/keyset/dir9/key5", KEY_VALUE, "value5", KEY_COMMENT, "comment5",0),
+       keyNew ("user/test/keyset/dir9/key6", KEY_VALUE, "value6", KEY_COMMENT, "comment6",0),
+       keyNew ("user/test/keyset/dir9/key7", KEY_VALUE, "value7", KEY_COMMENT, "comment7",0),
+       keyNew ("user/test/keyset/dir9/key8", KEY_VALUE, "value8", KEY_COMMENT, "comment8",0),
+       keyNew ("user/test/keyset/dir9/key9", KEY_VALUE, "value9", KEY_COMMENT, "comment9",0),
+       KS_END);
+
diff --git a/tests/keyset.xml b/tests/keyset.xml
new file mode 100644 (file)
index 0000000..790093c
--- /dev/null
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated by Elektra API. Total of 9 keys. -->
+<keyset xmlns="http://www.libelektra.org"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+       parent = "user/tests/filesys"
+       >
+ <key type="43" basename="0-27042916" value="0 216905227"><comment>2551516588474823843</comment></key>
+ <key type="253" basename="1-2449524622" value="1 1679328197"><comment>3246436893195629244</comment></key>
+ <key type="string" isdir="true" basename="dir-1-0">
+  <key type="114" basename="0-294164813" value="0 216245011"><comment>18454108762891828026</comment></key>
+  <key type="135" basename="1-1479930365" value="1 2732423037"><comment>24597295372375238</comment></key>
+  <key type="string" isdir="true" basename="dir-2-0">
+   <key type="144" basename="0-215571059" value="0 264857705"><comment>2188631490667217086</comment></key>
+   <key type="217" basename="1-172696863" value="1 1413010492"><comment>2009260691056619998</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-1">
+   <key type="173" basename="0-2000814607" value="0 1955026514"><comment>634720937883220825</comment></key>
+   <key type="20" basename="1-1242717281" value="1 1279621809"><comment>10835219082633423494</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-2">
+   <key type="28" basename="0-215623221" value="0 2615831169"><comment>439048222054330242</comment></key>
+   <key type="239" basename="1-268626445" value="1 211926272"><comment>2671264801133512045</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-3">
+   <key type="145" basename="0-72095994" value="0 3082914659"><comment>2462099381906519637</comment></key>
+   <key type="26" basename="1-1112526887" value="1 13790397"><comment>29552250853230432415</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-4">
+   <key type="242" basename="0-43814952" value="0 245627749"><comment>287081235381626635</comment></key>
+   <key type="223" basename="1-312568090" value="1 36219472"><comment>2056467162314026779</comment></key>
+  </key>
+ </key>
+ <key type="string" isdir="true" basename="dir-1-1">
+  <key type="88" basename="0-2993514317" value="0 3007016338"><comment>6513136152238131569</comment></key>
+  <key type="161" basename="1-2673925626" value="1 3085029208"><comment>595310088267681325</comment></key>
+  <key type="string" isdir="true" basename="dir-2-0">
+   <key type="247" basename="0-164223741" value="0 64307894"><comment>22531187641857031662</comment></key>
+   <key type="190" basename="1-3182527006" value="1 152121106"><comment>6050128332068515722</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-1">
+   <key type="229" basename="0-3219021027" value="0 240036888"><comment>16238284472997719576</comment></key>
+   <key type="159" basename="1-2486914396" value="1 145207467"><comment>3883281333052412037</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-2">
+   <key type="82" basename="0-46903647" value="0 2268621888"><comment>32753327232797919976</comment></key>
+   <key type="115" basename="1-905011462" value="1 1273310721"><comment>14800138803221317405</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-3">
+   <key type="197" basename="0-3208912452" value="0 1169512568"><comment>2676924538788523596</comment></key>
+   <key type="186" basename="1-1091331377" value="1 199987266"><comment>4522262551362712874</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-4">
+   <key type="202" basename="0-294181683" value="0 3146316501"><comment>2101827480127277145</comment></key>
+   <key type="179" basename="1-175659776" value="1 2334131443"><comment>198753828813576</comment></key>
+  </key>
+ </key>
+ <key type="string" isdir="true" basename="dir-1-2">
+  <key type="100" basename="0-972431067" value="0 133203847"><comment>1347011419147246413</comment></key>
+  <key type="129" basename="1-75222444" value="1 1867917733"><comment>31620169261048613078</comment></key>
+  <key type="string" isdir="true" basename="dir-2-0">
+   <key type="92" basename="0-315168369" value="0 1810024091"><comment>17609274262950321182</comment></key>
+   <key type="110" basename="1-18334792" value="1 355327020"><comment>26036252041174319415</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-1">
+   <key type="167" basename="0-43779371" value="0 2717329532"><comment>1847528778209268968</comment></key>
+   <key type="33" basename="1-211724603" value="1 1274718100"><comment>16075148982907919368</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-2">
+   <key type="93" basename="0-23186501" value="0 162585077"><comment>1823923665139346176</comment></key>
+   <key type="238" basename="1-117112769" value="1 124652661"><comment>15563326831112072</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-3">
+   <key type="20" basename="0-1045212001" value="0 2042429548"><comment>2095516044228183782</comment></key>
+   <key type="35" basename="1-2201810163" value="1 121814102"><comment>2139722875720320817</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-4">
+   <key type="33" basename="0-1913626837" value="0 2913127539"><comment>776043343146721534</comment></key>
+   <key type="187" basename="1-2016421284" value="1 753117627"><comment>255613898214632040</comment></key>
+  </key>
+ </key>
+ <key type="string" isdir="true" basename="dir-1-3">
+  <key type="176" basename="0-3155020840" value="0 990825763"><comment>21852226343272631185</comment></key>
+  <key type="200" basename="1-874922" value="1 1759916191"><comment>2623735621954316926</comment></key>
+  <key type="string" isdir="true" basename="dir-2-0">
+   <key type="223" basename="0-630724305" value="0 2995613338"><comment>1964917880248812636</comment></key>
+   <key type="157" basename="1-169830617" value="1 2619012633"><comment>6277190786814312</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-1">
+   <key type="20" basename="0-1313819824" value="0 1076430387"><comment>143682467514357045</comment></key>
+   <key type="78" basename="1-1642029463" value="1 1695720003"><comment>165992136954451860</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-2">
+   <key type="176" basename="0-153568349" value="0 802220498"><comment>2544318414240793465</comment></key>
+   <key type="220" basename="1-203925831" value="1 1773615811"><comment>326122332660329661</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-3">
+   <key type="205" basename="0-132335709" value="0 753610877"><comment>194216610785116060</comment></key>
+   <key type="203" basename="1-267732539" value="1 29164318"><comment>990832569759630797</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-4">
+   <key type="209" basename="0-3199312703" value="0 1001316339"><comment>21741123163068723187</comment></key>
+   <key type="93" basename="1-1790727245" value="1 289129416"><comment>53059801514130705</comment></key>
+  </key>
+ </key>
+ <key type="string" isdir="true" basename="dir-1-4">
+  <key type="133" basename="0-387126160" value="0 204394545"><comment>21188322531851320535</comment></key>
+  <key type="137" basename="1-151647309" value="1 2842511711"><comment>9883314882767419206</comment></key>
+  <key type="string" isdir="true" basename="dir-2-0">
+   <key type="53" basename="0-526230536" value="0 1181112782"><comment>1065225830188249547</comment></key>
+   <key type="132" basename="1-203866829" value="1 21315999"><comment>30944117441110710316</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-1">
+   <key type="162" basename="0-232282487" value="0 28204903"><comment>14723134441057028108</comment></key>
+   <key type="80" basename="1-2551523896" value="1 140713945"><comment>5891315043040223589</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-2">
+   <key type="198" basename="0-2408724547" value="0 293399851"><comment>11915196071822725798</comment></key>
+   <key type="136" basename="1-2609326364" value="1 240967228"><comment>2183621105162112017</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-3">
+   <key type="204" basename="0-563328213" value="0 1680912618"><comment>76214949230088786</comment></key>
+   <key type="64" basename="1-1453831796" value="1 245126793"><comment>15761313992127631931</comment></key>
+  </key>
+  <key type="string" isdir="true" basename="dir-2-4">
+   <key type="145" basename="0-257736880" value="0 867028967"><comment>319532359220727161</comment></key>
+   <key type="20" basename="1-2918914282" value="1 311082578"><comment>1104442372487923737</comment></key>
+  </key>
+ </key>
+<!-- 92 keys generated -->
+</keyset>
diff --git a/tests/others.c b/tests/others.c
new file mode 100644 (file)
index 0000000..72cd801
--- /dev/null
@@ -0,0 +1,418 @@
+ksNew( 94 ,
+       keyNew ("user"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir1/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir2/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir3/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir4/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir5/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir6/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir7/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir8/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key1"
+               , KEY_VALUE, "value1"
+               , KEY_COMMENT, "comment1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key2"
+               , KEY_VALUE, "value2"
+               , KEY_COMMENT, "comment2"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key3"
+               , KEY_VALUE, "value3"
+               , KEY_COMMENT, "comment3"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key4"
+               , KEY_VALUE, "value4"
+               , KEY_COMMENT, "comment4"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key5"
+               , KEY_VALUE, "value5"
+               , KEY_COMMENT, "comment5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key6"
+               , KEY_VALUE, "value6"
+               , KEY_COMMENT, "comment6"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key7"
+               , KEY_VALUE, "value7"
+               , KEY_COMMENT, "comment7"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key8"
+               , KEY_VALUE, "value8"
+               , KEY_COMMENT, "comment8"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/test/keyset/dir9/key9"
+               , KEY_VALUE, "value9"
+               , KEY_COMMENT, "comment9"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),KS_END);
diff --git a/tests/passwd.xml b/tests/passwd.xml
new file mode 100644 (file)
index 0000000..fab7f49
--- /dev/null
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+        
+       Subversion stuff
+        
+       $Id$
+        
+        -->
+
+<!--
+        
+       This specification-by-example defines a key tree layout for users, groups and password.
+        
+       Its purpose is similar to the Unix well-known files:
+        
+        /etc/passwd
+        /etc/group
+        /etc/shadow
+        
+       It defines 2 trees, system/users and system/groups
+       All keys must be owned by root with RO permissions to others, exept for the
+       password keys which should be readable only to root.
+        
+       In this example we'll define the 'jdoe' user and 'root' keys.
+        
+       Avi Alkalay
+       <avi at unix.sh>
+       Nov 2005
+
+-->
+
+
+
+<keyset xmlns="http://www.libelektra.org"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+        parent="system">
+
+
+
+       <key basename="users/jdoe">
+               <!-- The 'jdoe' record -->
+
+               <!-- Real name -->
+               <key basename="gecos" value="John Doe"/>
+
+               <!-- User and group IDs -->
+               <key basename="uid" value="500"/>
+               <key basename="gid" value="800"/>
+
+               <!-- Home directory and shell program -->
+               <key basename="home" value="/root"/>
+               <key basename="shell" value="/bin/bash"/>
+
+               <!-- Password metainfo as /etc/shadow.
+                         Check the 'mode' attribute, meaning they are secure keys -->
+               <key basename="passwdChangeAfter" mode="0600" value="99999"/>
+               <key basename="passwdChangeBefore" mode="0600" value="0"/>
+               <key basename="passwdDisableAfter" mode="0600"/>
+               <key basename="passwdDisabledSince" mode="0600"/>
+               <key basename="passwdReserved" mode="0600"/>
+               <key basename="passwdWarnBefore" mode="0600" value="7"/>
+
+               <!-- Obsolete key.... from /etc/passwd -->
+               <key basename="password" mode="0644" value="x"/>
+               
+               <!-- Real password goes here in encrypted form -->
+               <key basename="shadowPassword" mode="0600" value="an encrypted passwd should appear here"/>
+       </key>
+
+       
+       <!-- The group which jdoe is member of -->
+       <key basename="groups/guests">
+               <key basename="gid" value="800"/>
+               <key basename="members" value="jdoe,miriam,ana"/>
+       </key>
+       
+
+
+
+
+
+
+       
+
+       <!-- The 'root' record -->
+       
+       <key basename="users/root">
+               <key basename="gecos" value="root"/>
+               <key basename="uid" value="0"/>
+               <key basename="gid" value="0"/>
+               <key basename="home" value="/root"/>
+               <key basename="shell" value="/bin/bash"/>
+               <key basename="passwdChangeAfter" mode="0600" value="99999"/>
+               <key basename="passwdChangeBefore" mode="0600" value="0"/>
+               <key basename="passwdDisableAfter" mode="0600"/>
+               <key basename="passwdDisabledSince" mode="0600"/>
+               <key basename="passwdReserved" mode="0600"/>
+               <key basename="passwdWarnBefore" mode="0600" value="7"/>
+               <key basename="password" mode="0644" value="x"/>
+               <key basename="shadowPassword" mode="0600" value="an encrypted passwd should appear here"/>
+       </key>
+
+
+       <key basename="groups/root">
+               <key basename="gid" value="0"/>
+               <key basename="members" value="root"/>
+       </key>
+
+
+
+       <!-- Just another group that has root as a member -->
+       <key basename="groups/sys">
+               <key basename="gid" value="3"/>
+               <key basename="members" value="root,bin,adm"/>
+       </key>
+       
+
+</keyset>
diff --git a/tests/print_info.c b/tests/print_info.c
new file mode 100644 (file)
index 0000000..d33d22f
--- /dev/null
@@ -0,0 +1,59 @@
+#include <kdb.h>
+
+#include <stdio.h>
+
+void print_time (Key *key)
+{
+       printf ("atime: %lld\n", (long long int)keyGetATime(key));
+       printf ("mtime: %lld\n", (long long int)keyGetMTime(key));
+       printf ("ctime: %lld\n", (long long int)keyGetCTime(key));
+}
+
+void print_constant()
+{
+       printf ("\nFollowing constants must be defined in elektra\n");
+       printf ("The Key Separator is %c\n", KEY_SEPARATOR);
+       printf ("The Path Separator is %c\n", PATH_SEPARATOR);
+       printf ("The default mode for new keys 0%o\n", KEY_DEF_MODE);
+       printf ("The default mode added to directory keys 0%o\n", KEY_DEF_DIR);
+}
+
+void print_sizeof()
+{
+       printf ("\nFollowing types must be defined in elektra\n");
+       printf ("Sizeof (int) is %zd\n", sizeof(int));
+       printf ("Sizeof (uid_t) is %zd\n", sizeof(uid_t));
+       printf ("Sizeof (gid_t) is %zd\n", sizeof(gid_t));
+       printf ("Sizeof (size_t) is %zd\n", sizeof(size_t));
+       printf ("Sizeof (ssize_t) is %zd\n", sizeof(ssize_t));
+       printf ("Sizeof (time_t) is %zd\n", sizeof(time_t));
+
+       printf ("Sizeof (type_t) is %zd\n", sizeof(type_t));
+       printf ("Sizeof (keyswitch_t) is %zd\n", sizeof(keyswitch_t));
+       printf ("Sizeof (option_t) is %zd\n", sizeof(option_t));
+
+       printf ("Sizeof (cursor_t is %zd\n", sizeof(cursor_t));
+
+       printf ("Sizeof (void *) is %zd\n", sizeof(void*));
+       printf ("Sizeof (KDB *) is %zd\n", sizeof(KDB*));
+       printf ("Sizeof (Key *) is %zd\n", sizeof(Key*));
+       printf ("Sizeof (KeySet *) is %zd\n", sizeof(KeySet*));
+}
+
+void print_limits()
+{
+       printf ("\nFollowing limits must be defined in elektra\n");
+       printf ("Maximum Integer is %d\n", INT_MAX);
+       printf ("Minimum Integer is %d\n", INT_MIN);
+       printf ("Maximum size_t is %d\n", SIZE_MAX);
+       printf ("Maximum ssize_t is %d\n", SSIZE_MAX);
+       printf ("Maximum length of a Keyname %d\n", MAX_KEY_LENGTH);
+       printf ("Maximum length of a Path %d\n", MAX_PATH_LENGTH);
+}
+
+int main()
+{
+       print_constant();
+       print_sizeof();
+       print_limits();
+}
diff --git a/tests/test_backendhelpers.c b/tests/test_backendhelpers.c
new file mode 100644 (file)
index 0000000..62f96ba
--- /dev/null
@@ -0,0 +1,269 @@
+/*************************************************************************** 
+ *           test_split.c  - Test suite for splitted keyset data structure
+ *                  -------------------
+ *  begin                : Fri 21 Mar 2008
+ *  copyright            : (C) 2008 by Markus Raab
+ *  email                : elektra@markus-raab.org
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+
+#include <langinfo.h>
+
+#include <tests.h>
+
+/*Needs private declarations*/
+#include <kdbbackend.h>
+
+void test_calc_rel_filename()
+{
+       Key *k;
+       char buffer [MAX_PATH_LENGTH];
+
+       printf ("Test encoding\n");
+
+       k = keyNew ("user/key", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+
+       k = keyNew ("user/key\\", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key%5C") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+
+       k = keyNew ("user/key%", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key%25") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+
+       k = keyNew ("user/key+", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key%2B") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+
+       k = keyNew ("user/key ", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key+") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+
+       k = keyNew ("user/key\\/", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key%5C%2F") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+
+       k = keyNew ("user/key\\/ls\\\\tt", KEY_END);
+       kdbbKeyCalcRelativeFilename (k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "user/key%5C%2Fls%5C%5Ctt") == 0, "did not encode correctly with backslash");
+       keyDel (k);
+}
+
+void test_filename()
+{
+       Key *mnt;
+       KeySet *conf;
+       Key *k = keyNew ("user/key", KEY_OWNER, "max", KEY_END);
+       Key *i = keyNew (KEY_END);
+       char buffer [MAX_PATH_LENGTH];
+
+       printf ("Test filenames\n");
+
+#ifdef HAVE_CLEARENV
+       clearenv();
+#else
+       unsetenv("HOME");
+       unsetenv("KDB_HOME");
+#endif
+
+       KDB *handle = kdbOpen();
+
+       kdbMount (handle, mnt=keyNew("system/users", KEY_VALUE, "filesys", KEY_END),
+               conf=ksNew (0));
+
+       succeed_if (kdbbGetFullFilename(handle, i, buffer, MAX_PATH_LENGTH) == -1, "how did kdbbGetFullFilename found path for invalid key?");
+
+       succeed_if (kdbbGetFullFilename(handle, k, buffer, 5) == -1, "case 1: not even /home/markus fits in buffer");
+
+       succeed_if (kdbbGetFullFilename(handle, k, buffer, 16) == -1, "case 2: kdbbKeyCalcRelativeFilename");
+
+       kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "/home/max/.kdb/user/key") == 0, "step 4: got wrong filename without environment, no users");
+
+
+#ifdef HAVE_SETENV
+       setenv ("HOME","/home/owner",1);
+#else
+       putenv ("HOME=/home/owner");
+#endif
+       kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "/home/owner/.kdb/user/key") == 0, "step 3: got wrong filename with environment HOME");
+
+       kdbSetString (handle, "system/users/max/home", "/usr/homes");
+       kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "/usr/homes/.kdb/user/key") == 0, "step 2b: got wrong filename with home in elektra users db set");
+       kdbRemove(handle, "system/users/max/home");
+
+       kdbSetString (handle, "system/users/max/kdb", "/storage/kdb");
+       kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "/storage/kdb/user/key") == 0, "step 2a: got wrong filename with kdb in elektra users db set");
+       kdbRemove(handle, "system/users/max/kdb");
+
+#ifdef HAVE_SETENV
+       setenv ("KDB_HOME","/home/else",1);
+#else
+       putenv("KDB_HOME=/home/else");
+#endif
+       kdbbGetFullFilename(handle, k, buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "/home/else/.kdb/user/key") == 0, "got wrong filename with environment KDB_HOME");
+
+       keyDel (mnt);
+       ksDel (conf);
+
+       keyDel (k);
+       keyDel (i);
+       kdbClose (handle);
+}
+
+void test_filename_to_keyname()
+{
+       char buffer [MAX_PATH_LENGTH];
+
+       printf ("Test filename to keyname\n");
+
+       kdbbFilenameToKeyName("folder/key", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key+", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key ") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key%5C", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key\\") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key%25", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key%") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key ", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key ") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key\\", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key\\") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key%5C%2F", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key\\/") == 0, "did not encode correctly");
+
+       kdbbFilenameToKeyName("folder/key%5C+", buffer, MAX_PATH_LENGTH);
+       succeed_if (strcmp (buffer, "folder/key\\ ") == 0, "did not encode correctly");
+}
+
+void test_keyname()
+{
+       Key *k = keyNew(0);
+       Key *parentKey = keyNew ("user/absolute/reference", KEY_END);
+       KDB *h = kdbOpen();
+
+       printf ("Test keynames\n");
+
+       kdbbGetFullKeyName(h, "folder/relative/key", parentKey, k);
+       succeed_if (strcmp (keyName(k), "user/absolute/reference/folder/relative/key") == 0, "could not get correct keyname");
+
+       kdbbGetFullKeyName(h, "folder/relative%5C%2Fkey%25+", parentKey, k);
+       succeed_if (strcmp (keyName(k), "user/absolute/reference/folder/relative\\/key% ") == 0, "could not get correct keyname");
+
+       keyDel (parentKey);
+       keyDel (k);
+       kdbClose (h);
+}
+
+void test_utf8_needed()
+{
+#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
+       printf ("Test if utf8 conversation is needed\n");
+
+       printf ("setlocale %s\n",setlocale(LC_CTYPE,""));
+       printf ("langinfo %s\n", nl_langinfo(CODESET));
+       warn_if_fail (kdbbNeedsUTF8Conversion() == 0, "Your default needs conversation, use utf8 to avoid that");
+
+       printf ("setlocale %s\n",setlocale (LC_CTYPE, "C"));
+       printf ("langinfo %s\n", nl_langinfo(CODESET));
+       warn_if_fail (kdbbNeedsUTF8Conversion() != 0, "C needs conversation (you maybe disabled iconv)");
+
+       /*
+       printf ("%s\n",setlocale (LC_CTYPE, "de_AT.utf8"));
+       printf ("%s\n", nl_langinfo(CODESET));
+       succeed_if (kdbbNeedsUTF8Conversion() == 0, "UTF-8 does not need conversation");
+       */
+#else
+       printf ("Dont test utf8 needed, iconv disabled\n");
+       succeed_if (kdbbNeedsUTF8Conversion() == 0, "should never need to convert");
+#endif
+}
+
+void set_str (char **str, size_t *len, char *newstr)
+{
+       *len = strlen (newstr)+1;
+       kdbiRealloc ((void**)str, *len);
+       strcpy (*str, newstr);
+}
+
+void test_utf8_conversation()
+{
+#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
+       char * str = malloc (MAX_PATH_LENGTH);
+       size_t len;
+
+       printf ("Test utf8 conversation\n");
+
+       printf ("setlocale %s\n",setlocale(LC_ALL,"C"));
+
+
+       set_str (&str, &len, "only ascii");
+       succeed_if (kdbbUTF8Engine (UTF8_FROM, &str, &len) != -1, "could not use utf8engine");
+       succeed_if (strcmp ("only ascii", str) == 0, "ascii conversation incorrect");
+
+       /* leads to EILSEQ, means illegal byte sequence */
+       set_str (&str, &len, "Ug.ly:St@ri€n.g Key");
+       succeed_if (kdbbUTF8Engine (UTF8_FROM, &str, &len) == -1, "could use utf8engine");
+       /*succeed_if (errno == EILSEQ, "errno not set correctly");*/
+
+       free (str);
+#else
+       printf ("Dont test utf8 conversations, iconv disabled\n");
+#endif
+}
+
+
+int main()
+{
+       printf("BACKENDHELPERS TESTS\n");
+       printf("====================\n\n");
+
+       test_utf8_needed();
+       test_utf8_conversation();
+       test_calc_rel_filename();
+       test_filename();
+       test_filename_to_keyname();
+       test_keyname();
+
+       printf("\ntest_backendhelpers RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
+
diff --git a/tests/test_cap.c b/tests/test_cap.c
new file mode 100644 (file)
index 0000000..ef393c9
--- /dev/null
@@ -0,0 +1,135 @@
+#include <tests.h>
+
+void test_system()
+{
+       Key * key = keyNew ("system", KEY_END);
+       KDB * handle = kdbOpen();
+       KDBCap *cap=0;
+
+       printf ("Test system capability\n");
+       cap=kdbGetCapability(handle, key);
+
+       exit_if_fail (cap != 0, "could not get capabilities");
+       succeed_if (strcmp (kdbcGetName(cap), "filesys") == 0, "You are not using filesys for system keys");
+       succeed_if (strcmp (kdbcGetVersion(cap), "0.2.1") == 0, "wrong version");
+       succeed_if (strcmp (kdbcGetLicence(cap), "BSD") == 0, "wrong licence");
+       succeed_if (kdbcGetonlyFullGet(cap) == 0, "default backend must support single gets");
+       succeed_if (kdbcGetonlyRemoveAll(cap) == 0, "default backend must support single removes");
+       succeed_if (kdbcGetonlyAddKeys(cap) == 0, "default backend must support adding keys");
+       succeed_if (kdbcGetnoComment(cap) == 0, "default backend must support comments");
+       succeed_if (kdbcGetnoValue(cap) == 0, "default backend must *really* support values");
+       warn_if_fail (kdbcGetnoError(cap) == 0, "What a pity default backend does not support proper error codes");
+
+       kdbClose (handle);
+       keyDel (key);
+}
+
+void test_user()
+{
+       Key * key = keyNew ("user", KEY_END);
+       KDB * handle = kdbOpen();
+       KDBCap *cap=0;
+
+       printf ("Test user capability\n");
+       cap=kdbGetCapability(handle, key);
+
+       exit_if_fail (cap != 0, "could not get capabilities");
+       warn_if_fail (strcmp (kdbcGetName(cap), "filesys") == 0, "You are not using filesys for user keys");
+       warn_if_fail (strcmp (kdbcGetLicence(cap), "BSD") == 0, "Your user keys are not stored in a bsd licenced backend");
+       succeed_if (kdbcGetonlyFullGet(cap) == 0, "user backend must support single gets");
+       succeed_if (kdbcGetonlyRemoveAll(cap) == 0, "user backend must support single removes");
+       succeed_if (kdbcGetonlyAddKeys(cap) == 0, "user backend must support adding keys");
+       succeed_if (kdbcGetnoComment(cap) == 0, "user backend must support comments");
+       succeed_if (kdbcGetnoValue(cap) == 0, "user backend must *really* support values");
+       warn_if_fail (kdbcGetnoError(cap) == 0, "What a pity user backend does not support proper error codes");
+
+       kdbClose (handle);
+       keyDel (key);
+}
+
+void test_fstab()
+{
+       Key * key = keyNew ("system/tests/filesystems", KEY_VALUE, "fstab", KEY_END);
+       KDB * handle = kdbOpen();
+       KDBCap *cap = 0;
+       KeySet * conf;
+
+       printf ("Test fstab capability\n");
+       if (kdbMount (handle, key,
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, "/tmp/fstab", KEY_END), KS_END)) == -1)
+       {
+               ksDel (conf);
+               keyDel (key);
+               kdbClose (handle);
+               printf ("Could not mount backend %s\n", "fstab");
+               printf ("Will not continue with tests on that backend\n");
+               return;
+       }
+       ksDel (conf);
+       cap = kdbGetCapability(handle, key);
+
+       exit_if_fail (cap != 0, "could not get capabilities");
+       succeed_if (strcmp (kdbcGetName(cap), "fstab") == 0, "Not fstab in mounted path");
+       succeed_if (strcmp (kdbcGetVersion(cap), "0.0.1") == 0, "wrong version");
+       succeed_if (strcmp (kdbcGetLicence(cap), "BSD") == 0, "wrong licence");
+       succeed_if (kdbcGetonlyFullGet(cap) == 1, "fstab only full get");
+       succeed_if (kdbcGetonlyRemoveAll(cap) == 1, "fstab only remove all");
+       succeed_if (kdbcGetonlyAddKeys(cap) == 1, "fstab only add keys");
+       succeed_if (kdbcGetnoComment(cap) == 1, "fstab does not support comments");
+       succeed_if (kdbcGetnoError(cap) == 1, "fstab does not return correct error values");
+
+       kdbClose (handle);
+       keyDel (key);
+}
+
+void test_hosts()
+{
+       Key * key = keyNew ("system/tests/hosts", KEY_VALUE, "hosts", KEY_END);
+       KDB * handle = kdbOpen();
+       KDBCap *cap = 0;
+       KeySet * conf;
+
+       printf ("Test hosts capability\n");
+       if (kdbMount (handle, key,
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, "/tmp/hosts", KEY_END), KS_END)) == -1)
+       {
+               ksDel (conf);
+               keyDel (key);
+               kdbClose (handle);
+               printf ("Could not mount backend %s\n", "hosts");
+               printf ("Will not continue with tests on that backend\n");
+               return;
+       }
+       ksDel (conf);
+       cap = kdbGetCapability(handle, key);
+
+       exit_if_fail (cap != 0, "could not get capabilities");
+       succeed_if (strcmp (kdbcGetName(cap), "hosts") == 0, "Not hosts in mounted path");
+       succeed_if (strcmp (kdbcGetVersion(cap), "0.0.2") == 0, "wrong version");
+       succeed_if (strcmp (kdbcGetLicence(cap), "BSD") == 0, "wrong licence");
+       succeed_if (kdbcGetonlyFullGet(cap) == 1, "hosts only full get");
+       succeed_if (kdbcGetonlyRemoveAll(cap) == 1, "hosts only remove all");
+       succeed_if (kdbcGetonlyAddKeys(cap) == 1, "hosts only add keys");
+       succeed_if (kdbcGetnoComment(cap) == 1, "hosts does not support comments");
+       succeed_if (kdbcGetnoError(cap) == 1, "hosts does not return correct error values");
+
+       kdbClose (handle);
+       keyDel (key);
+}
+
+int main()
+{
+       printf("ELEKTRA CAPABILTIY TEST SUITE\n");
+       printf("========================================\n\n");
+
+       init ();
+
+       test_system();
+       test_user();
+
+       test_fstab();
+       test_hosts();
+
+       printf("\ntest_cap RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+       return nbError;
+}
diff --git a/tests/test_getset.c b/tests/test_getset.c
new file mode 100644 (file)
index 0000000..9193580
--- /dev/null
@@ -0,0 +1,707 @@
+/***************************************************************************
+ *          test_setget.c  -  setting and getting test suite
+ *                -------------------
+ *  begin                : Wed Aug 26 2006
+ *  copyright            : (C) 2006 by Patrick Sabin
+ *  email                : patricksabin@gmx.at
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <tests.h>
+
+void test_subdir()
+{
+       KDB *kdb;
+       Key *key,*key1,*key2;
+       KeySet *ks;
+       KeySet *returned;
+       int key1_found;
+       const char* name;
+       Key *parent;
+
+       printf("testing subdir\n");
+
+       kdb=kdbOpen();
+       exit_if_fail(kdb,"kdbOpen() failed");
+
+       ks=ksNew(0);
+       exit_if_fail(ks,"ksNew(0) failed");
+
+       /* Test creating two keys */
+       key=keyNew(KEY_ROOT "/test_d", KEY_DIR, KEY_VALUE, "dirkey", KEY_COMMENT, "dircomment", KEY_END);
+       succeed_if(key!=NULL, "keyNew: Unable to create a new key: user/test");
+       succeed_if(keySetString(key,"test value")!=0, "keySetString: Cannot set value");
+       succeed_if(keySetComment(key,"empty")!=0, "keySetComment: Cannot set the key Comment");
+       ksAppendKey(ks,key);
+
+       key=keyNew(KEY_ROOT "/test_d/test", KEY_END);
+       succeed_if(key!=NULL, "keyNew: Unable to create a new key: user/test/test");
+       succeed_if(keySetString(key,"test value2")!=0, "keySetString: Cannot set value");
+       succeed_if(keySetComment(key,"empty")!=0, "keySetComment: Cannot set the key Comment");
+       ksAppendKey(ks,key);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "kdbSet failed");
+
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+       exit_if_fail((kdb=kdbOpen())!=NULL,"Could not open elektra");
+       
+       // read the directory key
+
+       succeed_if(ksDel(ks)==0,"Could not close keySet");
+       ks=ksNew(0);
+
+       returned=ksNew(0);
+       parent=keyNew(KEY_ROOT "/",KEY_END);
+       succeed_if (kdbGet(kdb,returned,parent,0)!=-1, "kdbGet failed");
+
+       key1_found=0;
+       for (key=ksNext(returned);key;key=ksNext(returned)) {
+               name=keyName(key);
+               if (!strcmp(name, KEY_ROOT "/test_d")) {
+                       key1_found=1;
+                       /*
+                       output_key(key);
+                       */
+                       succeed_if(!strcmp(keyValue(key),"test value"),"value of directory key not correct");
+                       succeed_if(!strcmp(keyComment(key),"empty"),"comment of directory key not correct");
+               }
+       }
+       succeed_if(key1_found, KEY_ROOT "/test1 not found");
+       ksDel(returned);
+       keyDel(parent);
+
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+       exit_if_fail((kdb=kdbOpen())!=NULL,"Could not open elektra");
+
+       // user/test and user/test/test keys are written. Now overwrite them
+
+       key1=keyNew(KEY_ROOT "/test_d", KEY_DIR,  KEY_END);
+       succeed_if(key1!=NULL, "keyNew: Unable to create a new key: user/test");
+       succeed_if(keySetString(key1,"test value")!=0, "keySetString: Cannot set value");
+       succeed_if(keySetComment(key1,"empty")!=0, "keySetComment: Cannot set the key Comment");
+       ksAppendKey(ks,key1);
+
+       key2=keyNew(KEY_ROOT "/test_d/test", KEY_END);
+       succeed_if(key2!=NULL, "keyNew: Unable to create a new key: user/test/test");
+       succeed_if(keySetString(key2,"test value2")!=0, "keySetString: Cannot set value");
+       succeed_if(keySetComment(key2,"empty")!=0, "keySetComment: Cannot set the key Comment");
+       ksAppendKey(ks,key2);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "kdbSet failed");
+
+       keyRemove (key2);
+       succeed_if(kdbSetKey(kdb,key2)==0,"Cannot remove key2");
+       keyRemove (key1);
+       succeed_if(kdbSetKey(kdb,key1)==0,"Cannot remove key1");
+       succeed_if(ksDel(ks)==0,"Could not close keySet");
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+}
+
+void test_writeread()
+{
+       KDB *kdb;
+       Key *parent,*key,*key1,*key2;
+       KeySet *ks;
+       KeySet *returned;
+       const char *name;
+       int key1_found,key2_found;
+       //char buffer[1024];
+
+       printf("testing write/read\n");
+       kdb=kdbOpen();
+       exit_if_fail(kdb,"kdbOpen() failed");
+
+       ks=ksNew(0);
+       exit_if_fail(ks,"ksNew(0) failed");
+
+       /* Test creating two keys */
+       key1=keyNew(KEY_ROOT "/test1", KEY_VALUE,"value1", KEY_END);
+       succeed_if(key1!=NULL, "keyNew: Unable to create a new key: user/test");
+       ksAppendKey(ks,key1);
+
+       key2=keyNew(KEY_ROOT "/test2", KEY_VALUE,"value2",KEY_COMMENT,"comment2",KEY_END);
+       succeed_if(key2!=NULL, "keyNew: Unable to create a new key: user/test2");
+       ksAppendKey(ks,key2);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "kdbSet failed");
+       succeed_if(ksDel(ks)==0,"Could not close keySet");
+
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+       kdb=kdbOpen();
+
+       /* Test reading the written keys */
+
+       returned=ksNew(0);
+       parent=keyNew(KEY_ROOT "/",KEY_END);
+
+       succeed_if (kdbGet(kdb,returned,parent,0)!=-1, "could not get keys");
+
+       key1_found=key2_found=0;
+       for (key=ksNext(returned);key;key=ksNext(returned)) {
+               name=keyName(key);
+               if (!strcmp(name, KEY_ROOT "/test1")) {
+                       key1_found=1;
+                       /*
+                       output_key(key);
+                       */
+                       succeed_if(!strcmp(keyValue(key),"value1"),"value of first written key not correct");
+                       succeed_if(!strcmp(keyComment(key),""),"comment of first written key not correct");
+               }
+               if (!strcmp(name, KEY_ROOT "/test2")) {
+                       key2_found=1;
+                       /*
+                       output_key(key);
+                       */
+                       succeed_if(!strcmp(keyValue(key),"value2"),"value of second written key not correct");
+                       succeed_if(!strcmp(keyComment(key),"comment2"),"value of second written key not correct");
+               }
+       }
+       succeed_if(key1_found, KEY_ROOT "/test1 not found");
+       succeed_if(key2_found, KEY_ROOT "/test2 not found");
+       ksDel(returned);
+       keyDel(parent);
+
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+       exit_if_fail((kdb=kdbOpen())!=NULL,"Could not open elektra");
+
+       /* Test overwriting the keys */
+
+       ks=ksNew(0);
+       exit_if_fail(ks,"ksNew(0) failed");
+
+       key1=keyNew(KEY_ROOT "/test1", KEY_VALUE,"value_new1",KEY_COMMENT,"comment1",KEY_END);
+       succeed_if(key1!=NULL, "keyNew: Unable to create a new key: user/test");
+       ksAppendKey(ks,key1);
+
+       key2=keyNew(KEY_ROOT "/test2", KEY_VALUE,"value_new2",KEY_END);
+       succeed_if(key2!=NULL, "keyNew: Unable to create a new key: user/test/test");
+       ksAppendKey(ks,key2);
+
+       succeed_if(kdbSet(kdb,ks,0,0)!=-1, "kdbSet failed");
+
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+       exit_if_fail((kdb=kdbOpen())!=NULL,"Could not open elektra");
+
+       /* Test reading the overwritten keys */
+
+       returned=ksNew(0);
+       parent=keyNew(KEY_ROOT "/",KEY_END);
+
+       succeed_if (kdbGet(kdb,returned,parent,0)!=-1, "could not kdbGet keys");
+       succeed_if (keyDel (parent) == 0, "Could not delete parent key");
+
+       key1_found=key2_found=0;
+       ksRewind(returned);
+       while ((key=ksNext(returned))!=0)
+       {
+               name=keyName(key);
+               if (!strcmp(name, KEY_ROOT "/test1")) {
+                       key1_found=1;
+                       succeed_if(!strcmp(keyValue(key),"value_new1"),"value of first overwritten key not correct");
+                       succeed_if(!strcmp(keyComment(key),"comment1"),"comment of first overwritten key not correct");
+               }
+               if (!strcmp(name, KEY_ROOT "/test2")) {
+                       key2_found=1;
+                       succeed_if(!strcmp(keyValue(key),"value_new2"),"value of second overwritten key not correct");
+                       succeed_if(!strcmp(keyComment(key),""),"comment of second overwritten key not correct");
+               }
+       }
+       succeed_if(key1_found, KEY_ROOT "/test1 not found");
+       succeed_if(key2_found, KEY_ROOT "/test2 not found");
+       ksDel(returned);
+
+       keyRemove (key2);
+       succeed_if(kdbSetKey(kdb,key2)==0,"Cannot remove key2");
+       keyRemove (key1);
+       succeed_if(kdbSetKey(kdb,key1)==0,"Cannot remove key1");
+       succeed_if(ksDel(ks)==0,"Could not close keySet");
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+}
+
+void test_mode()
+{
+       KDB *kdb;
+       Key *key,*key1,*key2,*parent;
+       KeySet *returned,*ks;
+       int key1_found;
+       int ret;
+       int mode, setMode;
+
+       printf("testing mode\n");
+       kdb=kdbOpen();
+       exit_if_fail(kdb,"kdbOpen() failed");
+
+       //TODO: mode 0..5
+       for (mode = 6; mode < 8; mode++)
+       {
+               setMode = mode | mode << 3 | mode << 6;
+               printf ("Testing mode: %o\n", setMode);
+               
+               exit_if_fail(ks=ksNew(0),"ksNew(0) failed");
+               /* Create a key with permission setMode */
+               key=keyNew(KEY_ROOT "/mode", KEY_MODE, setMode, KEY_VALUE,"value1",
+                               KEY_COMMENT,"comment1",KEY_END);
+               succeed_if(key!=NULL, "keyNew: Unable to create a new key: user/test");
+               succeed_if(keyGetMode(key) == setMode, "Could not set mode");
+               ksAppendKey(ks,key);
+               /*errno = KDB_ERR_OK;*/
+               ret = kdbSet(kdb,ks,0,0);
+               if (mode & MODE_WRITE)
+               {
+                       // succeed_if (ret == 0, "could not write key");
+               } else {
+                       succeed_if (ret == -1, "wrote key without write mode?");
+                       /*succeed_if (errno == KDB_ERR_NOCRED, "Errno not set correctly");*/
+                       // TODO: exit_if_fail (ksCurrent(ks), "no current key, where did it fail?");
+                       // succeed_if (strcmp (keyName(ksCurrent(ks)), KEY_ROOT "/mode") == 0, "did stop at wrong key");
+                       // succeed_if (strcmp (keyValue(ksCurrent(ks)), "value1") == 0, "wrong value");
+                       // succeed_if (strcmp (keyComment(ksCurrent(ks)), "comment1") == 0, "wrong comment");
+               }
+               ksDel (ks);
+
+               returned=ksNew(0);
+               parent=keyNew(KEY_ROOT, KEY_END);
+               ret = kdbGet(kdb,returned,parent,0);
+               keyDel(parent);
+
+               key1_found=0;
+
+               ksRewind(returned);
+               while ((key=ksNext(returned))!=0)
+               {
+                       if (!strcmp(keyName(key), KEY_ROOT "/mode")) {
+                               key1_found=1;
+                               // succeed_if(keyGetMode(key) == setMode, "Mode not correct after get");
+                               if ((mode & MODE_READ) && (mode & MODE_WRITE))
+                               {
+                                       succeed_if(strcmp (keyValue (key), "value1") == 0,
+                                                       "Could not read value, but should be allowed to");
+                                       succeed_if(strcmp (keyComment (key), "comment1") == 0,
+                                                       "Could not read comment, but should be allowed to");
+                               }
+                               else if (mode & MODE_WRITE)
+                               {
+                                       succeed_if(!strcmp (keyValue (key), "value1") == 0,
+                                                       "Could read value, but should not be allowed to");
+                                       succeed_if(strcmp (keyValue (key), "") == 0,
+                                                       "Value not empty, but write should have failed");
+                                       succeed_if(!strcmp (keyComment (key), "comment1") == 0,
+                                                       "Could read comment, but should not be allowed to");
+                                       succeed_if(strcmp (keyComment (key), "") == 0,
+                                                       "Comment not empty, but write should have failed");
+                               }
+                               else if (mode & MODE_READ)
+                               {
+                                       succeed_if(!strcmp (keyValue (key), "value1") == 0,
+                                                       "Could read value, but should not be allowed to");
+                                       succeed_if(strcmp (keyValue (key), "") == 0,
+                                                       "Value not empty after not allowed read");
+                                       succeed_if(!strcmp (keyComment (key), "comment1") == 0,
+                                                       "Could read comment, but should not be allowed to");
+                                       succeed_if(strcmp (keyComment (key), "") == 0,
+                                                       "Comment not empty after not allowed read");
+                               }
+                       }
+               }
+
+               succeed_if(key1_found, KEY_ROOT "/test1 key not found");
+               ksDel(returned);
+
+               /* Overwrite the key*/
+               // TODO: copy code from above
+
+               
+               /* Create a sub key for /mode */
+               exit_if_fail(ks=ksNew(0), "ksNew(0) failed");
+               key2=keyNew(KEY_ROOT "/mode/key", KEY_VALUE,"value_sub",KEY_COMMENT,"comment_sub",KEY_END);
+               succeed_if(key2!=NULL, "keyNew: Unable to create a new subkey");
+               ksAppendKey(ks,key2);
+
+               if (kdbSet(kdb,ks,0,0)==-1) {
+                       if (!(mode & 1)) // not a directory, can't write subkey
+                       {
+                               succeed_if(0,"kdbSet failed");
+                       }
+               }
+               ksDel(ks);
+
+               /* Overwrite the directory key if there is already a subkey */
+               ks=ksNew(0);
+               key1=keyNew(KEY_ROOT "/mode/key", KEY_VALUE,"value_new",KEY_COMMENT,"comment_new",KEY_END);
+               key2=keyNew(KEY_ROOT "/mode", KEY_VALUE,"value_new",KEY_COMMENT,"comment_new",KEY_END);
+               ksAppendKey(ks,key1);
+               ksAppendKey(ks,key2);
+
+               if (kdbSet(kdb,ks,0,0)==-1)
+               {
+                       // succeed_if(errno==KDB_ERR_NOTEMPTY, "kdbSet delete nonempty key KDB_ERR_NOTEMPTY expected");
+                       // TODO exit_if_fail (ksCurrent(ks) != 0, "key is Null but should be set to incorrect key");
+                       // printf("trapped: %s\n",keyName(ksCurrent(ks)));
+                       // succeed_if (!strcmp(KEY_ROOT "/mode",keyName(ksCurrent(ks))), "kdbSet messed up setting ksCurrent()");
+               } else {
+                       /*
+                       if (!(mode & 1)) // is not directory key
+                       {
+                               succeed_if(0,"Could overwrite a directory with subkeys");
+                       }
+                       */
+               }
+
+               /* Cleanup */
+               succeed_if (kdbRemove (kdb, KEY_ROOT "/mode/key") == 0, "Could not remove mode/key");
+               succeed_if (kdbRemove (kdb, KEY_ROOT "/mode") == 0, "Could not remove mode");
+               succeed_if(ksDel(ks)==0,"Could not close keySet");
+       }
+       exit_if_fail(kdbClose(kdb)==0,"Could not close libelektra");
+
+
+       return;
+}
+
+/* This tests the lowest-level way to set a key and get it afterwards */
+void test_simple_getset()
+{
+       KDB *kdb;
+       Key *key,*key1;
+       KeySet *ks;
+       KeySet *returned;
+
+       printf("testing simple write/read\n");
+
+       // create key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/test1", KEY_VALUE,"value", KEY_COMMENT,"comment", KEY_END);
+       ksAppendKey(ks,key1);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       // get key test1
+       kdb=kdbOpen();
+       key=keyNew(KEY_ROOT, KEY_END);
+       returned = ksNew(0);
+       if (kdbGet(kdb,returned,key,0)==-1) {
+               succeed_if(0,"kdbGet failed");
+       }
+       keyDel(key);
+       key1 = ksLookupByName (returned, KEY_ROOT "/test1", 0);
+       exit_if_fail (key1 != 0, "could not lookup key");
+       succeed_if (strcmp (keyValue (key1), "value") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "comment") == 0, "Comment not correct");
+       ksDel (returned);
+       kdbClose(kdb);
+
+       // delete key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/test1", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       return;
+}
+
+/* This tests the highlevel-level way to set a key and get it afterwards */
+void test_highlevel_getset()
+{
+       KDB *kdb;
+       Key *key1;
+
+       printf("testing simple write/read\n");
+
+       // create key test3
+       kdb=kdbOpen();
+       key1=keyNew(KEY_ROOT "/test3", KEY_VALUE,"value", KEY_COMMENT,"comment", KEY_END);
+       succeed_if (kdbSetKey(kdb,key1)!=-1, "could not kdbSet keys");
+       keyDel (key1);
+       kdbClose(kdb);
+
+       // get key test3
+       kdb=kdbOpen();
+       key1=keyNew(KEY_ROOT "/test3", KEY_END);
+       if (kdbGetKey(kdb,key1)==-1) {
+               succeed_if(0,"kdbGetKey failed");
+       }
+       succeed_if (strcmp (keyValue (key1), "value") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "comment") == 0, "Comment not correct");
+       keyDel (key1);
+       kdbClose(kdb);
+
+       // delete key test3
+       kdb=kdbOpen();
+       key1=keyNew(KEY_ROOT "/test3", KEY_REMOVE, KEY_END);
+       succeed_if (kdbSetKey (kdb,key1)==0, "could not get a key");
+       keyDel (key1);
+       kdbClose(kdb);
+
+       return;
+}
+
+/* This tests do recursive set and get keys */
+void test_simple_recursive_getset()
+{
+       KDB *kdb;
+       Key *key,*key1;
+       KeySet *ks;
+       KeySet *returned;
+
+       printf("testing recursive write/read\n");
+
+       // create key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/dir", KEY_DIR, KEY_VALUE,"dirvalue", KEY_COMMENT,"dircomment", KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir/key", KEY_VALUE,"value", KEY_COMMENT,"comment", KEY_END);
+       ksAppendKey(ks,key1);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       // get key test1
+       kdb=kdbOpen();
+       key=keyNew(KEY_ROOT, KEY_END);
+       returned = ksNew(0);
+       if (kdbGet(kdb,returned,key,0)==-1) {
+               succeed_if(0,"kdbGet failed");
+       }
+       keyDel(key);
+       key1 = ksLookupByName (returned, KEY_ROOT "/dir/key", 0);
+       exit_if_fail (key1 != 0, "could not lookup key");
+       succeed_if (strcmp (keyValue (key1), "value") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "comment") == 0, "Comment not correct");
+       
+       key1 = ksLookupByName (returned, KEY_ROOT "/dir", 0);
+       exit_if_fail (key1 != 0, "could not lookup key");
+       succeed_if (strcmp (keyValue (key1), "dirvalue") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "dircomment") == 0, "Comment not correct");
+       ksDel (returned);
+       kdbClose(kdb);
+
+       // delete key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/dir/key", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir", KEY_DIR, KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       return;
+}
+
+/* This tests do recursive set and get keys */
+void test_recursive_getset()
+{
+       KDB *kdb;
+       Key *key,*key1;
+       KeySet *ks;
+       KeySet *returned;
+       int found [5];
+       int i;
+
+       for (i=0; i<5; i++) found[i] = 0;
+
+       printf("testing recursive write/read\n");
+
+       // create key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/key1", KEY_VALUE,"value1", KEY_COMMENT,"comment", KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir2", KEY_DIR, KEY_VALUE,"dirvalue", KEY_COMMENT,"dircomment", KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir1", KEY_DIR, KEY_VALUE,"dirvalue", KEY_COMMENT,"dircomment", KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir1/key2", KEY_VALUE,"value2", KEY_COMMENT,"comment", KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir1/key1", KEY_VALUE,"value1", KEY_COMMENT,"comment", KEY_END);
+       ksAppendKey(ks,key1);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       // get key test1
+       kdb=kdbOpen();
+       key=keyNew(KEY_ROOT, KEY_END);
+       returned = ksNew(0);
+       if (kdbGet(kdb,returned,key,0)==-1) {
+               succeed_if(0,"kdbGet failed");
+       }
+       keyDel(key);
+
+       // check if all keys are in set and exactly once
+       // output_keyset(returned, 0);
+       ksRewind(returned);
+       while ((key=ksNext(returned))!=0)
+       {
+               if (strcmp (keyName(key), KEY_ROOT "/key1") == 0) found [0]++;
+               else if (strcmp (keyName(key), KEY_ROOT "/dir2") == 0) found [1]++;
+               else if (strcmp (keyName(key), KEY_ROOT "/dir1") == 0) found [2]++;
+               else if (strcmp (keyName(key), KEY_ROOT "/dir1/key2") == 0) found [3]++;
+               else if (strcmp (keyName(key), KEY_ROOT "/dir1/key1") == 0) found [4]++;
+       }
+
+       for (i=0; i<5;i++)
+       {
+               // printf("%d", found[i]);
+               succeed_if (found[i] == 1, "key did not appear exactly once");
+       }
+
+
+       key1 = ksLookupByName (returned, KEY_ROOT "/dir1/key1", 0);
+       exit_if_fail (key1 != 0, "could not lookup key");
+       succeed_if (strcmp (keyValue (key1), "value1") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "comment") == 0, "Comment not correct");
+       
+       key1 = ksLookupByName (returned, KEY_ROOT "/dir1", 0);
+       exit_if_fail (key1 != 0, "could not lookup key");
+       succeed_if (strcmp (keyValue (key1), "dirvalue") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "dircomment") == 0, "Comment not correct");
+       ksDel (returned);
+       kdbClose(kdb);
+
+       // delete key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/dir1/key1", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir1/key2", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir1", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/dir2", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       key1=keyNew(KEY_ROOT "/key1", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       return;
+}
+
+/* This tests the lowest-level way to set a directory key and get it afterwards */
+void test_directory_getset()
+{
+       KDB *kdb;
+       Key *key,*key1;
+       KeySet *ks;
+       KeySet *returned;
+
+       printf("testing simple write/read\n");
+
+       // create key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/dir4", KEY_DIR, KEY_VALUE,"value4", KEY_COMMENT,"comment4", KEY_END);
+       ksAppendKey(ks,key1);
+
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       // get key test1
+       kdb=kdbOpen();
+       key=keyNew(KEY_ROOT, KEY_END);
+       returned = ksNew(0);
+       if (kdbGet(kdb,returned,key,0)==-1) {
+               succeed_if(0,"kdbGet failed");
+       }
+       keyDel(key);
+       key1 = ksLookupByName (returned, KEY_ROOT "/dir4", 0);
+       succeed_if (strcmp (keyValue (key1), "value4") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "comment4") == 0, "Comment not correct");
+       succeed_if (keyIsDir(key1) , "not a directory");
+       ksDel (returned);
+       kdbClose(kdb);
+
+       // delete key test1
+       kdb=kdbOpen();
+       ks=ksNew(0);
+       key1=keyNew(KEY_ROOT "/dir4", KEY_REMOVE, KEY_END);
+       ksAppendKey(ks,key1);
+       succeed_if (kdbSet(kdb,ks,0,0)!=-1, "could not kdbSet keys");
+       ksDel(ks);
+       kdbClose(kdb);
+
+       return;
+}
+
+/* This tests the highlevel-level way to set a directory and get it afterwards */
+void test_directory_highlevel_getset()
+{
+       KDB *kdb;
+       Key *key1;
+
+       printf("testing simple write/read\n");
+
+       // create key test3
+       kdb=kdbOpen();
+       key1=keyNew(KEY_ROOT "/dir5", KEY_DIR, KEY_VALUE,"value5", KEY_COMMENT,"comment5", KEY_END);
+       succeed_if (kdbSetKey(kdb,key1)!=-1, "could not kdbSet a key");
+       keyDel (key1);
+       kdbClose(kdb);
+
+       // get key test3
+       kdb=kdbOpen();
+       key1=keyNew(KEY_ROOT "/dir5", KEY_END);
+       if (kdbGetKey(kdb,key1)==-1) {
+               succeed_if(0,"kdbGetKey failed");
+       }
+       succeed_if (strcmp (keyValue (key1), "value5") == 0, "Value not correct");
+       succeed_if (strcmp (keyComment(key1), "comment5") == 0, "Comment not correct");
+       succeed_if (keyIsDir(key1) , "not a directory");
+       keyDel (key1);
+       kdbClose(kdb);
+
+       // delete key test3
+       kdb=kdbOpen();
+       key1=keyNew(KEY_ROOT "/dir5", KEY_REMOVE, KEY_END);
+       succeed_if (kdbSetKey (kdb,key1)!=-1, "could not set a key");
+       keyDel (key1);
+       kdbClose(kdb);
+
+       return;
+}
+
+int main()
+{
+       printf("\n\n");
+       printf("ELEKTRA SET/GET TEST SUITE\n");
+       printf("========================================\n\n");
+
+       exit_if_fail (loadToolsLib()==0, "Unable to load elektratools");
+
+       init ();
+
+       test_simple_getset();
+       test_writeread();
+       test_subdir();
+       test_mode();
+       test_highlevel_getset();
+       test_simple_recursive_getset();
+       test_recursive_getset();
+       test_directory_getset();
+       test_directory_highlevel_getset();
+
+       printf("\ntest_getset RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+       
+       return nbError;
+}
+
diff --git a/tests/test_internals.c b/tests/test_internals.c
new file mode 100644 (file)
index 0000000..32a7ce0
--- /dev/null
@@ -0,0 +1,74 @@
+/*************************************************************************** 
+ *           test_split.c  - Test suite for splitted keyset data structure
+ *                  -------------------
+ *  begin                : Fri 21 Mar 2008
+ *  copyright            : (C) 2008 by Markus Raab
+ *  email                : elektra@markus-raab.org
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+/*Needs private declarations*/
+#include <kdbbackend.h>
+
+void test_strlen ()
+{
+       wchar_t multicharSeq [5];
+
+       printf ("Test kdbiStrLen\n");
+       multicharSeq [0] = '\323';
+       multicharSeq [1] = L'a';
+       multicharSeq [2] = L'\20';
+       multicharSeq [3] = L'\40';
+       multicharSeq [4] = L'\0';
+
+       // printf ("%s %d %d\n", multicharSeq, kdbiStrLen (multicharSeq), strlen(multicharSeq));
+       succeed_if(kdbiStrLen ((char*)multicharSeq) == 6, "could not deduce correct multichar sequence length");
+}
+
+int test_load_toolslib()
+{
+       KeySet          *ks = ksNew (0);
+
+       exit_if_fail (loadToolsLib()==0, "Unable to load elektratools");
+       exit_if_fail( ksFromXMLfile(ks, "key.xml") == 0, "ksFromXMLfile(key.xml) failed.");
+       ksGenerate (ks, stdout, KDB_O_HEADER);
+
+       ksDel (ks);
+       return 0;
+}
+
+int main()
+{
+       printf("INTERNALS    TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_strlen();
+       // test_load_toolslib();
+
+       printf("\ntest_internals RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
+
diff --git a/tests/test_kdb.c b/tests/test_kdb.c
new file mode 100644 (file)
index 0000000..e2bbd47
--- /dev/null
@@ -0,0 +1,484 @@
+/***************************************************************************
+ *          test_kdb.c  -  Backend test suite
+ *                -------------------
+ *  begin                : Thu Aug 03 2006
+ *  copyright            : (C) 2006 by Yannick Lecaillez
+ *  email                : sizon5@gmail.com
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <tests.h>
+
+/* Remove all keys below and with root.
+ *
+ * Will remove but not delete the root element!
+ *
+ * */
+void remove_keys (KDB *handle, Key *root)
+{
+       KeySet *ks;
+
+       ks = ksNew(0);
+       succeed_if (kdbGet (handle, ks, root, KDB_O_INACTIVE) > 0, "could not get keys below root key");
+       ksRewind (ks);
+       succeed_if (kdbSet (handle, ks, root,KDB_O_REMOVEONLY) != -1, "could not remove root");
+       ksDel (ks);
+}
+
+int test_keyCompare(Key *key1, Key *key2, unsigned int ignoreFlags)
+{
+       int     ret;
+       int     err = nbError;
+
+       ret = keyCompare(key1, key2);
+       if ( (ret & ignoreFlags) != ret ) {
+               if ( !(ignoreFlags & KEY_TYPE) )        succeed_if ( (ret & KEY_TYPE) == 0,     "Key type isn't same");
+               if ( !(ignoreFlags & KEY_NAME) )        succeed_if ( (ret & KEY_NAME) == 0,     "Key name isn't same");
+               if ( !(ignoreFlags & KEY_VALUE))        succeed_if ( (ret & KEY_VALUE) == 0,    "Key value isn't same");
+               if ( !(ignoreFlags & KEY_OWNER))        succeed_if ( (ret & KEY_OWNER) == 0,    "Key owner isn't same");
+               if ( !(ignoreFlags & KEY_COMMENT))      succeed_if ( (ret & KEY_COMMENT) == 0,  "Key comment isn't same");
+               if ( !(ignoreFlags & KEY_UID) )         succeed_if ( (ret & KEY_UID) == 0,      "Key UID isn't name");
+               if ( !(ignoreFlags & KEY_GID) )         succeed_if ( (ret & KEY_GID) == 0,      "Key GID isn't name");
+               if ( !(ignoreFlags & KEY_MODE)) succeed_if ( (ret & KEY_MODE) == 0,     "Key mode isn't name");
+       }
+
+       return nbError-err;
+}
+
+/* Test kdbGet()/kdbSet()
+ * ----------------------------
+ *
+ * kdbSetKey the supplied key. Then kdbGetKey it
+ * Compare these two.
+ *
+ * NOTE: The tested key isn't kdbRemoved().
+ *
+ * return 0 if succeed, otherwise number of errors
+ */
+int testcase_kdbSetKeyAndkdbGetKey(KDB *handle, Key *key)
+{
+       Key     *check;
+       int     err = nbError;
+       int     ignoreFlags = 0;
+
+       // printf("\t* testing kdbSetKey()/kdbGetKey()\n");
+
+       check = keyDup(key);
+       succeed_if( kdbSetKey(handle, key) == 0, "kdbSetKey failed.");
+       if ( !err ) {
+               succeed_if( kdbGetKey(handle, check) == 0, "kdbGetKey failed.");
+               test_keyCompare(check, key, ignoreFlags);
+       }
+
+       keyDel(check);
+
+       return nbError-err;
+}
+
+/* Test kdbStatKey()
+ * -----------------
+ *
+ * kdbGetKey() then kdbStatKey() the supplied key. 
+ * Compare the "STATed" version against the getted version.
+ *
+ * NOTE: The tested key must exist.
+ */
+int testcase_kdbStatKey(KDB *handle, Key *k)
+{
+       Key     *tmp;
+       Key     *key = keyDup (k);
+
+       int err = nbError;
+
+
+       /* printf("\t* testing kdbStatKey()\n"); */
+       
+       tmp = keyNew(keyName(key), KEY_END);
+
+       succeed_if( kdbGetKey(handle, key) == 0, "kdbGetKey failed.");
+
+       keyStat (key);
+       succeed_if( kdbGetKey(handle, tmp) == 0, "kdbStatKey failed.");
+       succeed_if( (keyGetUID(tmp) == keyGetUID(key)), "kdbStatKey returned wrong UID.");
+       succeed_if( (keyGetGID(tmp) == keyGetGID(key)), "kdbStatKey returned wrong GID.");
+
+       succeed_if( (keyGetMode(tmp) == keyGetMode(key)), "kdbStatKey returned wrong Mode.");
+
+       succeed_if( (keyGetATime(tmp) >= keyGetATime(key)), "kdbStatKey returned wrong mode time.");
+       succeed_if( (keyGetMTime(tmp) == keyGetMTime(key)), "kdbStatKey returned wrong modification time.");
+       succeed_if( (keyGetCTime(tmp) == keyGetCTime(key)), "kdbStatKey returned wrong last change time.");
+
+       keyDel(tmp);
+       
+       keyDel (key);
+
+       return nbError-err;
+}
+
+/*
+ * Renames the key to a given newname in backend in an atomic way.
+ *
+ * @note the key will not exist afterwards in database
+ *
+ * @param handle contains internal information of @link kdbOpen() opened @endlink key database
+ * @param key an initialized Key retrieved from backend
+ * @param newname the new name which the name should have in backend
+ * @return 0 on success
+ * @return -1 on failure and @c errno is propagated
+ * @see kdbSet()
+ * @ingroup kdb
+ */
+int kdbRename(KDB *handle, const Key *key, const char *newname) {
+       KeySet * ks = ksNew(0);
+       Key * toRename = keyDup(key);
+       Key * toRemove = keyDup(key);
+       keyRemove (toRemove);
+       ksAppendKey(ks, toRemove);
+
+       if (keySetName (toRename, newname) == -1)
+       {
+               ksDel (ks);
+               return -1;
+       }
+       ksAppendKey(ks, toRename);
+
+       if (kdbSet (handle, ks,0,0) == -1)
+       {
+#if DEBUG
+               printf ("kdbRename: kdbSet failed\n");
+#endif
+               ksDel (ks);
+               return -1;
+       }
+
+       ksDel (ks);
+       return 0;
+}
+
+/* Test kdbRename()
+ * ----------------
+ *
+ * Rename the tested key to <keyname>-renamed.
+ * Then check if <keyname> is removed & <keyname>-renamed
+ * exists.
+ *
+ * NOTE: The tested key must be set.
+ * At the end, the key is renamed to its original name,
+ * thus only the original will be there after test.
+ */
+int testcase_kdbRename(KDB *handle, Key *k)
+{
+       Key     *tmp;
+       char    buf[KEY_LENGTH];
+
+       Key     *key=keyDup(k);
+       
+       int err = nbError;
+
+       snprintf(buf, sizeof(buf), "%s-renamed", keyName(key));
+       tmp = keyNew(buf, KEY_END);
+
+       succeed_if( kdbRename(handle, key, buf) == 0, "kdbRename failed.");
+       succeed_if( kdbGetKey(handle, tmp) == 0, "kdbGetKey on the renamed key failed.");
+       
+       succeed_if( kdbGetKey(handle, key) , "kdbGetKey succeed. The old renamed key is still existing.");
+       succeed_if( kdbRename(handle, tmp, keyName(key)) == 0, "kdbRename failed. Can't reverse to the original name.");
+
+       // already renamed back
+       // keyRemove (tmp);
+       // succeed_if (kdbSetKey(handle, tmp) == -1, "the renamed back key still exists");
+       
+       keyDel(tmp);
+       keyDel(key);
+       
+       return nbError-err;
+}
+
+
+/* Make sure that it is possible to create, set and get keys
+ * in the default backend before trying to mount others*/
+void test_default(KDB * handle, Key * root)
+{
+       Key             *key, *key2;
+
+       // test if current directory works
+       key = keyDup(root);
+       succeed_if (key, "Could not create new key");
+       keyAddBaseName(key, "test_delete");
+       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, key) == 0, "Set and Get key failed");
+       succeed_if (keyRemove (key) != -1, "Could not mark key to remove");
+       succeed_if (kdbSetKey (handle, key) != -1, "Could not remove key");
+       succeed_if (kdbGetKey (handle, key) == -1, "Key seems to exists");
+       keyDel (key);
+       
+       // test if current directory works - with value, comment
+       key = keyDup(root);
+       keySetString(key, "myvalue");
+       keySetComment (key, "mycomment");
+       keyAddBaseName(key, "test_key");
+       succeed_if (key, "Could not create new key");
+       //succeed_if (strcmp (keyName (key), root) == 0, "Name for key is not correct");
+       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, key) == 0, "Set and Get key failed");
+       succeed_if (keyRemove (key) != -1, "Could not mark key to remove");
+       succeed_if (kdbSetKey (handle, key) != -1, "Could not remove key");
+       succeed_if (kdbGetKey (handle, key) == -1, "Key seems to exists");
+       keyDel(key);
+
+       // remove it again
+       key = keyDup (root);
+       succeed_if (key, "Could not create new key");
+       keyAddBaseName(key, "test_delete");
+       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, key) == 0, "Set and Get key failed");
+       keyRemove (key);
+       succeed_if (kdbSetKey (handle, key) != -1, "Could not remove key");
+       succeed_if (kdbGetKey (handle, key) == -1, "Key seems to exists");
+       keyDel (key);
+
+       // clone key with keyDup
+       key = keyDup (root);
+       keyAddBaseName (key, "dup_test");
+       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, key) == 0, "Set and Get key failed");
+       keyRemove (key);
+       succeed_if (kdbSetKey (handle, key) != -1, "Could not remove key");
+       succeed_if (kdbGetKey (handle, key) == -1, "Key seems to exists");
+       keyDel (key);
+
+       // last test with a subdirectory
+       key = keyDup (root);
+       keyAddBaseName (key, "default");
+               key2 = keyDup (key);
+               keyAddBaseName (key2, "key");
+               succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, key2) == 0, "Set and Get key failed");
+               keyRemove (key2);
+               succeed_if (kdbSetKey (handle, key2) != -1, "Could not remove key");
+               succeed_if (kdbGetKey (handle, key2) == -1, "Key seems to exists");
+               keyDel (key2);
+       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, key) == 0, "Set and Get key failed");
+       keyRemove (key);
+       succeed_if (kdbSetKey (handle, key) != -1, "Could not remove directory key");
+       succeed_if (kdbGetKey (handle, key) == -1, "Key seems to exists");
+       keyDel (key);
+       
+       // now lets see if root key survived the procedure
+       key = keyDup (root);
+       succeed_if (key, "Could not create new key");
+       exit_if_fail (kdbGetKey (handle, key) == 0, "Root key suddenly missing?");
+       keyDel (key);
+
+       keyDel (root);
+       // default backends seems to work
+}
+
+
+void test_backend(KDB * handle, Key * root, char *fileName, KeySet *conf)
+{
+       KeySet          *ks, *ks2, *ksd;
+       Key             *cur;
+       unsigned long options = KDB_O_POP;
+       KDBCap  *cap;
+
+       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, root) == 0, "Set and Get backend root key failed");
+
+       printf("Testing elektra-%s backend with file %s, Root: %s.\n", (char*)keyValue(root), fileName,  keyName (root));
+       printf("==========================\n");
+
+       if (kdbMount (handle, root, conf) == -1)
+       {
+               printf ("Could not mount backend %s\n", (char*)keyValue(root));
+               printf ("Will not continue with tests on that backend\n");
+
+               remove_keys(handle, root);
+
+               keyDel (root);
+               ksDel (conf);
+
+               return;
+       }
+
+       cap = kdbGetCapability (handle, root);
+       exit_if_fail (cap != NULL, "can't test backend not declaring its capabilities");
+
+       ks = ksNew(0);
+       printf("Getting file from %s.\n", fileName);
+       exit_if_fail( ksFromXMLfile(ks, fileName) == 0, "ksFromXMLfile failed.");
+       /* ksOutput (ks, stdout, 0); */
+       /* ksGenerate (ks, stdout,  0); */
+
+       if (kdbcGetonlyFullSet(cap))
+       {
+               /* Add a duplication of root key so that kdbSet does not fail */
+               ksAppendKey(ks, keyDup(root));
+               options = 0;
+
+               /* ksOutput (ks, stdout, 0); */
+               succeed_if( kdbSet(handle, ks, root, KDB_O_SYNC) > 0, "kdbSet with FullSet failed.");
+       } else {
+               exit_if_fail (kdbGetKey (handle, root) == 0, "No root key?");
+               ksRewind(ks);
+               ksSort(ks);
+               while ( (cur = ksNext(ks)) ) {
+                       /* keyGenerate (cur); */
+
+                       keySetUID (cur, nbUid);
+                       keySetGID (cur, nbGid);
+
+                       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, cur) == 0, "Set and Get key failed");
+                       succeed_if (testcase_kdbRename(handle, cur) == 0, "Could not rename key");
+                       succeed_if (testcase_kdbStatKey(handle, cur) == 0, "Could not stat key");
+
+                       // to get sync flag away, to allow next testcase
+                       succeed_if (testcase_kdbSetKeyAndkdbGetKey (handle, cur) == 0, "Set and Get key failed");
+               }
+       }
+
+       printf ("Testing kdbGet with 2 inactive keys\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_INACTIVE | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, 0, cap) == 0, "compare keyset with all keys failed");
+       /*
+       printf ("We got :\n"); ksGenerate (ks2, stdout, KDB_O_HEADER);
+       printf ("We want:\n"); ksGenerate (ks, stdout, KDB_O_HEADER);
+       */
+       ksDel(ks2);
+
+       printf ("Testing kdbGet with 2 inactive keys not yet updating (fails)\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_INACTIVE | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, 0, cap) == 0, "compare keyset with all keys failed");
+       /*
+       printf ("We got :\n"); ksGenerate (ks2, stdout, KDB_O_HEADER);
+       printf ("We want:\n"); ksGenerate (ks, stdout, KDB_O_HEADER);
+       */
+       ksDel(ks2);
+
+       printf ("Testing kdbGet with no option\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_INACTIVE, cap) == 0, "compare keyset INACTIVE failed");
+       /*
+       ksOutput (ks, stdout, KDB_O_HEADER);
+       ksOutput (ks2, stdout, KDB_O_HEADER);
+       */
+       ksDel(ks2);
+
+       printf ("Testing kdbGet with 3 directories\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_NODIR  | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_NODIR | KDB_O_INACTIVE, cap) == 0, "compare keyset NODIR failed");
+       ksDel(ks2);
+
+       printf ("Testing kdbGet only with directories\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_INACTIVE | KDB_O_DIRONLY  | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_DIRONLY, cap) == 0, "compare keyset DIRONLY failed");
+       ksDel(ks2);
+
+       remove_keys(handle, root);
+       succeed_if( kdbSet(handle, ks, root, KDB_O_SYNC) != -1, "kdbSet failed.");
+
+       printf ("Testing kdbGet with inactive keys\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_INACTIVE | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, 0, cap) == 0, "compare keyset with all keys failed");
+       /*
+       ksGenerate (ks, stdout, 0);
+       printf ("but is "); ksGenerate (ks2, stdout, 0);
+       */
+       ksDel(ks2);
+
+       printf ("Testing kdbGet with no option\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_INACTIVE, cap) == 0, "compare keyset INACTIVE failed");
+       ksDel(ks2);
+
+       printf ("Testing kdbGet without directories\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_NODIR  | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_NODIR | KDB_O_INACTIVE, cap) == 0, "compare keyset NODIR failed");
+       ksDel(ks2);
+
+       printf ("Testing kdbGet only with directories\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_DIRONLY  | options) >= 0, "kdbGet failed.");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_DIRONLY | KDB_O_INACTIVE, cap) == 0, "compare keyset DIRONLY failed");
+       ksDel(ks2);
+
+       printf ("Testing kdbGet with only stat\n");
+       ks2 = ksNew(0);
+       keyStat(root);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_STATONLY | options) >= 0, "kdbGet failed.");
+       succeed_if (keyNeedStat(root) == 1, "root should need stat");
+       ksRewind (ks2); while (ksNext(ks2) != 0) succeed_if (keyNeedStat(ksCurrent(ks2)) == 1, "all keys should need stat");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_INACTIVE | KDB_O_STATONLY, cap) == 0, "compare keyset STATONLY failed");
+       ksDel(ks2);
+
+       printf ("Testing kdbGet no stat\n");
+       ks2 = ksNew(0);
+       keyStat(root);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_NOSTAT | options) >= 0, "kdbGet failed.");
+       succeed_if (keyNeedStat(root) == 0, "root should not need stat");
+       ksRewind (ks2); while (ksNext(ks2) != 0) succeed_if (keyNeedStat(ksCurrent(ks2)) == 0, "all keys should not need stat");
+       succeed_if( compare_keyset(ks, ks2, KDB_O_INACTIVE, cap) == 0, "compare keyset NOSTAT failed");
+       ksDel(ks2);
+
+       printf ("Testing kdbGet with inactive keys but no recursive\n");
+       ks2 = ksNew(0);
+       succeed_if( kdbGet(handle, ks2, root, KDB_O_INACTIVE | KDB_O_NORECURSIVE | options) >= 0, "kdbGet failed.");
+       ksd = ksNew (0);
+       ksRewind (ks);
+       while ((cur = ksNext(ks)) != 0)
+       {
+               if (!strcmp(keyName(root), keyName(cur)) || keyIsDirectBelow(root, cur))
+               {
+                       ksAppendKey(ksd, cur);
+               }
+       }
+       succeed_if( compare_keyset(ksd, ks2, 0, cap) == 0, "compare keyset with all keys no recursive failed");
+       /*
+       printf ("ks was "); ksGenerate (ks, stdout, 0);
+       printf ("ksd is "); ksGenerate (ksd, stdout, 0);
+       printf ("but is "); ksGenerate (ks2, stdout, 0);
+       */
+       ksDel(ks2);
+       ksDel(ksd);
+
+       ksDel(ks);
+       remove_keys(handle, root);
+
+       kdbUnmount (handle, root);
+       keyDel (root);
+       ksDel (conf);
+}
+
+int main()
+{
+       printf("ELEKTRA BACKENDS TEST SUITE\n");
+       printf("========================================\n\n");
+
+       init ();
+
+       KDB     *handle = kdbOpen();
+       exit_if_fail(handle, "kdbOpen() failed.");
+
+       test_default(handle, create_root_key(""));
+
+       test_backend(handle, create_root_key("filesys"),srcdir_file("filesys.xml"),create_conf (".kdb"));
+       test_backend(handle, create_root_key("filesys"),srcdir_file("keyset.xml"), create_conf (".kdb"));
+       test_backend(handle, create_root_key("fstab"),  srcdir_file("fstab.xml"),  create_conf (".kdb/fstab_kdb"));
+       test_backend(handle, create_root_key("hosts"),  srcdir_file("hosts.xml"),  create_conf (".kdb/hosts_kdb"));
+
+       printf("\ntest_kdb RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       exit_if_fail (kdbClose(handle) == 0, "Could not close libelektra");
+
+       return nbError;
+}
+
diff --git a/tests/test_key.c b/tests/test_key.c
new file mode 100644 (file)
index 0000000..33cc33a
--- /dev/null
@@ -0,0 +1,2254 @@
+/***************************************************************************
+ *          test_key.c  -  Key struct test suite
+ *                  -------------------
+ *  begin                : Thu Aug 03 2006
+ *  copyright            : (C) 2006 by Yannick Lecaillez
+ *  email                : sizon5@gmail.com
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <tests.h>
+
+
+struct test {
+       char    *testName;
+       char    *keyName;
+       
+       char    *expectedKeyName;
+       char    *expectedBaseName;
+       char    *expectedFRootName;
+       char    *expectedParentName;
+};
+
+struct test tstKeyName[] = 
+{
+       { "Normal key", "system/foo/bar",
+               "system/foo/bar",
+               "bar",
+               "system",
+               "system/foo"
+       },
+
+       { "Key containing redundant & trailing separator", "system//foo//bar//",
+               "system/foo/bar",       /* keyName      */
+               "bar",                  /* keyBaseName  */
+               "system",               /* keyGetFullRootName   */
+               "system/foo"            /* keyGetParentName     */
+       },
+
+       { "Normal user key", "user/key",
+               "user/key",                     /* keyName      */
+               "key",                          /* keyBaseName  */
+               "user:env1",                    /* keyGetFullRootName   */ 
+               "user"                          /* keyGetParentName     */
+       
+       },
+
+       { "Normal user key with owner", "user:owner/key",
+               "user/key",                     /* keyName      */
+               "key",                          /* keyBaseName  */
+               "user:owner",                   /* keyGetFullRootName   */ 
+               "user"                          /* keyGetParentName     */
+       
+       },
+
+       { "Depth user key with owner", "user:owner/folder/long/base/dir/key",
+               "user/folder/long/base/dir/key", /* keyName     */
+               "key",                          /* keyBaseName  */
+               "user:owner",                   /* keyGetFullRootName   */ 
+               "user/folder/long/base/dir"     /* keyGetParentName     */
+       
+       },
+
+       { "Key containing escaped separator", "user:yl///foo\\///bar\\/foo_bar\\",
+               "user/foo\\//bar\\/foo_bar\\",  /* keyName      */
+               "bar\\/foo_bar\\",              /* keyBaseName  */
+               "user:yl",                      /* keyGetFullRootName   */ 
+               "user/foo\\/"                   /* keyGetParentName     */
+       
+       },
+
+       { "Key containing escaped separator at the end", "user:yl///foo\\///bar\\/foo_bar\\/",
+               "user/foo\\//bar\\/foo_bar\\/", /* keyName      */
+               "bar\\/foo_bar\\/",             /* keyBaseName  */
+               "user:yl",                      /* keyGetFullRootName   */ 
+               "user/foo\\/"                   /* keyGetParentName     */
+       
+       },
+       
+       { NULL, NULL, NULL }
+};
+
+void test_keyComparing()
+{
+       Key *key1 = keyNew(0);
+       Key *key2 = keyNew(0);
+
+       succeed_if(keyCompare(key1,key2) == 0, "the keys don't differ of course");
+
+       keySetName (key1, "user/myname");
+       succeed_if(keyCompare(key1,key2) == KEY_NAME, "the keys should differ in name");
+       keySetName (key2, "user/myname");
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in name");
+
+       keySetOwner (key1, "myowner");
+       succeed_if(keyCompare(key1,key2) == KEY_OWNER, "the keys should differ in owner");
+       keySetOwner (key2, "myowner");
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in owner");
+
+       keySetString (key1, "myvalue");
+       succeed_if(keyCompare(key1,key2) == (KEY_VALUE|KEY_TYPE), "the keys should differ in value and type");
+       keySetType (key2, KEY_TYPE_STRING);
+       succeed_if(keyCompare(key1,key2) == KEY_VALUE, "the keys should differ in value");
+       keySetString (key2, "myvalue");
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in value");
+
+       keySetComment (key1, "mycomment");
+       succeed_if(keyCompare(key1,key2) == KEY_COMMENT, "the keys should differ in comment");
+       keySetComment (key2, "mycomment");
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in comment");
+
+       keySetUID (key1, 50);
+       succeed_if(keyCompare(key1,key2) == KEY_UID, "the keys should differ in uid");
+       keySetUID (key2, 50);
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in uid");
+
+       keySetGID (key1, 50);
+       succeed_if(keyCompare(key1,key2) == KEY_GID, "the keys should differ in gid");
+       keySetGID (key2, 50);
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in gid");
+
+       keySetMode (key1, 0222);
+       succeed_if(keyCompare(key1,key2) == KEY_MODE, "the keys should differ in mode");
+       keySetMode (key2, 0222);
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in mode");
+
+       keySetType (key1, 50);
+       succeed_if(keyCompare(key1,key2) == KEY_TYPE, "the keys should differ in type");
+       keySetType (key2, 50);
+       succeed_if(keyCompare(key1,key2) == 0, "the keys should not differ in type");
+
+       keyDel (key1);
+       keyDel (key2);
+}
+
+void test_keyNewSystem()
+{
+       Key     *key;
+       char array[] = "here is some data stored";
+       char * getBack;
+       Key *k1;
+       Key *k2;
+       Key *k3;
+
+       printf("Test system key creation\n");
+
+       // Empty key
+       key = keyNew(0);
+       succeed_if(key != NULL, "keyNew: Unable to create a new empty key");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete empty key");
+
+       // Key with name
+       key = keyNew("system/sw/test", KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name");
+       succeed_if(keyNeedRemove (key) == 0, "KEY_REMOVE set");
+       succeed_if(strcmp(keyName(key), "system/sw/test") == 0, "keyNew: Key's name setted incorrectly");
+       keyCopy (key, 0);
+       succeed_if (strcmp (keyName(key), "") == 0, "name after keyCopy(,0)");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name");
+       
+       // Key with name
+       key = keyNew("system/sw/test", KEY_REMOVE, KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name");
+       succeed_if(keyNeedRemove (key) == 1, "KEY_REMOVE not set");
+       succeed_if(strcmp(keyName(key), "system/sw/test") == 0, "keyNew: Key's name setted incorrectly");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name");
+       
+       // Key with name + value (default type must be KEY_TYPE_STRING)
+       key = keyNew("system/sw/test",
+                       KEY_VALUE, "test",
+                       KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(key), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(key), "test") == 0, "keyNew: Value not set correctly");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + value");
+       
+       // Key with name + UID/GID
+       key = keyNew("system/sw/test",
+                       KEY_UID, 123,
+                       KEY_GID, 456,
+                       KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name + UID + GID");
+       succeed_if(keyGetUID(key) == 123, "keyNew: UID no set correctly");
+       succeed_if(keyGetGID(key) == 456, "keyNew: GID not set correctly");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + UID + GID");
+
+       // Key with name + MODE
+       key = keyNew("system/sw/test",
+                       KEY_MODE, 0644,
+                       KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name + mode");
+       succeed_if(keyGetMode(key) == 0644, "keyNew: mode no set correctly");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + mode");
+
+       key = keyNew("system/valid/there",
+                       KEY_TYPE, KEY_TYPE_BINARY,
+                       KEY_SIZE, sizeof(array),
+                       KEY_VALUE, array,
+                       KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsBinary (key), "Could not set type to binary");
+       succeed_if(keyGetValueSize(key) == sizeof(array), "Value size not correct");
+       succeed_if(memcmp ((char *) keyValue(key), array, sizeof(array)) == 0, "could not get correct binary value");
+       getBack = malloc (keyGetValueSize(key));
+       keyGetBinary(key, getBack, keyGetValueSize(key));
+       succeed_if(memcmp(getBack, array, sizeof(array)) == 0, "could not get correct value with keyGetBinary");
+       free (getBack);
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + owner");
+
+       key = keyNew("system", KEY_END);
+       succeed_if (strcmp (keyName(key), "system") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 7, "empty name size" );
+       succeed_if (strcmp (keyOwner(key), "") == 0, "owner for a system key?");
+       succeed_if (keyGetOwnerSize(key) == 1, "owner y size" );
+       keyDel (key);
+
+       // testing multiple values at once
+       k1=keyNew("system/1",  KEY_VALUE, "singlevalue", KEY_END);
+       k2=keyNew("system/2",   KEY_VALUE, "myvalue", KEY_END);
+       k3=keyNew("system/3", KEY_VALUE, "syskey",  KEY_END);
+       succeed_if(k1 != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(k1), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(k1), "singlevalue") == 0, "keyNew: Value not set correctly");
+       
+       succeed_if(k2 != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(k2), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(k2), "myvalue") == 0, "keyNew: Value not set correctly");
+       
+       succeed_if(k3 != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(k3), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(k3), "syskey") == 0, "keyNew: Value not set correctly");
+
+       succeed_if(keyDel(k1) == 0, "keyDel: Unable to delete key with name + value");
+       succeed_if(keyDel(k2) == 0, "keyDel: Unable to delete key with name + value");
+       succeed_if(keyDel(k3) == 0, "keyDel: Unable to delete key with name + value");
+}
+
+void test_keyNewUser()
+{
+       Key     *key;
+       char array[] = "here is some data stored";
+       char fullroot [MAX_PATH_LENGTH];
+       char * getBack;
+       Key *k1;
+       Key *k2;
+       Key *k3;
+
+       printf("Test user key creation\n");
+       
+#ifdef HAVE_SETENV
+       setenv ("USER","hugo",1);
+#else
+       putenv ("USER=hugo");
+#endif
+       key = keyNew ("user/test/test", KEY_END);
+       succeed_if( strcmp(keyOwner(key), "hugo") == 0, "keyNew: owner not set correctly");
+       succeed_if( keyGetOwnerSize(key) == 5, "owner length not correct");
+       keyGetFullName (key, fullroot, MAX_PATH_LENGTH);
+       succeed_if( strcmp(keyOwner(key), "hugo") == 0, "keyNew: owner not set correctly");
+       /* printf ("%s, %s, %s\n", keyName(key), keyBaseName(key), fullroot); */
+       succeed_if(strcmp(keyName(key),"user/test/test") == 0, "Wrong keyname: keyName");
+       succeed_if(strcmp(keyBaseName(key),"test") == 0, "Wrong keyname: keyBaseName");
+       succeed_if(strcmp(fullroot,"user:hugo/test/test") == 0, "Wrong keyname: keyGetFullName");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + owner");
+
+#ifdef HAVE_SETENV     
+       setenv ("KDB_USER","tommy",1);
+#else
+       putenv("KDB_USER=tommy");
+#endif
+       key = keyNew ("user/test/test", KEY_END);
+       succeed_if( strcmp(keyOwner(key), "tommy") == 0, "keyNew: owner not set correctly");
+       succeed_if( keyGetOwnerSize(key) == 6, "owner length not correct");
+       keyGetFullName (key, fullroot, MAX_PATH_LENGTH);
+       succeed_if( strcmp(keyOwner(key), "tommy") == 0, "keyNew: owner not set correctly");
+       /* printf ("%s, %s, %s\n", keyName(key), keyBaseName(key), fullroot); */
+       succeed_if(strcmp(keyName(key),"user/test/test") == 0, "Wrong keyname: keyName");
+       succeed_if(strcmp(keyBaseName(key),"test") == 0, "Wrong keyname: keyBaseName");
+       succeed_if(strcmp(fullroot,"user:tommy/test/test") == 0, "Wrong keyname: keyGetFullName");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + owner");
+       
+       // Key with name + owner
+       key = keyNew("user/test/test",
+                       KEY_OWNER, "yl",
+                       KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name + owner");
+       succeed_if( strcmp(keyOwner(key), "yl") == 0, "keyNew: owner not set correctly");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + owner");
+       
+       key = keyNew("user/valid/there",
+                       KEY_TYPE, KEY_TYPE_BINARY,
+                       KEY_SIZE, sizeof(array),
+                       KEY_VALUE, array,
+                       KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsBinary (key), "Could not set type to binary");
+       succeed_if(keyGetValueSize(key) == sizeof(array), "Value size not correct");
+       succeed_if(memcmp ((char *) keyValue(key), array, sizeof(array)) == 0, "could not get correct binary value");
+       getBack = malloc (keyGetValueSize(key));
+       keyGetBinary(key, getBack, keyGetValueSize(key));
+       succeed_if(memcmp(getBack, array, sizeof(array)) == 0, "could not get correct value with keyGetBinary");
+       free (getBack);
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name + owner");
+
+       key = keyNew("user:y", KEY_END);
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 5, "empty name size" );
+       succeed_if (strcmp (keyOwner(key), "y") == 0, "Should be a name not length 0");
+       succeed_if (keyGetOwnerSize(key) == 2, "owner y size" );
+       keyDel (key);
+
+       succeed_if (key = keyNew("user:perfectowner", KEY_END), "could not create new key");
+       succeed_if (keySetName(key, "user:perfectowner") == 5, "could not set to user with owner");
+       succeed_if (keyGetOwnerSize (key) == 13, "owner size not correct");
+       succeed_if (strcmp (keyOwner(key), "perfectowner") == 0, "Owner not same as set");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       // testing multiple values at once
+       k1=keyNew("user/1",  KEY_VALUE, "singlevalue", KEY_END);
+       k2=keyNew("user/2",   KEY_VALUE, "myvalue", KEY_END);
+       k3=keyNew("user/3", KEY_VALUE, "syskey",  KEY_END);
+       succeed_if(k1 != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(k1), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(k1), "singlevalue") == 0, "keyNew: Value not set correctly");
+       
+       succeed_if(k2 != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(k2), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(k2), "myvalue") == 0, "keyNew: Value not set correctly");
+       
+       succeed_if(k3 != NULL, "keyNew: Unable to create a key with name + value of default type");
+       succeed_if(keyIsString(k3), "keyNew: Default key value isn't set to KEY_TYPE_STRING");
+       succeed_if(strcmp(keyValue(k3), "syskey") == 0, "keyNew: Value not set correctly");
+
+       succeed_if(keyDel(k1) == 0, "keyDel: Unable to delete key with name + value");
+       succeed_if(keyDel(k2) == 0, "keyDel: Unable to delete key with name + value");
+       succeed_if(keyDel(k3) == 0, "keyDel: Unable to delete key with name + value");
+
+       k1 = keyNew ("invalid", KEY_END);
+       succeed_if (k1 != 0, "should construct key even on invalid names");
+       succeed_if (strcmp (keyName(k1), "") == 0, "no name should be set");
+       succeed_if (keyGetNameSize(k1) == 1, "namesize");
+       keyDel (k1);
+}
+
+void test_keyReference()
+{
+       printf("Test key reference\n");
+
+       Key *key = keyNew (KEY_END);
+       Key *c = keyNew (KEY_END);
+       Key *d;
+       KeySet *ks1, *ks2;
+       succeed_if (keyGetRef(key) == 0, "New created key reference");
+
+       succeed_if (keyIncRef (key) == 1, "keyIncRef return value");
+       succeed_if (keyGetRef(key) == 1, "After keyIncRef key reference");
+       succeed_if (keyIncRef (key) == 2, "keyIncRef return value");
+       succeed_if (keyGetRef(key) == 2, "After keyIncRef key reference");
+       succeed_if (keyIncRef (key) == 3, "keyIncRef return value");
+       succeed_if (keyGetRef(key) == 3, "After keyIncRef key reference");
+       succeed_if (keyIncRef (key) == 4, "keyIncRef return value");
+       succeed_if (keyGetRef(key) == 4, "After keyIncRef key reference");
+
+       d = keyDup (key);
+       succeed_if (keyGetRef(d) == 0, "After keyDup key reference");
+       succeed_if (keyIncRef (d) == 1, "keyIncRef return value");
+       succeed_if (keyGetRef(key) == 4, "Reference should not change");
+       succeed_if (keyDecRef (d) == 0, "decrement key");
+       succeed_if (keyDel(d) == 0, "last keyDel d, key exist");
+
+       keyCopy (c, key);
+       succeed_if (keyGetRef(c) == 0, "After keyCopy key reference");
+       succeed_if (keyIncRef (c) == 1, "keyIncRef return value");
+       succeed_if (keyGetRef(key) == 4, "Reference should not change");
+
+       keyCopy (c, key);
+       succeed_if (keyGetRef(c) == 1, "After keyCopy key reference");
+       succeed_if (keyDecRef (c) == 0, "keyDecRef return value");
+       succeed_if (keyGetRef(key) == 4, "Reference should not change");
+
+       succeed_if (keyIncRef (c) == 1, "keyIncRef return value");
+       succeed_if (keyIncRef (c) == 2, "keyIncRef return value");
+       keyCopy (c, key);
+       succeed_if (keyGetRef(c) == 2, "After keyCopy key reference");
+       succeed_if (keyDecRef (c) == 1, "keyDecRef return value");
+       succeed_if (keyDecRef (c) == 0, "keyDecRef return value");
+       succeed_if (keyDel (c) == 0, "could not delete copy");
+
+       succeed_if (keyGetRef(key) == 4, "After keyIncRef key reference");
+       succeed_if (keyDecRef (key) == 3, "keyDel return value");
+       succeed_if (keyDel (key) == 3, "should not do anything");
+       succeed_if (keyGetRef(key) == 3, "After keyIncRef key reference");
+       succeed_if (keyDecRef (key) == 2, "keyDel return value");
+       succeed_if (keyDel (key) == 2, "should not do anything");
+       succeed_if (keyGetRef(key) == 2, "After keyIncRef key reference");
+       succeed_if (keyDecRef (key) == 1, "keyDel return value");
+       succeed_if (keyDel (key) == 1, "should not do anything");
+       succeed_if (keyGetRef(key) == 1, "Should have no more reference");
+       succeed_if (keyDecRef (key) == 0, "last keyDel key, key exist");
+       succeed_if (keyDel (key) == 0, "last keyDel key, key exist");
+
+       /* From examples in ksNew () */
+       key = keyNew(0); // ref counter 0
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       keyIncRef(key); // ref counter of key 1
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       keyDel(key);    // has no effect
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       keyDecRef(key); // ref counter back to 0
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       keyDel(key);    // key is now deleted
+
+       ks1 = ksNew(0);
+       ks2 = ksNew(0);
+       key = keyNew(0); // ref counter 0
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       ksAppendKey(ks1, key); // ref counter of key 1
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       ksAppendKey(ks2, key); // ref counter of key 2
+       succeed_if (keyGetRef(key) == 2, "reference counter");
+       ksDel(ks1); // ref counter of key 1
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       ksDel(ks2); // key is now deleted
+
+       key = keyNew(0); // ref counter 0
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       keyIncRef(key); // ref counter of key 1
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       keyDel (key);   // has no effect
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       keyIncRef(key); // ref counter of key 2
+       succeed_if (keyGetRef(key) == 2, "reference counter");
+       keyDel (key);   // has no effect
+       succeed_if (keyGetRef(key) == 2, "reference counter");
+       keyDecRef(key); // ref counter of key 1
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       keyDel (key);   // has no effect
+       succeed_if (keyGetRef(key) == 1, "reference counter");
+       keyDecRef(key); // ref counter is now 0
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       keyDel (key); // key is now deleted
+
+       return;
+
+       /* This code needs very long to execute, especially on 64bit
+        * systems. */
+
+       key = keyNew(0); // ref counter 0
+       while (keyGetRef(key) < SSIZE_MAX) keyIncRef(key);
+       succeed_if (keyGetRef(key) == SSIZE_MAX, "reference counter");
+       succeed_if (keyIncRef(key) == SSIZE_MAX, "should stay at maximum");
+       succeed_if (keyGetRef(key) == SSIZE_MAX, "reference counter");
+       succeed_if (keyIncRef(key) == SSIZE_MAX, "should stay at maximum");
+
+       key->ksReference = 5;
+       while (keyGetRef(key) > 0) keyDecRef(key);
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       succeed_if (keyDecRef(key) == 0, "should stay at minimum");
+       succeed_if (keyGetRef(key) == 0, "reference counter");
+       succeed_if (keyDecRef(key) == 0, "should stay at minimum");
+       keyDel (key);
+
+}
+
+void test_keyType()
+{
+       Key * key;
+       char array [] = "test";
+       int i;
+
+       printf("Test key type\n");
+
+       key = keyNew (KEY_END);
+
+       for (i=0; i<256; i++)
+       {
+               keySetType (key, i);
+               succeed_if (keyGetType (key) == (i), "could not change to dir type to something between 0 and 255");
+       }
+
+       keySetType (key, 0);
+       succeed_if (keyGetType (key) == 0, "could not unset type");
+
+       succeed_if (keySetString (key, array) == 5, "size not correct");
+       succeed_if (keyIsString (key), "could not change to string type");
+       succeed_if (keyGetValueSize (key) == sizeof (array), "size is not correct");
+
+       succeed_if (keySetBinary (key, array, 4) == 4, "size not correct");
+       succeed_if (keyIsBinary (key), "could not change to binary type");
+       succeed_if (keyGetValueSize (key) == sizeof (array)-1, "size is not correct");
+       
+       succeed_if (keySetString (key, array) == 5, "size not correct");
+       succeed_if (keyIsString (key), "could not change to string type");
+       succeed_if (keyGetValueSize (key) == sizeof (array), "size is not correct");
+
+       keyDel (key);
+}
+
+void test_keyName()
+{
+       Key     *key;
+       size_t  size;
+       char    *buf;
+       char    ret [1000];
+       int     i;
+       char *getBack;
+       char testName[] = "user/name";
+       char testFullName[] = "user:max/name";
+       char testBaseName[] = "name";
+       char testOwner[] = "max";
+
+       Key * copy = keyNew (KEY_END);
+
+#ifdef HAVE_CLEARENV
+       clearenv();
+#else
+       unsetenv("USER");
+#endif
+
+       printf ("Test Key Name\n");
+
+       key = keyNew (testName, KEY_END);
+       succeed_if (keyGetName (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetName (key,0,100) == -1, "string null pointer");
+       succeed_if (keyGetName (key,ret,0) == -1, "length checking");
+       for (i=1; i< sizeof(testName);i++)
+       {
+               succeed_if (keyGetName (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testName); i<sizeof(testName)*2; i++)
+       {
+               succeed_if (keyGetName (key,ret,i) == sizeof(testName), "length checking longer");
+       }
+       succeed_if (keyGetName (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+       keyDel (key);
+
+       succeed_if (keyName(0) == 0, "null pointer");
+
+       key = keyNew (0);
+       succeed_if (strcmp(keyName(key), "") == 0, "empty name");
+       succeed_if (keyGetName (key,ret, 1000) == 1, "get empty name");
+       succeed_if (strcmp(ret, "") == 0, "not empty name");
+       succeed_if (keyGetName (key,ret, 0) == -1, "get empty name");
+       keyDel (key);
+
+       succeed_if (keySetName(0,ret) == -1, "Null pointer");
+
+
+
+       printf ("Test Key Full Name\n");
+
+       key = keyNew (testFullName, KEY_END);
+       succeed_if (keyGetFullName (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetFullName (key,0,100) == -1, "string null pointer");
+       succeed_if (keyGetFullName (key,ret,0) == -1, "length checking");
+       for (i=1; i< sizeof(testFullName);i++)
+       {
+               succeed_if (keyGetFullName (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testFullName); i<sizeof(testFullName)*2; i++)
+       {
+               succeed_if (keyGetFullName (key,ret,i) == sizeof(testFullName), "length checking longer");
+       }
+       succeed_if (keyGetFullName (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+       keyDel (key);
+
+       key = keyNew (0);
+       succeed_if (keyIsUser(key) == 0, "empty user key?");
+       succeed_if (keyIsSystem(key) == 0, "empty user key?");
+       succeed_if (keyGetFullName (key,ret, 1000) == 1, "get empty name");
+       succeed_if (strcmp(ret, "") == 0, "not empty name");
+       succeed_if (keyGetFullName (key,ret, 0) == -1, "get empty name");
+       keyDel (key);
+
+       succeed_if (keySetName(0,ret) == -1, "Null pointer");
+
+
+
+
+       printf ("Test Key Base Name\n");
+
+       key = keyNew (testFullName, KEY_END);
+       succeed_if (keyGetBaseName (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetBaseName (key,0,100) == -1, "string null pointer");
+       succeed_if (keyGetBaseName (key,ret,0) == -1, "length checking");
+       for (i=1; i< sizeof(testBaseName);i++)
+       {
+               succeed_if (keyGetBaseName (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testBaseName); i<sizeof(testBaseName)*2; i++)
+       {
+               succeed_if (keyGetBaseName (key,ret,i) == sizeof(testBaseName), "length checking longer");
+       }
+       succeed_if (keyGetBaseName (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+       keyDel (key);
+
+       succeed_if (keyBaseName(0) == 0, "null pointer");
+
+       key = keyNew (0);
+       succeed_if (strcmp(keyBaseName(key), "") == 0, "empty name");
+       succeed_if (keyIsUser(key) == 0, "empty user key?");
+       succeed_if (keyIsSystem(key) == 0, "empty user key?");
+       succeed_if (keyGetBaseName (key,ret, 1000) == 1, "get empty name");
+       succeed_if (strcmp(ret, "") == 0, "not empty name");
+       succeed_if (keyGetBaseName (key,ret, 0) == -1, "get empty name");
+       keyDel (key);
+
+       succeed_if (keySetName(0,ret) == -1, "Null pointer");
+
+       key = keyNew ("user", KEY_END);
+       succeed_if (keyGetBaseNameSize (key) == 1, "length checking");
+       succeed_if (keyGetBaseName (key,ret,1) == 1, "GetBaseName for root key");
+       succeed_if (strcmp (ret, "") == 0, "did not return correct basename");
+       succeed_if (keyGetBaseName (key,ret,2) == 1, "GetBaseName for root key");
+       succeed_if (strcmp (ret, "") == 0, "did not return correct basename");
+       keyDel (key);
+
+       key = keyNew ("system", KEY_END);
+       succeed_if (keyGetBaseNameSize (key) == 1, "length checking");
+       succeed_if (keyGetBaseName (key,ret,1) == 1, "GetBaseName for root key");
+       succeed_if (strcmp (ret, "") == 0, "did not return correct basename");
+       succeed_if (keyGetBaseName (key,ret,2) == 1, "GetBaseName for root key");
+       succeed_if (strcmp (ret, "") == 0, "did not return correct basename");
+       keyDel (key);
+
+
+
+
+
+       printf ("Test Key Owner\n");
+
+       key = keyNew (testFullName, KEY_END);
+       succeed_if (keyGetOwner (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetOwner (key,0,100) == -1, "string null pointer");
+       succeed_if (keyGetOwner (key,ret,0) == -1, "length checking");
+       for (i=1; i< sizeof(testOwner);i++)
+       {
+               succeed_if (keyGetOwner (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testOwner); i<sizeof(testOwner)*2; i++)
+       {
+               succeed_if (keyGetOwner (key,ret,i) == sizeof(testOwner), "length checking longer");
+       }
+       succeed_if (keyGetOwner (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+
+       succeed_if (keySetOwner(key,0) == 1, "delete owner");
+       succeed_if (keyGetOwner (key,ret,i) == 1, "length checking deleting");
+       succeed_if (strcmp(ret, "") == 0, "not empty owner");
+
+       succeed_if (keySetOwner(key,testOwner) == sizeof(testOwner), "set owner");
+       succeed_if (keyGetOwner (key,ret,i) == sizeof(testOwner), "length checking working");
+       succeed_if (strcmp(ret, testOwner) == 0, "not empty owner");
+
+       succeed_if (keySetOwner(key,"") == 1, "delete owner");
+       succeed_if (keyGetOwner (key,ret,i) == 1, "length checking deleting");
+       succeed_if (strcmp(ret, "") == 0, "not empty owner");
+
+       succeed_if (keySetOwner(key,testOwner) == sizeof(testOwner), "set owner");
+       succeed_if (keyGetOwner (key,ret,i) == sizeof(testOwner), "length checking working");
+       succeed_if (strcmp(ret, testOwner) == 0, "not empty owner");
+       keyDel (key);
+
+       succeed_if (keyOwner(0) == 0, "null pointer");
+
+       key = keyNew (0);
+       succeed_if (strcmp(keyOwner(key), "") == 0, "empty owner");
+       succeed_if (keyGetOwner (key,ret, 1000) == 1, "get empty owner");
+       succeed_if (strcmp(ret, "") == 0, "not empty owner");
+       succeed_if (keyGetOwner (key,ret, 0) == -1, "get empty owner");
+       keyDel (key);
+
+       succeed_if (keySetOwner(0,"") == -1, "null pointer");
+
+
+
+
+
+       printf("Test Slashes in Key Name\n");
+       key = keyNew(0);
+       succeed_if (keyGetNameSize(key) == 1, "empty name size" );
+       keyDel (key);
+
+       key = keyNew("", KEY_END);
+       succeed_if (key != 0, "key should not be null!");
+       succeed_if (strcmp (keyName(key), "") == 0, "keyName should be "" string");
+       succeed_if (keyGetName(key,ret, 999) == 1, "keyGetName should return 1");
+       succeed_if (strcmp (ret, "") == 0, "\\0 should be first character in array");
+       succeed_if (keyGetNameSize(key) == 1, "empty name size" );
+       keyDel (key);
+
+       key = keyNew (KEY_END);
+       keySetName(key,"user");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: User as basename");
+       succeed_if (keyGetNameSize(key) == 5, "empty name size" );
+       succeed_if (keyGetOwnerSize(key) >= 1, "empty owner size");
+
+       keySetName(key,"system");
+       succeed_if (strcmp (keyName(key), "system") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 7, "empty name size" );
+       succeed_if (keyGetOwnerSize(key) == 1, "empty owner size");
+       keyDel (key);
+
+       key = keyNew (KEY_END);
+       keySetName(key,"system");
+       succeed_if (strcmp (keyName(key), "system") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 7, "empty name size" );
+       succeed_if (keyGetOwnerSize(key) == 1, "empty owner size");
+
+       keySetName(key,"user");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: User as basename");
+       succeed_if (keyGetNameSize(key) == 5, "empty name size" );
+       succeed_if (keyGetOwnerSize(key) == 1, "empty owner size");
+       keyDel (key);
+
+       key = keyNew(0);
+       succeed_if (keySetName(key,"user:") == 5, "setting user: generates error");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 5, "empty name size" );
+       succeed_if (strcmp (keyOwner(key), "") == 0, "Should be a name not length 0");
+       succeed_if (keyGetOwnerSize(key) == 1, "empty owner size" );
+       keyDel (key);
+
+       key = keyNew(0);
+       succeed_if (keySetName(key,"user:y") == 5, "setting user: generates error");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 5, "empty name size" );
+       succeed_if (strcmp (keyOwner(key), "y") == 0, "Should be a name not length 0");
+       succeed_if (keyGetOwnerSize(key) == 2, "owner y size" );
+       keyDel (key);
+
+       key = keyNew(0);
+       succeed_if (keySetName(key,"no") == -1, "no error code setting invalid name");
+       succeed_if (strcmp (keyName(key), "") == 0, "Name Problem: System as basename");
+       succeed_if (keyGetNameSize(key) == 1, "empty name size" );
+       keyDel (key);
+
+       key = keyNew("user/noname", KEY_END);
+       succeed_if(keyGetNameSize(key) == 12, "size not correct after keyNew");
+       getBack = malloc (12);
+       succeed_if(keyGetName(key, getBack, 12), "could not get name");
+       succeed_if(strcmp(getBack, "user/noname") == 0, "did not get correct value back");
+       free (getBack);
+
+       keySetName (key, "user/noname");
+       succeed_if(keyGetNameSize(key) == 12, "size not correct after keySetName");
+       getBack = malloc (12);
+       succeed_if(keyGetName(key, getBack, 12), "could not get name");
+       succeed_if(strcmp(getBack, "user/noname") == 0, "did not get correct value back");
+       free (getBack);
+
+       keySetName (key, "no");
+       succeed_if(keyGetNameSize(key) == 1, "size not correct after keySetName");
+       getBack = malloc (1);
+       succeed_if(keyGetName(key, getBack, 1), "could not get name");
+       succeed_if(strcmp(getBack, "") == 0, "did not get correct value back");
+       free (getBack);
+       keyDel (key);
+
+       key = keyNew("user/noname", KEY_END);
+       keySetName(key,"");
+       succeed_if (strcmp (keyName(key), "") == 0, "keyName should be "" string");
+       succeed_if (keyGetName(key,ret, 999) == 1, "keyGetName should return 1");
+       succeed_if (strcmp (ret, "") == 0, "\\0 should be first character in array");
+       succeed_if (keyGetNameSize(key) == 1, "empty name size" );
+
+       keySetName(key,"user//hidden");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Two slashes in name");
+       succeed_if (keyGetNameSize(key) == 12, "name size minus slashes" );
+
+       keySetName(key,"user///hidden");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Three slashes in name");
+       succeed_if (keyGetNameSize(key) == 12, "name size minus slashes" );
+
+       keySetName(key,"user////////////////////////////////////hidden");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Many slashes in name");
+       succeed_if (keyGetNameSize(key) == 12, "name size minus slashes" );
+
+       printf("Test trailing Slashes in Key Name\n");
+       keySetName(key,"user//hidden/");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Trailing Slashes");
+       succeed_if (keyGetNameSize(key) == 12, "name size minus slashes" );
+
+       keySetName(key,"user//hidden//");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Trailing Slashes");
+       succeed_if (keyGetNameSize(key) == 12, "name size minus slashes" );
+
+       keySetName(key,"user//hidden///////");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Trailing Slashes");
+       succeed_if (keyGetNameSize(key) == 12, "name size minus slashes" );
+
+       keySetName(key,"user/");
+       // printf ("Name: %s\n", keyName(key));
+       succeed_if (strcmp (keyName(key), "user") == 0, "Trailing Slashes");
+
+       keySetName(key,"user/a");
+       succeed_if (strcmp (keyName(key), "user/a") == 0, "Trailing Slashes: One letter problem");
+
+       keySetName(key,"user//");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Trailing Slashes");
+
+       keySetName(key,"user/////////");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Trailing Slashes");
+
+       printf("Test Dots in Key Name\n");
+       keySetName(key,"user/hidden/.");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Name Problem: Dot as basename");
+
+       keySetName(key,"user/.");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: Dot in Middle");
+
+       keySetName(key,"user/./hidden");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Name Problem: Dot in Middle");
+
+       keySetName(key,"user/.valid/.");
+       succeed_if (strcmp (keyName(key), "user/.valid") == 0, "Name Problem: Dot as basename (with comment)");
+
+       keySetName(key,"user/./.valid");
+       succeed_if (strcmp (keyName(key), "user/.valid") == 0, "Name Problem: Dot in Middle (with comment)");
+
+       keySetName(key,"user/./.valid/.");
+       succeed_if (strcmp (keyName(key), "user/.valid") == 0, "Name Problem: More dots");
+
+       keySetName(key,"user/././././.valid/././././.");
+       succeed_if (strcmp (keyName(key), "user/.valid") == 0, "Name Problem: Much more dots");
+
+       printf("Test Double Dots in Key Name\n");
+       keySetName(key,"user/hidden/parent/..");
+       succeed_if (strcmp (keyName(key), "user/hidden") == 0, "Name Problem: Double Dot as basename");
+
+       keySetName(key,"user/hidden/..");
+       // printf ("Name: %s\n", keyName(key));
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: Double Dot as basename");
+
+       keySetName(key,"user/..");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: Can't go higher then user in hierachy");
+
+       keySetName(key,"user/hidden/../..");
+       succeed_if (strcmp (keyName(key), "user") == 0, "Name Problem: Can't go higher then user in hierachy");
+
+       succeed_if (keySetName(key, "user///sw/../sw//././MyApp")==sizeof("user/sw/MyApp"), "could not set keySet example");
+       succeed_if (strcmp (keyName(key), "user/sw/MyApp") == 0, "Example of keySet does not work");
+       succeed_if (keyGetNameSize(key) == sizeof("user/sw/MyApp"), "incorrect length for keySet example");
+
+       printf("Test Mixed Dots and Slashes in Key Name\n");
+       keySetName(key,"user/hidden/../.");
+
+       keyDel (key);
+
+
+       printf("Test failure key creation\n");
+
+       key = keyNew ("invalid", KEY_END);
+       succeed_if (key!=0, "null pointer for invalid name");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name");
+
+       key = keyNew("nonhere/valid/there", KEY_END);
+       succeed_if (key != NULL, "keyNew did not accept wrong name");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name");
+
+       key = keyNew("nonhere:y/valid/there", KEY_END);
+       succeed_if (key != NULL, "keyNew did not accept wrong name");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name");
+
+       key = keyNew ("user/validname", KEY_END);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name");
+       succeed_if(strcmp(keyName(key), "user/validname") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName(key, "invalid");
+       succeed_if (keyGetNameSize(key) == 1, "name size for invalid name");
+       succeed_if(strcmp(keyName(key), "") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName (key, "user/validname");
+       succeed_if(strcmp(keyName(key), "user/validname") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName(key, "");
+       succeed_if (keyGetNameSize(key) == 1, "name size for invalid name");
+       succeed_if(strcmp(keyName(key), "") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName (key, "user/validname\\/t");
+       succeed_if(strcmp(keyName(key), "user/validname\\/t") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName(key, 0);
+       succeed_if (keyGetNameSize(key) == 1, "name size for invalid name");
+       succeed_if(strcmp(keyName(key), "") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName (key, "user/validname\\");
+       succeed_if(strcmp(keyName(key), "user/validname\\") == 0, "keyNew: Key's name setted incorrectly");
+
+       keySetName (key, "user/validname\\/");
+       succeed_if(strcmp(keyName(key), "user/validname\\/") == 0, "keyNew: Key's name setted incorrectly");
+       succeed_if(keyDel(key) == 0, "keyDel: Unable to delete key with name");
+
+#ifdef HAVE_CLEARENV
+       clearenv();
+#else
+       unsetenv("USER");
+       unsetenv("KDB_USER");
+#endif
+
+#ifdef HAVE_SETENV
+       setenv ("USER", "env1", 1);
+#else
+       putenv ("USER=env1");
+#endif
+
+       printf("Test key's name manipulation\n");
+       for(i = 0 ; tstKeyName[i].testName != NULL ; i++) {
+               key = keyNew(tstKeyName[i].keyName, KEY_END);
+               
+               /* keyName */
+               succeed_if( (strcmp(keyName(key), tstKeyName[i].expectedKeyName) == 0) , "keyName" );
+
+               /* keyBaseName */
+               succeed_if( (strcmp(keyBaseName(key), tstKeyName[i].expectedBaseName) == 0), "keyBaseName" );
+
+               /* keyGetFullRootNameSize */
+               size = keyGetFullRootNameSize(key);
+               succeed_if( (size == kdbiStrLen(tstKeyName[i].expectedFRootName)), "keyGetFullRootNameSize" );
+               
+               /* keyGetFullRootName */
+               buf = kdbiMalloc(size*sizeof(char));
+               keyGetFullRootName(key, buf, size);
+               // printf ("comp: %s - %s\n", buf, tstKeyName[i].expectedFRootName);
+               succeed_if( (strncmp(buf, tstKeyName[i].expectedFRootName, size) == 0), "keyGetFullRootName" );
+               free(buf);
+
+               /* keyGetParentNameSize */
+               size = keyGetParentNameSize(key);
+               succeed_if( (size == kdbiStrLen(tstKeyName[i].expectedParentName)), "ketGetParentNameSize" );
+
+               /* keyGetParentName */
+               size = keyGetParentNameSize(key)+1;
+               buf = kdbiMalloc(size*sizeof(char));
+               keyGetParentName(key, buf, size);
+               succeed_if( (strncmp(buf, tstKeyName[i].expectedParentName, size) == 0), "keyGetParentName" );
+               free(buf);
+
+               /* keyGetBaseNameSize */
+               size = keyGetBaseNameSize(key);
+               succeed_if( (size == kdbiStrLen(tstKeyName[i].expectedBaseName)), "keyGetBaseNameSize" );
+
+               /* keyGetBaseName */
+               size = keyGetBaseNameSize(key)+1;
+               buf = kdbiMalloc(size*sizeof(char));
+               keyGetBaseName(key, buf, size);
+               succeed_if( (strncmp(buf, tstKeyName[i].expectedBaseName, size) == 0), "keyGetBaseName" );
+               free(buf);
+
+               /* keyGetNameSize */
+               size = keyGetNameSize(key);
+               succeed_if( (size == kdbiStrLen(tstKeyName[i].expectedKeyName)), "keyGetKeyNameSize" );
+               
+               /* keyGetName */
+               size = keyGetNameSize(key);
+               buf = kdbiMalloc(size*sizeof(char));
+               keyGetName(key, buf, size);
+               succeed_if( (strcmp(buf, tstKeyName[i].expectedKeyName) == 0), "keyGetName" );
+               free(buf);
+
+               succeed_if (keyGetRef (copy) == 0, "reference of copy not correct");
+               keyCopy (copy, key);
+               succeed_if (compare_key (copy, key, 0) == 0, "keys are not equal after copy");
+               succeed_if (keyGetRef (copy) == 0, "reference of copy not correct");
+
+               keyDel(key);
+       }
+       keyDel (copy);
+}
+
+
+void test_keyValue()
+{
+       Key * key;
+       char    ret [1000];
+       int i;
+       char testString[] = "teststring";
+       char testBinary[] = "\0tes\1tbinary";
+       testBinary[sizeof(testBinary)] = 'T';
+
+       printf("Test value of keys\n");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (keyGetValueSize (key) == 1, "empty value size");
+       succeed_if (keySetString (key,"perfectvalue") == 13, "could not set string");
+       succeed_if (keyGetValueSize (key) == 13, "value size not correct");
+       succeed_if (strcmp (keyValue(key), "perfectvalue") == 0, "String not same as set");
+       succeed_if (keySetString (key,"perfectvalue") == 13, "could not re-set same string");
+       succeed_if (strcmp (keyValue(key), "perfectvalue") == 0, "String not same as set");
+       succeed_if (keySetString (key,"nearperfectvalue") == 17, "could not re-set other string");
+       succeed_if (keyGetValueSize (key) == 17, "value size not correct");
+       succeed_if (strcmp (keyValue(key), "nearperfectvalue") == 0, "String not same as set");
+       succeed_if (keyGetString (key, ret, keyGetValueSize (key)>=999 ? 999 : keyGetValueSize (key))
+                       == 17, "could not get string");
+       succeed_if (strcmp (ret, "nearperfectvalue") == 0, "String not same as set");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (strcmp (keyValue(key), "") == 0, "Empty value problem");
+       succeed_if (keyGetValueSize(key) == 1, "Empty value size problem");
+       succeed_if (keySetString (key,"") == 1, "could not set empty string");
+       succeed_if (strcmp (keyValue(key), "") == 0, "Empty value problem");
+       succeed_if (keyGetValueSize(key) == 1, "Empty value size problem");
+       succeed_if (keyGetString(key, ret, 0) == -1, "Could not get empty value");
+       succeed_if (keyGetString(key, ret, 1) == 1, "Could not get empty value");
+       succeed_if (ret[0] == 0, "keyGetValue did not return empty value");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (keySetString (key, "a long long string") == 19, "could not set string");
+       succeed_if (keyGetString (key, ret, 6) == -1, "string not truncated");
+       /*succeed_if (errno == KDB_ERR_TRUNC, "errno not set properly trunc");*/
+       succeed_if (keyGetBinary (key, ret, 999) == -1, "binary not mismatch");
+       /*succeed_if (errno == KDB_ERR_TYPEMISMATCH, "errno not set properly typemismatch");*/
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (keySetBinary (key, "a long long binary", 19) == 19, "could not set string");
+       succeed_if (keyGetBinary (key, ret, 6) == -1, "binary not truncated");
+       /*succeed_if (errno == KDB_ERR_TRUNC, "errno not set properly trunc");*/
+       succeed_if (keyGetString (key, ret, 999) == -1, "string not mismatch");
+       /*succeed_if (errno == KDB_ERR_TYPEMISMATCH, "errno not set properly typemismatch");*/
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       for (i=1; i<255; i++)
+       {
+               ret[0] =  i; ret[1] = i; ret[2] = 0;
+               //output_key (key);
+               succeed_if (keySetString (key,ret) == 3, "could not set string");
+               succeed_if (strcmp (keyValue(key), ret) == 0, "String not same as set");
+       }
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+
+
+
+       printf ("Test string of key\n");
+
+       succeed_if (keyValue(0) == 0, "null pointer");
+       succeed_if (keyGetValueSize(0) == -1, "null pointer");
+       succeed_if (keySetString(0,"") == -1, "null pointer");
+
+       key = keyNew(0);
+       succeed_if (keyGetValueSize(key) == 1, "empty value size");
+
+       keySetString (key, testString);
+       succeed_if (keyGetString (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetString (key,0,100) == -1, "string null pointer");
+       succeed_if (keyGetString (key,ret,0) == -1, "length checking");
+
+       for (i=1; i< sizeof(testString);i++)
+       {
+               succeed_if (keyGetString (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testString); i<sizeof(testString)*2; i++)
+       {
+               succeed_if (keyGetString (key,ret,i) == sizeof(testString), "length checking longer");
+       }
+       succeed_if (keyGetString (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+
+       succeed_if (keySetString(key,0) == 1, "delete string");
+       succeed_if (keyGetString (key,ret,i) == 1, "length checking deleting");
+       succeed_if (strcmp(ret, "") == 0, "not empty string");
+
+       succeed_if (keySetString(key,testString) == sizeof(testString), "set string");
+       succeed_if (keyGetString (key,ret,i) == sizeof(testString), "length checking working");
+       succeed_if (strcmp(ret, testString) == 0, "not empty string");
+
+       succeed_if (keySetString(key,"") == 1, "delete string");
+       succeed_if (keyGetString (key,ret,i) == 1, "length checking deleting");
+       succeed_if (strcmp(ret, "") == 0, "not empty string");
+
+       succeed_if (keySetString(key,testString) == sizeof(testString), "set string");
+       succeed_if (keyGetString (key,ret,i) == sizeof(testString), "length checking working");
+       succeed_if (strcmp(ret, testString) == 0, "not empty string");
+
+       succeed_if (keyGetValueSize(key) == sizeof(testString), "testString value size");
+       succeed_if (strncmp(keyValue(key), testString, sizeof(testString)) == 0, "testString not same");
+       keyDel (key);
+
+
+
+       printf ("Test binary of key\n");
+
+       succeed_if (keyValue(0) == 0, "null pointer");
+       succeed_if (keyGetValueSize(0) == -1, "null pointer");
+       succeed_if (keySetBinary(0,"",1) == -1, "null pointer");
+
+       key = keyNew(0);
+       succeed_if (keySetBinary(key,"",0) == -1, "null size");
+       succeed_if (keySetBinary(key,"b",0) == -1, "null size");
+       succeed_if (keySetBinary(key,"",SIZE_MAX) == -1, "max size");
+       succeed_if (keySetBinary(key,"b",SIZE_MAX) == -1, "max size");
+       succeed_if (keyGetValueSize(key) == 1, "empty value size");
+
+       keySetBinary (key, testBinary, sizeof(testBinary));
+       succeed_if (keyGetBinary (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetBinary (key,0,100) == -1, "binary null pointer");
+       succeed_if (keyGetBinary (key,ret,0) == -1, "length checking");
+
+       for (i=1; i< sizeof(testBinary);i++)
+       {
+               succeed_if (keyGetBinary (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testBinary); i<sizeof(testBinary)*2; i++)
+       {
+               succeed_if (keyGetBinary (key,ret,i) == sizeof(testBinary), "length checking longer");
+       }
+       succeed_if (keyGetBinary (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+
+       succeed_if (keySetBinary(key,0,0) == 0, "delete binary");
+       succeed_if (keyGetBinary (key,ret,i) == 0, "length checking deleting");
+
+       succeed_if (keySetBinary(key,testBinary, sizeof(testBinary)) == sizeof(testBinary), "set binary");
+       succeed_if (keyGetBinary (key,ret,i) == sizeof(testBinary), "length checking working");
+       succeed_if (strcmp(ret, testBinary) == 0, "not empty binary");
+
+       succeed_if (keySetBinary(key,0,1) == 0, "delete binary");
+       succeed_if (keyGetBinary (key,ret,i) == 0, "length checking deleting");
+
+       succeed_if (keySetBinary(key,testBinary, sizeof(testBinary)) == sizeof(testBinary), "set binary");
+       succeed_if (keyGetBinary (key,ret,i) == sizeof(testBinary), "length checking working");
+       succeed_if (strcmp(ret, testBinary) == 0, "not empty binary");
+
+       succeed_if (keySetBinary(key,"",1) == 1, "delete binary the string way");
+       succeed_if (keyGetBinary (key,ret,i) == 1, "length checking deleting string way");
+       succeed_if (strcmp(ret, "") == 0, "not empty binary the string way");
+
+       succeed_if (keySetBinary(key,testBinary, sizeof(testBinary)) == sizeof(testBinary), "set binary");
+       succeed_if (keyGetBinary (key,ret,i) == sizeof(testBinary), "length checking working");
+       succeed_if (strcmp(ret, testBinary) == 0, "not empty binary");
+
+       succeed_if (keyGetValueSize(key) == sizeof(testBinary), "testBinary value size");
+       succeed_if (strncmp(keyValue(key), testBinary, sizeof(testBinary)) == 0, "testBinary not same");
+       keyDel (key);
+
+}
+
+void test_keyBinary(void)
+{
+       Key *key =0;
+       char ret [1000];
+       int i;
+       char c;
+       char binaryData[] = "\0binary \1\34data";
+       binaryData[sizeof(binaryData)] = 'T';
+
+       printf ("Test binary special cases\n");
+
+       key = keyNew ("user/binary",
+               KEY_TYPE, KEY_TYPE_BINARY+1,
+               KEY_SIZE, sizeof(binaryData),
+               KEY_VALUE, binaryData,
+               KEY_END);
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == sizeof(binaryData), "size not correct");
+       succeed_if (memcmp(binaryData, keyValue(key), sizeof(binaryData)) == 0, "memcmp");
+       succeed_if (keyGetBinary(key, ret, 1000) == sizeof(binaryData), "could not get binary data");
+       succeed_if (memcmp(binaryData, ret, sizeof(binaryData)) == 0, "memcmp");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       keySetType (key, KEY_TYPE_BINARY+1);
+       keySetBinary (key, binaryData, sizeof(binaryData));
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == sizeof(binaryData), "size not correct");
+       succeed_if (memcmp(binaryData, keyValue(key), sizeof(binaryData)) == 0, "memcmp");
+       succeed_if (keyGetBinary(key, ret, 1000) == sizeof(binaryData), "could not get binary data");
+       succeed_if (memcmp(binaryData, ret, sizeof(binaryData)) == 0, "memcmp");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       keySetType (key, KEY_TYPE_BINARY+1);
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == 0, "size not correct");
+       succeed_if (keyValue(key) == 0, "should be null pointer");
+       succeed_if (keyGetBinary(key, ret, 1000) == 0, "should write nothing because of no data");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       keySetBinary(key, 0, 0);
+       keySetType (key, KEY_TYPE_BINARY+1);
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == 0, "size not correct");
+       succeed_if (keyValue(key) == 0, "should be null pointer");
+       succeed_if (keyGetBinary(key, ret, 1000) == 0, "should write nothing because of no data");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       keySetBinary(key, 0, 1);
+       keySetType (key, KEY_TYPE_BINARY+1);
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == 0, "size not correct");
+       succeed_if (keyValue(key) == 0, "should be null pointer");
+       succeed_if (keyGetBinary(key, ret, 1000) == 0, "should write nothing because of no data");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       keySetBinary(key, "", 1);
+       succeed_if (keySetBinary(key, 0, SIZE_MAX) == -1, "should do nothing and fail");
+       succeed_if (keySetBinary(key, 0, SSIZE_MAX) == 0, "should free data");
+       keySetType (key, KEY_TYPE_BINARY+1);
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == 0, "size not correct");
+       succeed_if (keyValue(key) == 0, "should be null pointer");
+       succeed_if (keyGetBinary(key, ret, 1000) == 0, "should write nothing because of no data");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       keySetBinary(key, "", 1);
+       keySetType (key, KEY_TYPE_BINARY+1);
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == 1, "size not correct");
+       succeed_if (memcmp(binaryData, keyValue(key), 1) == 0, "memcmp");
+       succeed_if (keyGetBinary(key, ret, 1000) == 1, "could not get binary data");
+       succeed_if (memcmp(binaryData, ret, 1) == 0, "memcmp");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       keyDel (key);
+
+       key = keyNew(0);
+       i = 23;
+       keySetType (key, KEY_TYPE_BINARY+1);
+       keySetBinary (key, (void*)&i, sizeof(i));
+
+       succeed_if (keyIsBinary(key) == 1, "should be binary");
+       succeed_if (keyIsString(key) == 0, "should not be string");
+       succeed_if (keyGetType(key) == KEY_TYPE_BINARY+1, "forgot its type");
+       succeed_if (keyGetValueSize(key) == sizeof(i), "size not correct");
+       succeed_if (memcmp((void*)&i, keyValue(key), sizeof(i)) == 0, "memcmp");
+       succeed_if (keyGetBinary(key, ret, 1000) == sizeof(i), "could not get binary data");
+       succeed_if (memcmp((void*)&i, ret, sizeof(i)) == 0, "memcmp");
+       succeed_if (keyGetString(key, ret, 1000) == -1, "should be type mismatch");
+
+       i = *(int*)keyValue(key);
+       succeed_if (i==23, "incorrect int");
+
+       keyDel (key);
+
+}
+
+void test_keyComment()
+{
+       Key * key;
+       char    ret [1000];
+       int i;
+       char testComment [] = "testcomment";
+
+       printf("Test comment of key\n");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (keyGetCommentSize (key) == 1, "empty comment size");
+       succeed_if (keySetComment (key,"perfectcomment") == 15, "could not set comment");
+       succeed_if (keyGetCommentSize (key) == 15, "comment size not correct");
+       succeed_if (strcmp (keyComment(key), "perfectcomment") == 0, "Comment not same as set");
+       succeed_if (keySetComment (key,"perfectcomment") == 15, "could not re-set same comment");
+       succeed_if (strcmp (keyComment(key), "perfectcomment") == 0, "Comment not same as set");
+       succeed_if (keySetComment (key,"nearperfectcomment") == 19, "could not re-set other comment");
+       succeed_if (keyGetCommentSize (key) == 19, "comment size not correct");
+       succeed_if (strcmp (keyComment(key), "nearperfectcomment") == 0, "Comment not same as set");
+       succeed_if (keyGetComment (key, ret, keyGetCommentSize (key)>=999 ? 999 : keyGetCommentSize (key))
+                       == 19, "could not get comment");
+       succeed_if (strcmp (ret, "nearperfectcomment") == 0, "Comment not same as set");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+       
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (strcmp (keyComment(key), "") == 0, "Empty comment problem");
+       succeed_if (keyGetCommentSize(key) == 1, "Empty comment size problem");
+       succeed_if (keySetComment (key,"") == 0, "could not set comment");
+       succeed_if (strcmp (keyComment(key), "") == 0, "Empty comment problem");
+       succeed_if (keyGetCommentSize(key) == 1, "Empty comment size problem");
+       succeed_if (keyGetComment(key, ret, 0) == -1, "Could not get empty comment");
+       succeed_if (keyGetComment(key, ret, 1) == 1, "Could not get empty comment");
+       succeed_if (ret[0] == 0, "keyGetComment did not return empty comment");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+       
+       succeed_if (key = keyNew(0), "could not create new key");
+       for (i=1; i<255; i++)
+       {
+               ret[0] = i; ret[1] = i; ret[2] = 0;
+               succeed_if (keySetComment (key,ret) == 3, "could not set comment");
+               // output_key (key);
+               succeed_if (strcmp (keyComment(key), ret) == 0, "Comment not same as set");
+       }
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+
+
+       printf ("Test comment of key 2\n");
+
+       succeed_if (keyComment(0) == 0, "null pointer");
+       succeed_if (keyGetCommentSize(0) == -1, "null pointer");
+       succeed_if (keySetComment(0,"") == -1, "null pointer");
+
+       key = keyNew(0);
+       succeed_if (keyGetCommentSize(key) == 1, "empty comment size");
+
+       keySetComment (key, testComment);
+       succeed_if (keyGetComment (0,ret,100) == -1, "null pointer");
+       succeed_if (keyGetComment (key,0,100) == -1, "comment null pointer");
+       succeed_if (keyGetComment (key,ret,0) == -1, "length checking");
+
+       for (i=1; i< sizeof(testComment);i++)
+       {
+               succeed_if (keyGetComment (key,ret,i) == -1, "length checking too short");
+       }
+       for (i=sizeof(testComment); i<sizeof(testComment)*2; i++)
+       {
+               succeed_if (keyGetComment (key,ret,i) == sizeof(testComment), "length checking longer");
+       }
+       succeed_if (keyGetComment (key,ret, (size_t)-1) == -1, "maxSize exceeded");
+
+       succeed_if (keySetComment(key,0) == 1, "delete comment");
+       succeed_if (keyGetComment (key,ret,i) == 1, "length checking deleting");
+       succeed_if (strcmp(ret, "") == 0, "not empty comment");
+
+       succeed_if (keySetComment(key,testComment) == sizeof(testComment), "set comment");
+       succeed_if (keyGetComment (key,ret,i) == sizeof(testComment), "length checking working");
+       succeed_if (strcmp(ret, testComment) == 0, "not empty comment");
+
+       succeed_if (keySetComment(key,"") == 1, "delete comment");
+       succeed_if (keyGetComment (key,ret,i) == 1, "length checking deleting");
+       succeed_if (strcmp(ret, "") == 0, "not empty comment");
+
+       succeed_if (keySetComment(key,testComment) == sizeof(testComment), "set comment");
+       succeed_if (keyGetComment (key,ret,i) == sizeof(testComment), "length checking working");
+       succeed_if (strcmp(ret, testComment) == 0, "not empty comment");
+
+       succeed_if (keyGetCommentSize(key) == sizeof(testComment), "testComment comment size");
+       succeed_if (strncmp(keyComment(key), testComment, sizeof(testComment)) == 0, "testComment not same");
+       keyDel (key);
+
+}
+
+void test_keyOwner()
+{
+       Key * key;
+       char    ret [1000];
+       int i;
+       printf("Test owner of keys\n");
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (keyGetOwnerSize (key) == 1, "empty owner size");
+       succeed_if (keySetOwner (key,"perfectowner") == 13, "could not set owner");
+       succeed_if (keyGetOwnerSize (key) == 13, "owner size not correct");
+       succeed_if (strcmp (keyOwner(key), "perfectowner") == 0, "Owner not same as set");
+       succeed_if (keySetOwner (key,"perfectowner") == 13, "could not re-set same owner");
+       succeed_if (strcmp (keyOwner(key), "perfectowner") == 0, "Owner not same as set");
+       succeed_if (keySetOwner (key,"nearperfectowner") == 17, "could not re-set other owner");
+       succeed_if (keyGetOwnerSize (key) == 17, "owner size not correct");
+       succeed_if (strcmp (keyOwner(key), "nearperfectowner") == 0, "Owner not same as set");
+       succeed_if (keyGetOwner (key, ret, keyGetOwnerSize (key)>=999 ? 999 : keyGetOwnerSize (key))
+                       == 17, "could not get owner");
+       succeed_if (strcmp (ret, "nearperfectowner") == 0, "Owner not same as set");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (keySetName(key, "user:perfectowner") == 5, "could not set to user with owner");
+       succeed_if (keyGetOwnerSize (key) == 13, "owner size not correct");
+       succeed_if (strcmp (keyOwner(key), "perfectowner") == 0, "Owner not same as set");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+
+       succeed_if (key = keyNew(0), "could not create new key");
+       succeed_if (strcmp (keyOwner(key), "") == 0, "Empty owner problem");
+       succeed_if (keyGetOwnerSize(key) == 1, "Empty owner size problem");
+       succeed_if (keySetOwner (key,"") == 1, "could not set owner");
+       succeed_if (strcmp (keyOwner(key), "") == 0, "Empty owner problem");
+       succeed_if (keyGetOwnerSize(key) == 1, "Empty owner size problem");
+       succeed_if (keyGetOwner(key, ret, 0) == -1, "Could not get empty owner");
+       succeed_if (keyGetOwner(key, ret, 1) == 1, "Could not get empty owner");
+       succeed_if (ret[0] == 0, "keyGetOwner did not return empty owner");
+       succeed_if (keyDel (key) == 0, "could not delete key");
+       
+       succeed_if (key = keyNew(0), "could not create new key");
+       for (i=1; i<255; i++)
+       {
+               ret[0] = i; ret[1] = i; ret[2] = 0;
+               succeed_if (keySetOwner (key,ret) == 3, "could not set owner");
+               // output_key (key);
+               succeed_if (strcmp (keyOwner(key), ret) == 0, "Owner not same as set");
+       }
+       succeed_if (keyDel (key) == 0, "could not delete key");
+}
+
+void test_keyInactive ()
+{
+       Key * key = keyNew(0);
+
+       succeed_if (keyIsInactive(0) == -1, "NULL pointer");
+       succeed_if (keyIsInactive(key) == -1, "Key has no name");
+
+       printf("Test of active and inactive keys\n");
+       keySetName(key,"user/valid");
+       succeed_if (!keyIsInactive(key), "Key should not be inactive");
+
+       keySetName(key,"user/.hidden/valid");
+       succeed_if (!keyIsInactive(key), "Key should not be inactive");
+       
+       keySetName(key,"user/.hidden/€valid");
+       succeed_if (!keyIsInactive(key), "Key should not be inactive");
+       
+       keySetName(key,"user/hidden");
+       succeed_if (!keyIsInactive(key), "Key should not be inactive");
+       
+       keySetName(key,"user/.hidden");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       
+       keySetName(key,"user/.valid/.hidden");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       
+       keySetName(key,"user/.valid/.:hidden");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       
+       keySetName(key,"user/.valid/.€hidden");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       
+       keySetName(key,"user/.HiddenStringKey");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       
+       keySetName(key,"user/.HiddenDirectoryKey");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       
+       keySetName(key,"user/tests/file8xdLVS/filesys/.HiddenStringKey");
+       succeed_if (keyIsInactive(key), "Key should be inactive");
+       keyDel (key);
+}
+
+void test_keyBelow()
+{
+       Key * key1 = keyNew(0);
+       Key * key2 = keyNew(0);
+
+       printf("Test of relative positions of keys\n");
+
+       succeed_if (keyIsBelow(key1,0) == -1, "NULL pointer");
+       succeed_if (keyIsBelow(0,0) == -1, "NULL pointer");
+       succeed_if (keyIsBelow(0,key1) == -1, "NULL pointer");
+
+       succeed_if (keyIsDirectBelow(key1,0) == -1, "NULL pointer");
+       succeed_if (keyIsDirectBelow(0,0) == -1, "NULL pointer");
+       succeed_if (keyIsDirectBelow(0,key1) == -1, "NULL pointer");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid");
+       succeed_if (!keyIsBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/");
+       succeed_if (!keyIsBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/below");
+       succeed_if (keyIsBelow(key1,key2), "Key should be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/b");
+       succeed_if (keyIsBelow(key1,key2), "Key should be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/b");
+       succeed_if (keyIsBelow(key1,key2), "Key should be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/b/e");
+       succeed_if (keyIsBelow(key1,key2), "Key should be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valide");
+       keySetName(key2,"user/valid/e");
+       succeed_if (!keyIsBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid/b");
+       keySetName(key2,"user/valid/e");
+       succeed_if (!keyIsBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valide");
+       keySetName(key2,"user/valid/valide");
+       succeed_if (!keyIsBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/valide");
+       succeed_if (keyIsDirectBelow(key1,key2), "Key should be below");
+       succeed_if (!keyIsDirectBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid/non/valid");
+       succeed_if (!keyIsDirectBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsDirectBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid/a");
+       keySetName(key2,"user/valid/b");
+       succeed_if (!keyIsDirectBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsDirectBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid");
+       keySetName(key2,"user/valid");
+       succeed_if (!keyIsDirectBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsDirectBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid/a/b");
+       keySetName(key2,"user/valid");
+       succeed_if (!keyIsDirectBelow(key1,key2), "Key should not be below");
+       succeed_if (!keyIsDirectBelow(key2,key1), "Key should not be below");
+
+       keySetName(key1,"user/valid/a");
+       keySetName(key2,"user/valid");
+       succeed_if (!keyIsDirectBelow(key1,key2), "Key should not be below");
+       succeed_if (keyIsDirectBelow(key2,key1), "Key should be below");
+
+
+       keyDel (key1);
+       keyDel (key2);
+}
+
+void test_keyDup()
+{
+       Key     *orig, *copy;
+
+       printf("Test key duplication\n");
+
+       // Create test key
+       orig = keyNew("user:yl/foo/bar",
+                       KEY_TYPE, KEY_TYPE_BINARY,
+                       KEY_SIZE, 6,
+                       KEY_VALUE, "foobar",
+                       KEY_COMMENT, "mycomment", 
+                       KEY_UID, 123,
+                       KEY_GID, 456,
+                       KEY_MODE, 0644,
+                       KEY_END);
+
+
+
+       // Dup the key
+       succeed_if( (copy = keyDup(orig)) != 0, "keyDup failed");
+       succeed_if (keyGetATime(orig) == keyGetATime(copy), "ATime should be same");
+       succeed_if (keyGetCTime(orig) == keyGetCTime(copy), "CTime should be same");
+       succeed_if (keyGetMTime(orig) == keyGetMTime(copy), "MTime should be same");
+       succeed_if (keyGetRef(orig) == 0, "orig ref counter should be 0");
+       succeed_if (keyGetRef(copy) == 0, "copy ref counter should be 0");
+       keyDel(orig); // everything independent from original!
+
+       // Check the duplication
+       succeed_if( strcmp(keyName(copy), "user/foo/bar") == 0, "keyDup: key name copy error");
+       succeed_if( strcmp(keyOwner(copy), "yl") == 0, "keyDup: key name owner copy error");
+       succeed_if( strncmp(keyValue(copy), "foobar", 6) == 0, "keyDup: key value copy error");
+       succeed_if( strcmp(keyComment(copy), "mycomment") == 0, "keyDup: key comment copy error");
+       succeed_if( keyGetUID(copy) == 123, "keyDup: key UID copy error");
+       succeed_if( keyGetGID(copy) == 456, "keyDup: key GID copy error");
+       succeed_if( keyGetMode(copy) == 0644, "keyDup: key mode copy error");
+       succeed_if( keyGetType(copy) & (KEY_TYPE_BINARY), "keyDup: key type copy error");
+
+       keyDel(copy);
+
+       orig = keyNew (0);
+       keySetName (orig, "invalid");
+
+       succeed_if ( (copy = keyDup(orig)) != 0, "keyDup failed");
+       succeed_if ( strcmp(keyName(orig), "") == 0, "orig name should be empty");
+       succeed_if ( strcmp(keyName(copy), "") == 0, "copy name should be empty");
+       succeed_if (keyGetNameSize(orig) == 1, "orig name size");
+       succeed_if (keyGetNameSize(copy) == 1, "orig name size");
+       succeed_if ( strcmp(keyOwner(orig), "") == 0, "orig name should be empty");
+       succeed_if ( strcmp(keyOwner(copy), "") == 0, "copy name should be empty");
+       succeed_if (keyGetOwnerSize(orig) == 1, "orig name size");
+       succeed_if (keyGetOwnerSize(copy) == 1, "copy name size");
+       succeed_if (keyGetType(orig) == KEY_TYPE_UNDEFINED, "orig key type");
+       succeed_if (keyGetType(copy) == KEY_TYPE_UNDEFINED, "copy key type");
+       succeed_if (keyGetATime(orig) == keyGetATime(copy), "ATime should be same");
+       succeed_if (keyGetCTime(orig) == keyGetCTime(copy), "CTime should be same");
+       succeed_if (keyGetMTime(orig) == keyGetMTime(copy), "MTime should be same");
+       succeed_if (keyGetRef(orig) == 0, "orig ref counter should be 0");
+       succeed_if (keyGetRef(copy) == 0, "copy ref counter should be 0");
+
+       keyDel (orig);
+       keyDel (copy);
+}
+
+void test_keyCopy()
+{
+       Key     *orig, *copy;
+
+       printf("Test key copy\n");
+
+       // Create test key
+       orig = keyNew("user:yl/foo/bar",
+                       KEY_TYPE, KEY_TYPE_BINARY,
+                       KEY_SIZE, 6,
+                       KEY_VALUE, "foobar",
+                       KEY_COMMENT, "mycomment", 
+                       KEY_UID, 123,
+                       KEY_GID, 456,
+                       KEY_MODE, 0644,
+                       KEY_END);
+
+
+
+       // Copy the key
+       copy = keyNew(0);
+       succeed_if( keyCopy(copy, orig) == 1, "keyCopy failed");
+       succeed_if (keyGetATime(orig) == keyGetATime(copy), "ATime should be same");
+       succeed_if (keyGetCTime(orig) == keyGetCTime(copy), "CTime should be same");
+       succeed_if (keyGetMTime(orig) == keyGetMTime(copy), "MTime should be same");
+       succeed_if (keyGetRef(orig) == 0, "orig ref counter should be 0");
+       succeed_if (keyGetRef(copy) == 0, "copy ref counter should be 0");
+       keyDel(orig); // everything independent from original!
+
+       // Check the duplication
+       succeed_if( strcmp(keyName(copy), "user/foo/bar") == 0, "keyCopy: key name copy error");
+       succeed_if( strcmp(keyOwner(copy), "yl") == 0, "keyCopy: key name owner copy error");
+       succeed_if( strncmp(keyValue(copy), "foobar", 6) == 0, "keyCopy: key value copy error");
+       succeed_if( strcmp(keyComment(copy), "mycomment") == 0, "keyCopy: key comment copy error");
+       succeed_if( keyGetUID(copy) == 123, "keyCopy: key UID copy error");
+       succeed_if( keyGetGID(copy) == 456, "keyCopy: key GID copy error");
+       succeed_if( keyGetMode(copy) == 0644, "keyCopy: key mode copy error");
+       succeed_if( keyGetType(copy) & (KEY_TYPE_BINARY), "keyCopy: key type copy error");
+
+       orig = keyNew(0);
+       succeed_if (keyCopy(copy, 0) == 0, "make the key copy fresh");
+
+       succeed_if (compare_key (orig, copy, 0) == 0, "keyCopy did not make the key fresh");
+       keyDel (orig);
+
+       keyDel(copy);
+
+       orig = keyNew (0);
+       keySetName (orig, "invalid");
+
+       copy = keyNew(0);
+       succeed_if( keyCopy(copy, orig) == 1, "keyCopy failed");
+       succeed_if ( strcmp(keyName(orig), "") == 0, "orig name should be empty");
+       succeed_if ( strcmp(keyName(copy), "") == 0, "copy name should be empty");
+       succeed_if (keyGetNameSize(orig) == 1, "orig name size");
+       succeed_if (keyGetNameSize(copy) == 1, "orig name size");
+       succeed_if ( strcmp(keyOwner(orig), "") == 0, "orig name should be empty");
+       succeed_if ( strcmp(keyOwner(copy), "") == 0, "copy name should be empty");
+       succeed_if (keyGetOwnerSize(orig) == 1, "orig name size");
+       succeed_if (keyGetOwnerSize(copy) == 1, "copy name size");
+       succeed_if (keyGetType(orig) == KEY_TYPE_UNDEFINED, "orig key type");
+       succeed_if (keyGetType(copy) == KEY_TYPE_UNDEFINED, "copy key type");
+       succeed_if (keyGetATime(orig) == keyGetATime(copy), "ATime should be same");
+       succeed_if (keyGetCTime(orig) == keyGetCTime(copy), "CTime should be same");
+       succeed_if (keyGetMTime(orig) == keyGetMTime(copy), "MTime should be same");
+       succeed_if (keyGetRef(orig) == 0, "orig ref counter should be 0");
+       succeed_if (keyGetRef(copy) == 0, "copy ref counter should be 0");
+
+       keyDel (orig);
+       keyDel (copy);
+}
+
+void test_keyDir (void)
+{
+       int i;
+       Key * key = keyNew ("user", KEY_END);
+
+       printf ("Test directory keys\n");
+
+       succeed_if (keyGetMode(key) == 0664, "new key not 0664 by default");
+       succeed_if (keyIsDir (key) == 0, "new key should not be directory by default");
+
+       succeed_if (keySetMode(key, 0644) == 0, "could not set to 0644");
+       succeed_if (keyIsDir (key) == 0, "0644 should not be directory");
+       succeed_if (keyGetMode(key) == 0644, "key is not 0644, but was set");
+
+       succeed_if (keySetDir (key) == 0, "could not set directory key");
+       succeed_if (keyIsDir (key) == 1, "should be directory after keySetDir");
+       succeed_if (keyGetMode(key) == 0755, "key is not 0644, but was set");
+       
+       for (i = 0; i <= 0777; i++)
+       {
+               succeed_if (keySetMode(key, i) == 0, "could not set to 0000 <= i <= 0777");
+               succeed_if (keyGetMode(key) == i, "key is not correct 0000 <= i <= 0777");
+
+               if (/*getuid() == keyGetUID (key) &&*/ (keyGetMode (key) & 0100))
+               {
+                       succeed_if (keyIsDir (key) == 1, "should be directory because of executable and uid match");
+               } else if (/*getuid() != keyGetUID (key) && getgid() == keyGetGID (key) &&*/ (keyGetMode (key) & 0010))
+               {
+                       succeed_if (keyIsDir (key) == 1, "should be directory because of executable and gid match");
+               } else if (/*getuid() != keyGetUID (key) && getgid() != keyGetGID (key) &&*/ (keyGetMode (key) & 0001))
+               {
+                       succeed_if (keyIsDir (key) == 1, "should be directory because of executable and other match");
+               } else {
+                       succeed_if (keyIsDir (key) == 0, "should not be directory");
+               }
+       
+               succeed_if (keySetDir (key) == 0, "could not set directory key");
+               succeed_if (keyIsDir (key) == 1, "should be directory after keySetDir");
+       }
+       keyDel (key);
+
+       key = keyNew ("user", KEY_DIR, KEY_END);
+       succeed_if (keyGetMode(key) == 0775, "new key with KEY_DIR not 0775 by default");
+       succeed_if (keyIsDir (key) == 1, "new key with KEY_DIR should be directory by default");
+
+       succeed_if (keySetMode(key, 0644) == 0, "could not set to 0644");
+       succeed_if (keyIsDir (key) == 0, "0644 should not be directory");
+       succeed_if (keyGetMode(key) == 0644, "key is not 0644, but was set");
+
+       succeed_if (keySetDir (key) == 0, "could not set directory key");
+       succeed_if (keyIsDir (key) == 1, "should be directory after keySetDir");
+       succeed_if (keyGetMode(key) == 0755, "key is not 0644, but was set");
+       keyDel (key);
+
+       key = keyNew ("user/s", KEY_DIR, KEY_MODE, 0444, KEY_END);
+       succeed_if (keyGetMode(key) == 0444, "0444 set by keyNew");
+       succeed_if (keyIsDir (key) == 0, "0444 should be directory");
+       keyDel (key);
+       
+       key = keyNew ("user/s", KEY_MODE, 0444, KEY_DIR, KEY_END);
+       succeed_if (keyGetMode(key) == 0555, "0555 set by keyNew");
+       succeed_if (keyIsDir (key) == 1, "0555 should be directory");
+       keyDel (key);
+}
+
+void test_keyTime()
+{
+       Key * key = keyNew (KEY_END);
+       time_t now = time(0);
+       time_t past= now - 60*60*24*356 * 10;
+       time_t future = now + 60*60*24*356 * 10;
+       /*
+       time_t far_future = now + 60L*60L*24L*356L * 100L;
+       */
+
+       printf ("Test key time\n");
+
+       succeed_if (keyGetATime(0) == (size_t)-1, "null pointer check");
+       succeed_if (keyGetMTime(0) == (size_t)-1, "null pointer check");
+       succeed_if (keyGetCTime(0) == (size_t)-1, "null pointer check");
+
+       succeed_if (keySetATime(0,0) == -1, "null pointer check");
+       succeed_if (keySetMTime(0,0) == -1, "null pointer check");
+       succeed_if (keySetCTime(0,0) == -1, "null pointer check");
+
+       succeed_if (keyGetATime(key) == 0, "new initialized atime not 0");
+       succeed_if (keyGetMTime(key) == 0, "new initialized mtime not 0");
+       succeed_if (keyGetCTime(key) == 0, "new initialized ctime not 0");
+
+       succeed_if (keySetATime (key, now) == 0, "could not set atime");
+       succeed_if (keySetMTime (key, now) == 0, "could not set mtime");
+       succeed_if (keySetCTime (key, now) == 0, "could not set ctime");
+
+       succeed_if (keyGetATime(key) == now, "new initialized atime not 0");
+       succeed_if (keyGetMTime(key) == now, "new initialized mtime not 0");
+       succeed_if (keyGetCTime(key) == now, "new initialized ctime not 0");
+
+
+       succeed_if (keySetATime (key, past) == 0, "could not set atime");
+       succeed_if (keySetMTime (key, past) == 0, "could not set mtime");
+       succeed_if (keySetCTime (key, past) == 0, "could not set ctime");
+
+       succeed_if (keyGetATime(key) == past, "new initialized atime not 0");
+       succeed_if (keyGetMTime(key) == past, "new initialized mtime not 0");
+       succeed_if (keyGetCTime(key) == past, "new initialized ctime not 0");
+
+
+       succeed_if (keySetATime (key, future) == 0, "could not set atime");
+       succeed_if (keySetMTime (key, future) == 0, "could not set mtime");
+       succeed_if (keySetCTime (key, future) == 0, "could not set ctime");
+
+       succeed_if (keyGetATime(key) == future, "new initialized atime not 0");
+       succeed_if (keyGetMTime(key) == future, "new initialized mtime not 0");
+       succeed_if (keyGetCTime(key) == future, "new initialized ctime not 0");
+
+/*
+       succeed_if (keySetATime (key, far_future) == 0, "could not set atime");
+       succeed_if (keySetMTime (key, far_future) == 0, "could not set mtime");
+       succeed_if (keySetCTime (key, far_future) == 0, "could not set ctime");
+
+       succeed_if (keyGetATime(key) == far_future, "new initialized atime not 0");
+       succeed_if (keyGetMTime(key) == far_future, "new initialized mtime not 0");
+       succeed_if (keyGetCTime(key) == far_future, "new initialized ctime not 0");
+
+       warn_if_fail (keyGetATime(key) > 0, "time_t not 64 bit, 2038 problem");
+       warn_if_fail (keyGetMTime(key) > 0, "time_t not 64 bit, 2038 problem");
+       warn_if_fail (keyGetCTime(key) > 0, "time_t not 64 bit, 2038 problem");
+*/
+
+       keyDel (key);
+}
+
+void test_keyMeta(void)
+{
+       Key *key=0;
+
+       succeed_if (keyStat (key) == -1, "stat null pointer");
+       succeed_if (keyNeedStat(key) == -1, "could not stat null pointer");
+       succeed_if(keyGetUID(key) == (uid_t)-1, "uid null pointer");
+       succeed_if(keyGetGID(key) == (gid_t)-1, "gid null pointer");
+       succeed_if(keyGetMode(key) == (mode_t)-1, "mode null pointer");
+
+
+       key = keyNew (0);
+
+       succeed_if (keyNeedStat(key) == 0, "fresh key does not need stat");
+       succeed_if (keyStat(key) == 1, "could not stat key");
+       succeed_if (keyNeedStat(key) == 1, "could not stat key");
+       keyDel (key);
+
+       key = keyNew ("user/stat", KEY_STAT, KEY_END);
+       succeed_if (keyNeedStat(key) == 1, "fresh key need stat");
+       succeed_if (keyStat(key) == 1, "could not stat key");
+       succeed_if (keyNeedStat(key) == 1, "could not stat key");
+       keyDel (key);
+
+       key=0;
+       succeed_if (keyRemove (key) == -1, "remove null pointer");
+       succeed_if (keyNeedRemove(key) == -1, "could not remove key");
+       succeed_if (keyNeedSync(key) == -1, "key needs sync");
+       key = keyNew (0);
+       succeed_if (keyNeedRemove(key) == 0, "fresh key does not need remove");
+       succeed_if (keyNeedSync(key) == 0, "fresh key needs sync");
+       succeed_if (keyRemove(key) == 1, "could not remove key");
+       succeed_if (keyNeedRemove(key) == 1, "could not remove key");
+       succeed_if (keyNeedSync(key) == 1, "need sync after remove");
+       keyDel (key);
+
+       key = keyNew ("user/remove", KEY_REMOVE, KEY_END);
+       succeed_if (keyNeedRemove(key) == 1, "fresh key need remove");
+       succeed_if (keyNeedSync(key) == 1, "need sync after remove");
+       succeed_if (keyRemove(key) == 1, "could not remove key");
+       succeed_if (keyNeedRemove(key) == 1, "could not remove key");
+       succeed_if (keyNeedSync(key) == 1, "need sync after remove");
+       keyDel (key);
+
+       succeed_if (keyNeedSync(0) == -1, "keyNeedSync(0)");
+       succeed_if (keyNeedStat(0) == -1, "keyNeedStat(0)");
+       succeed_if (keyNeedRemove(0) == -1, "keyNeedRemove(0)");
+
+       key = keyNew(0);
+       succeed_if (keyNeedSync(key) == 0, "keyNew(0) should not need sync");
+       succeed_if (keySetName(key,"invalid") == -1, "invalid name should fail");
+       succeed_if (keyNeedSync(key) == 0, "keyNew(0) should not need sync");
+       keyDel (key);
+
+       key = keyNew(0);
+       succeed_if (keyNeedSync(key) == 0, "should not need sync");
+       keySetUID(key,20);
+       succeed_if (keyNeedSync(key) == 1, "should need sync");
+       keyDel (key);
+
+       key = keyNew(0);
+       succeed_if(keyGetUID(key) == (uid_t)-1, "uid not set to nobody");
+       succeed_if(keyGetGID(key) == (gid_t)-1, "gid not set to nobody");
+
+       succeed_if(keySetUID(key,20) == 0, "could not set uid");
+       succeed_if(keySetGID(key,21) == 0, "could not set uid");
+
+       succeed_if(keyGetUID(key) == 20, "uid not set to 20");
+       succeed_if(keyGetGID(key) == 21, "gid not set to 21");
+
+       succeed_if(keySetUID(key,(uid_t)-1) == 0, "could not set uid");
+       succeed_if(keySetGID(key,(gid_t)-1) == 0, "could not set uid");
+
+       succeed_if(keyGetUID(key) == (uid_t)-1, "uid not set to nobody");
+       succeed_if(keyGetGID(key) == (gid_t)-1, "gid not set to nobody");
+
+       succeed_if(keySetUID(key,0) == 0, "could not set uid");
+       succeed_if(keySetGID(key,0) == 0, "could not set uid");
+
+       succeed_if(keyGetUID(key) == 0, "uid not set to 20");
+       succeed_if(keyGetGID(key) == 0, "gid not set to 21");
+       keyDel (key);
+
+       key = keyNew(0);
+       succeed_if(keyGetMode(key) == KEY_DEF_MODE, "new key does not have default mode");
+       succeed_if(keySetDir(key) == 0, "could not set dir");
+       succeed_if(keyGetMode(key) == KEY_DEF_MODE | KEY_DEF_DIR, "directory key");
+       keyDel (key);
+
+       key = keyNew("user/dir", KEY_DIR, KEY_END);
+       succeed_if(keyGetMode(key) == KEY_DEF_MODE | KEY_DEF_DIR, "directory key");
+       succeed_if(keySetDir(key) == 0, "could not set dir");
+       succeed_if(keyGetMode(key) == KEY_DEF_MODE | KEY_DEF_DIR, "directory key");
+       keyDel (key);
+}
+
+void test_keyHelpers()
+{
+       char *name="user/abc/defghi/jkl";
+       char *p;
+       size_t size=0;
+       int level=0;
+       char buffer[20];
+       
+       Key *key=keyNew("system/parent/base",KEY_END);
+       char *parentName;
+       size_t parentSize;
+       Key *k1, *k2;
+
+       printf ("Test key helpers\n");
+
+       /* copied out of example from keyNameGetOneLevel
+        Lets define a key name with a lot of repeating '/' and escaped '/'
+        char *keyName="user////abc/def\\/ghi////jkl///";*/
+
+       p=name;
+       while (*(p=keyNameGetOneLevel(p+size,&size))) {
+               level++;
+
+               strncpy(buffer,p,size);
+               buffer[size]=0;
+
+               /* printf("Level %d name: \"%s\"\n",level,buffer);*/
+               switch (level)
+               {
+                       case 1: succeed_if (strcmp (buffer, "user") == 0, "keyNameGetOneLevel not correct"); break;
+                       case 2: succeed_if (strcmp (buffer, "abc") == 0, "keyNameGetOneLevel not correct"); break;
+                       case 3: succeed_if (strcmp (buffer, "defghi") == 0, "keyNameGetOneLevel not correct"); break;
+                       case 4: succeed_if (strcmp (buffer, "jkl") == 0, "keyNameGetOneLevel not correct"); break;
+                       default: succeed_if (0, "should not reach case statement");
+               }
+       }
+
+       /* with escaped sequence:*/
+       name="user////abc/def\\/ghi////jkl///";
+       size=0;
+       level=0;
+
+       p=name;
+       while (*(p=keyNameGetOneLevel(p+size,&size))) {
+               level++;
+
+               strncpy(buffer,p,size);
+               buffer[size]=0;
+
+               /* printf("Level %d name: \"%s\"\n",level,buffer);*/
+               switch (level)
+               {
+                       case 1: succeed_if (strcmp (buffer, "user") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 4, "wrong size returned"); break;
+                       case 2: succeed_if (strcmp (buffer, "abc") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 3, "wrong size returned"); break;
+                       case 3: succeed_if (strcmp (buffer, "def\\/ghi") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 8, "wrong size returned"); break;
+                       case 4: succeed_if (strcmp (buffer, "jkl") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 3, "wrong size returned"); break;
+                       default: succeed_if (0, "should not reach case statement");
+               }
+       }
+
+       /* with escaped sequence at the end:*/
+       name="user////abc/def\\/ghi////jkl\\/\\/";
+       size=0;
+       level=0;
+
+       p=name;
+       while (*(p=keyNameGetOneLevel(p+size,&size))) {
+               level++;
+
+               strncpy(buffer,p,size);
+               buffer[size]=0;
+
+               /* printf("Level %d name: \"%s\"\n",level,buffer);*/
+               switch (level)
+               {
+                       case 1: succeed_if (strcmp (buffer, "user") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 4, "wrong size returned"); break;
+                       case 2: succeed_if (strcmp (buffer, "abc") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 3, "wrong size returned"); break;
+                       case 3: succeed_if (strcmp (buffer, "def\\/ghi") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 8, "wrong size returned"); break;
+                       case 4: succeed_if (strcmp (buffer, "jkl\\/\\/") == 0, "keyNameGetOneLevel not correct");
+                               succeed_if (size == 7, "wrong size returned"); break;
+                       default: succeed_if (0, "should not reach case statement");
+               }
+       }
+       
+
+       parentSize=keyGetParentNameSize(key);
+       parentName=malloc(parentSize);
+       keyGetParentName(key,parentName,parentSize);
+       succeed_if (strcmp (parentName, "system/parent") == 0, "parentName error");
+       free (parentName);
+       keyDel (key);
+
+       succeed_if (keyAddBaseName (0, "s") == -1, "null pointer saftey");
+
+       k1 = keyNew ("user/dir1/dir2", KEY_END);
+       succeed_if (keyAddBaseName (k1, 0) == 15, "Could not add nothing to basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2") == 0, "added basename not correct");
+       succeed_if (keyAddBaseName (k1, "") == 15, "Could not add nothing to basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2") == 0, "added basename not correct");
+       succeed_if (keyAddBaseName (k1, "mykey") == 21, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 21, "Name size not correct");
+       succeed_if (keyAddBaseName (k1, "mykey") == 27, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey/mykey") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 27, "Name size not correct");
+       succeed_if (keyAddBaseName (k1, "a") == 29, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey/mykey/a") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 29, "Name size not correct");
+       keyDel (k1);
+
+       k1 = keyNew ("user/dir1/dir2", KEY_END);
+       succeed_if (keyAddBaseName (k1, "mykey/mykey/a") == 29, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey/mykey/a") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 29, "Name size not correct");
+       keyDel (k1);
+
+       k1 = keyNew ("user/////dir1//////dir2", KEY_END);
+       succeed_if (keyAddBaseName (k1, "mykey/////mykey////a") == 29, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey/mykey/a") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 29, "Name size not correct");
+       keyDel (k1);
+
+       k1 = keyNew ("user/dir1/////dir2////", KEY_END);
+       succeed_if (keyAddBaseName (k1, "////mykey////mykey/a") == 29, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey/mykey/a") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 29, "Name size not correct");
+       keyDel (k1);
+
+       k1 = keyNew ("user/dir1/dir2", KEY_END);
+       succeed_if (keyAddBaseName (k1, "mykey/mykey////a///") == 29, "Could not add basename");
+       succeed_if (strcmp (keyName(k1), "user/dir1/dir2/mykey/mykey/a") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k1) == 29, "Name size not correct");
+       keyDel (k1);
+
+       k2 = keyNew (KEY_END);
+       succeed_if (keyAddBaseName (k2, "no") == -1, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 1, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew (KEY_END);
+       succeed_if (keyAddBaseName (k2, "user") == 5, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 5, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user/dir1/dir2/mykey/mykey/a", KEY_END);
+       succeed_if (keySetBaseName (k2, "mykey") == 33, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/dir2/mykey/mykey/mykey") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 33, "Name size not correct");
+       succeed_if (keySetBaseName (k2, "einva") == 33, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/dir2/mykey/mykey/einva") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 33, "Name size not correct");
+       succeed_if (keySetBaseName (k2, "chang") == 33, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/dir2/mykey/mykey/chang") == 0, "added basename not correct");
+       succeed_if (keySetBaseName (k2, "change") == 34, "Could not add basename");
+       succeed_if (keyGetNameSize(k2) == 34, "Name size not correct");
+       succeed_if (strcmp (keyName(k2), "user/dir1/dir2/mykey/mykey/change") == 0, "added basename not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user/dir1/a", KEY_END);
+       succeed_if (keySetBaseName (k2, "") == 10, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 10, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user/dir1/a", KEY_END);
+       succeed_if (keySetBaseName (k2, "some/more") == 20, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/some/more") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 20, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user/dir1/a", KEY_END);
+       succeed_if (keySetBaseName (k2, "some////more") == 20, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/some/more") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 20, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user/dir1/a", KEY_END);
+       succeed_if (keySetBaseName (k2, "some////more///") == 20, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/some/more") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 20, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user/dir1/a", KEY_END);
+       succeed_if (keySetBaseName (k2, "///some////more") == 20, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user/dir1/some/more") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 20, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user", KEY_END);
+       succeed_if (keySetBaseName (k2, "user") == 5, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 5, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("system", KEY_END);
+       succeed_if (keySetBaseName (k2, "user") == 5, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "user") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 5, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user", KEY_END);
+       succeed_if (keySetBaseName (k2, "system") == 7, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "system") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 7, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("system", KEY_END);
+       succeed_if (keySetBaseName (k2, "system") == 7, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "system") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 7, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("user", KEY_END);
+       succeed_if (keySetBaseName (k2, "no") == -1, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 1, "Name size not correct");
+       keyDel (k2);
+
+       k2 = keyNew ("system", KEY_END);
+       succeed_if (keySetBaseName (k2, "no") == -1, "Could not add basename");
+       succeed_if (strcmp (keyName(k2), "") == 0, "added basename not correct");
+       succeed_if (keyGetNameSize(k2) == 1, "Name size not correct");
+       keyDel (k2);
+}
+
+
+void test_keyNamespace()
+{
+       Key *key;
+
+       printf ("Test namespaces\n");
+
+       key = keyNew (KEY_END);
+       succeed_if (keyNameGetNamespace (keyName(key)) == 0, "empty namespace not 0");
+       succeed_if (keyGetNamespace (key) == 0, "empty namespace not 0");
+       succeed_if (keyNameIsSystem (keyName(key)) == 0, "empty name is not system");
+       succeed_if (keyIsSystem (key) == 0, "empty key is not system");
+       succeed_if (keyNameIsUser (keyName(key)) == 0, "empty name is not user");
+       succeed_if (keyIsUser (key) == 0, "empty key is not user");
+       keyDel (key);
+
+       key = keyNew ("user", KEY_END);
+       succeed_if (keyNameGetNamespace (keyName(key)) == KEY_NS_USER, "user namespace not KEY_NS_USER");
+       succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER");
+       succeed_if (keyNameIsSystem (keyName(key)) == 0, "user name is not system");
+       succeed_if (keyIsSystem (key) == 0, "user key is not system");
+       succeed_if (keyNameIsUser (keyName(key)) == 1, "user name is not user");
+       succeed_if (keyIsUser (key) == 1, "user key is not user");
+       keyDel (key);
+
+       key = keyNew ("user/key", KEY_END);
+       succeed_if (keyNameGetNamespace (keyName(key)) == KEY_NS_USER, "user namespace not KEY_NS_USER");
+       succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER");
+       succeed_if (keyNameIsSystem (keyName(key)) == 0, "user name is not system");
+       succeed_if (keyIsSystem (key) == 0, "user key is not system");
+       succeed_if (keyNameIsUser (keyName(key)) == 1, "user name is not user");
+       succeed_if (keyIsUser (key) == 1, "user key is not user");
+       keyDel (key);
+
+       key = keyNew ("user:owner/key", KEY_END);
+       succeed_if (keyNameGetNamespace (keyName(key)) == KEY_NS_USER, "user namespace not KEY_NS_USER");
+       succeed_if (keyGetNamespace (key) == KEY_NS_USER, "user namespace not KEY_NS_USER");
+       succeed_if (keyNameIsSystem (keyName(key)) == 0, "user name is not system");
+       succeed_if (keyIsSystem (key) == 0, "user key is not system");
+       succeed_if (keyNameIsUser (keyName(key)) == 1, "user name is not user");
+       succeed_if (keyIsUser (key) == 1, "user key is not user");
+       keyDel (key);
+
+       key = keyNew ("system", KEY_END);
+       succeed_if (keyNameGetNamespace (keyName(key)) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM");
+       succeed_if (keyGetNamespace (key) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM");
+       succeed_if (keyNameIsSystem (keyName(key)) == 1, "system name is not system");
+       succeed_if (keyIsSystem (key) == 1, "system key is not system");
+       succeed_if (keyNameIsUser (keyName(key)) == 0, "system name is not system");
+       succeed_if (keyIsUser (key) == 0, "system key is not system");
+       keyDel (key);
+
+       key = keyNew ("system/key", KEY_END);
+       succeed_if (keyNameGetNamespace (keyName(key)) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM");
+       succeed_if (keyGetNamespace (key) == KEY_NS_SYSTEM, "system namespace not KEY_NS_SYSTEM");
+       succeed_if (keyNameIsSystem (keyName(key)) == 1, "system name is not system");
+       succeed_if (keyIsSystem (key) == 1, "system key is not system");
+       succeed_if (keyNameIsUser (keyName(key)) == 0, "system name is not system");
+       succeed_if (keyIsUser (key) == 0, "system key is not system");
+       keyDel (key);
+}
+
+
+int main()
+{
+       printf("KEY  STRUCT  TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+       test_keyComparing();
+       test_keyNewSystem();
+       test_keyNewUser();
+       test_keyReference();
+       test_keyType();
+       test_keyName();
+       test_keyValue();
+       test_keyBinary();
+       test_keyComment();
+       test_keyOwner();
+       test_keyInactive();
+       test_keyBelow();
+       test_keyDup();
+       test_keyCopy();
+       test_keyDir();
+       test_keyTime();
+       test_keyMeta();
+       test_keyHelpers();
+       test_keyNamespace();
+
+       printf("\ntest_key RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
diff --git a/tests/test_ks.c b/tests/test_ks.c
new file mode 100644 (file)
index 0000000..ec58563
--- /dev/null
@@ -0,0 +1,2108 @@
+/***************************************************************************
+ *          test_ks.c  -  KeySet struct test suite
+ *                  -------------------
+ *  begin                : Thu Dez 12 2006
+ *  copyright            : (C) 2006 by Markus Raab
+ *  email                : sizon5@gmail.com
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+void test_ksNew()
+{
+       KeySet *ks=0;
+       KeySet * keys = ksNew (15,0);
+       KeySet * config;
+
+       printf("Test ks creation\n");
+       exit_if_fail((ks=ksNew(0)) != 0, "could not create new keyset");
+
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 1, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if(ksGetSize(ks) == 3, "size not correct after 3 keys");
+
+       KeySet *ks2=ksNew (0);
+       ksCopy (ks2, ks);
+       compare_keyset (ks, ks2, 0, 0);
+
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 5, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 6, "could not append a key");
+       succeed_if(ksGetSize(ks) == 6, "could not append 3 more keys");
+
+       ksCopy (ks2, ks);
+       compare_keyset (ks, ks2, 0, 0);
+
+       ksClear (ks2); // useless, just test for double free
+       ksCopy (ks2, ks);
+       compare_keyset (ks, ks2, 0, 0);
+
+       succeed_if(ksDel(ks) == 0, "could not delete keyset");
+
+
+       succeed_if(ksGetSize(keys) == 0, "could not append 3 more keys");
+       succeed_if(ksGetAlloc(keys) == 16, "allocation size wrong");
+       succeed_if(ksDel(keys) == 0, "could not delete keyset");
+
+       config = ksNew (100,
+               keyNew ("user/sw/app/fixedConfiguration/key1", KEY_VALUE, "value1", 0),
+               keyNew ("user/sw/app/fixedConfiguration/key2", KEY_VALUE, "value2", 0),
+               keyNew ("user/sw/app/fixedConfiguration/key3", KEY_VALUE, "value3", 0),0);
+       succeed_if(ksGetSize(config) == 3, "could not append 3 keys in keyNew");
+       succeed_if(ksGetAlloc(config) == 100, "allocation size wrong");
+       keyDel (ksPop (config));
+       succeed_if(ksGetAlloc(config) == 50, "allocation size wrong");
+       keyDel (ksPop (config));
+       succeed_if(ksGetAlloc(config) == 25, "allocation size wrong");
+       keyDel (ksPop (config));
+       succeed_if(ksGetAlloc(config) == 16, "allocation size wrong");
+       succeed_if(ksDel(config) == 0, "could not delete keyset");
+
+       config = ksNew (10,
+               keyNew ("user/sw/app/fixedConfiguration/key1", KEY_VALUE, "value1", 0),
+               keyNew ("user/sw/app/fixedConfiguration/key2", KEY_VALUE, "value2", 0),
+               keyNew ("user/sw/app/fixedConfiguration/key3", KEY_VALUE, "value1", 0),
+               keyNew ("user/sw/app/fixedConfiguration/key4", KEY_VALUE, "value3", 0),0);
+
+       succeed_if(ksGetSize(config) == 4, "could not append 5 keys in keyNew");
+       succeed_if(ksGetAlloc(config) == 16, "allocation size wrong");
+       ksAppendKey(config, keyNew ("user/sw/app/fixedConfiguration/key6", KEY_VALUE, "value4", 0));
+
+       ksClear (ks2);
+       ksCopy (ks2, config);
+       compare_keyset (config, ks2, 0, 0);
+
+       succeed_if(ksDel(config) == 0, "could not delete keyset");
+       succeed_if(ksDel(ks2) == 0, "could not delete keyset");
+}
+
+void test_ksEmpty()
+{
+       printf ("Test empty keysets\n");
+       KeySet *ks;
+       KeySet *ks2;
+       Key *current;
+
+       ks = ksNew(0);
+       succeed_if (ksGetSize (ks) == 0, "size not correct");
+       succeed_if (ksPop (ks) == 0, "pop empty keyset");
+       succeed_if (ksGetSize (ks) == 0, "size not correct");
+       ksDel (ks);
+
+       ks = ksNew (1, current=keyNew("user/test", KEY_END), KS_END);
+       succeed_if (ksGetSize (ks) == 1, "size not correct");
+       succeed_if (ksPop (ks) == current, "pop empty keyset");
+       succeed_if (ksGetSize (ks) == 0, "size not correct");
+       succeed_if (ksPop (ks) == 0, "pop empty keyset");
+       succeed_if (ksGetSize (ks) == 0, "size not correct");
+       keyDel (current);
+       ksDel (ks);
+
+       ks = ksNew (0);
+       ks2 = ksNew (0);
+       succeed_if (ksAppend (ks, ks2) == 0, "could not append empty keyset");
+       succeed_if (ksGetSize (ks) == 0, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       succeed_if (ksPop(ks) == 0, "could not pop empty keyset");
+       succeed_if (ksPop(ks2) == 0, "could not pop empty keyset2");
+       ksDel (ks);
+       ksDel (ks2);
+
+       ks = ksNew (1, current=keyNew("user/test", KEY_END), KS_END);
+       ks2 = ksNew (0);
+       succeed_if (ksGetSize (ks) == 1, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       succeed_if (ksAppend (ks, ks2) == 1, "could not append empty keyset");
+       succeed_if (ksGetSize (ks) == 1, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       succeed_if (ksPop(ks) == current, "could not pop keyset");
+       succeed_if (ksPop(ks2) == 0, "could not pop empty keyset2");
+       succeed_if (ksGetSize (ks) == 0, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       succeed_if (ksAppend (ks, ks2) == 0, "could not append empty keyset");
+       succeed_if (ksGetSize (ks) == 0, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       keyDel (current);
+       ksDel (ks);
+       ksDel (ks2);
+
+
+       ks = ksNew (0);
+       ks2 = ksNew (1, current=keyNew("user/test", KEY_END), KS_END);
+       succeed_if (ksGetSize (ks) == 0, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 1, "empty keyset does not have correct size");
+       succeed_if (ksAppend (ks, ks2) == 1, "could not append empty keyset");
+       succeed_if (ksGetSize (ks) == 1, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 1, "empty keyset does not have correct size");
+       succeed_if (ksPop(ks) == current, "could not pop keyset");
+       succeed_if (ksPop(ks2) == current, "could not pop empty keyset2");
+       succeed_if (ksGetSize (ks) == 0, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       succeed_if (ksAppend (ks, ks2) == 0, "could not append empty keyset");
+       succeed_if (ksGetSize (ks) == 0, "empty keyset does not have correct size");
+       succeed_if (ksGetSize (ks2) == 0, "empty keyset does not have correct size");
+       keyDel (current); // only one keyDel, because ksPop decrements counter
+       ksDel (ks);
+       ksDel (ks2);
+}
+
+#define NR_KEYSETS 10
+
+void test_ksReference()
+{
+       KeySet *ks=0;
+       KeySet *ks1, *ks2;
+       Key *k1, *k2;
+       KeySet *kss[NR_KEYSETS];
+       int i;
+
+       printf("Test reference of key\n");
+
+       ks=ksNew(0);
+       k1 = keyNew("user/aname", KEY_END);
+       succeed_if (keyGetRef(k1) == 0, "reference counter of new key");
+       ksAppendKey(ks,k1);
+       succeed_if (keyGetRef(k1) == 1, "reference counter of inserted key");
+
+       k2 = keyDup (k1);
+
+       succeed_if (keyGetRef(k2) == 0, "reference counter not resetted");
+       ksAppendKey(ks,k2);
+       succeed_if (keyGetRef(k2) == 1, "reference counter not incremented");
+       ksSort (ks);
+
+       ksDel (ks);
+
+       ks=ksNew(5,
+               keyNew ("user/key", KEY_END),
+               keyNew ("system/key", KEY_END),
+               KS_END);
+
+       k1=ksLookupByName(ks, "user/key", 0);
+       k2=ksLookupByName(ks, "system/key", 0);
+       succeed_if (keyGetRef(k1) == 1, "reference counter of new inserted key");
+       succeed_if (keyGetRef(k2) == 1, "reference counter of new inserted key");
+
+       ksDel (ks);
+
+       ks=ksNew(5,
+               keyNew ("user/key", KEY_END),
+               keyNew ("system/key", KEY_END),
+               KS_END);
+
+       k1=ksLookupByName(ks, "user/key", 0);
+       k2=ksLookupByName(ks, "system/key", 0);
+       succeed_if (keyGetRef(k1) == 1, "reference counter of new inserted key");
+       succeed_if (keyGetRef(k2) == 1, "reference counter of new inserted key");
+       ks1=ksDup (ks);
+
+       succeed_if (keyGetRef(k1) == 2, "reference counter after duplication of keyset");
+       succeed_if (keyGetRef(k2) == 2, "reference counter after ksdup");
+       k1=ksPop (ks);
+       succeed_if (keyGetRef(k1) == 1, "reference counter after pop");
+       keyDel (k1);
+       succeed_if (keyGetRef(k1) == 1, "reference counter");
+       succeed_if (keyGetRef(k2) == 2, "reference counter should not be influenced");
+
+       ksDel (ks);
+       succeed_if (keyGetRef(k1) == 1, "reference counter, delete from first keyset");
+       succeed_if (keyGetRef(k2) == 1, "reference counter, delete from first keyset");
+       ksDel (ks1); // k1 and k2 deleted
+
+       ks1=ksNew(0);
+       ks2=ksNew(0);
+       k1=keyNew(0);
+       succeed_if (keyGetRef(k1) == 0, "reference counter of new inserted key");
+       ksAppendKey(ks1, k1);
+       succeed_if (keyGetRef(k1) == 1, "reference counter of new inserted key");
+       ksAppendKey(ks2, k1);
+       succeed_if (keyGetRef(k1) == 2, "reference counter of new inserted key");
+
+       k1=ksPop (ks1);
+       succeed_if (keyGetRef(k1) == 1, "reference counter of new inserted key");
+       succeed_if (keyDel (k1) == 1, "keyDel did not work");
+       succeed_if (keyGetRef(k1) == 1, "reference counter of new inserted key");
+
+       succeed_if (ksDel (ks1) == 0, "could not delete key");
+       succeed_if (ksDel (ks2) == 0, "could not delete key");
+
+
+       kss[0]=ksNew(5,
+               k1=keyNew ("user/key", KEY_END),
+               k2=keyNew ("system/key", KEY_END),
+               KS_END);
+       for (i=1; i< NR_KEYSETS; i++)
+       {
+               succeed_if (keyGetRef(k1) == i, "reference counter");
+               succeed_if (keyGetRef(k2) == 1, "reference counter");
+               kss[i] = ksDup (kss[i-1]);
+               succeed_if (keyGetRef(k2) == 2, "reference counter");
+               ksPop (kss[i-1]);
+               succeed_if (keyGetRef(k2) == 1, "reference counter");
+               succeed_if (keyDel(k2) == 1, "delete key");
+               succeed_if (keyGetRef(k2) == 1, "reference counter");
+               
+       }
+       succeed_if (keyGetRef(k1) == NR_KEYSETS, "reference counter");
+       succeed_if (keyGetRef(k2) == 1, "reference counter");
+
+       for (i=0; i< NR_KEYSETS; i++)
+       {
+               succeed_if (keyGetRef(k1) == NR_KEYSETS-i, "reference counter");
+               ksDel (kss[i]);
+       }
+}
+
+void test_ksResize()
+{
+       int i;
+       KeySet *ks=0;
+       KeySet *copy = ksNew (0);
+
+       printf("Test resize of keyset\n");
+       exit_if_fail((ks=ksNew(0)) != 0, "could not create new keyset");
+       for (i=0; i< 100; i++)
+       {
+               ksAppendKey(ks, keyNew (KEY_END));
+               if (i >= 63) { succeed_if(ksGetAlloc(ks) == 128, "allocation size wrong"); }
+               else if (i >= 31) { succeed_if(ksGetAlloc(ks) == 64, "allocation size wrong"); }
+               else if (i >= 15) { succeed_if(ksGetAlloc(ks) == 32, "allocation size wrong"); }
+               else if (i >= 0) { succeed_if(ksGetAlloc(ks) == 16, "allocation size wrong"); }
+       }
+       succeed_if(ksGetSize(ks) == 100, "could not append 100 keys");
+       succeed_if(ksGetAlloc(ks) == 128, "allocation size wrong");
+       for (i=100; i >= 0; i--)
+       {
+               keyDel (ksPop(ks));
+               if (i >= 64) { succeed_if(ksGetAlloc(ks) == 128, "allocation size wrong"); }
+               else if (i >= 32) { succeed_if(ksGetAlloc(ks) == 64, "allocation size wrong"); }
+               else if (i >= 16) { succeed_if(ksGetAlloc(ks) == 32, "allocation size wrong"); }
+               else if (i >= 0) { succeed_if(ksGetAlloc(ks) == 16, "allocation size wrong"); }
+       }
+       succeed_if(ksGetSize(ks) == 0, "could not pop 100 keys");
+       succeed_if(ksGetAlloc(ks) == 16, "allocation size wrong");
+       ksDel (ks);
+       
+       exit_if_fail((ks=ksNew(0)) != 0, "could not create new keyset");
+       ksResize (ks, 101);
+       succeed_if(ksGetAlloc(ks) == 101, "allocation size wrong");
+       for (i=0; i< 100; i++)
+       {
+               ksAppendKey(ks, keyNew (KEY_END));
+       }
+       succeed_if(ksGetSize(ks) == 100, "could not append 100 keys");
+       succeed_if(ksGetAlloc(ks) == 101, "allocation size wrong");
+       ksDel (ks);
+
+       ks =
+#include "keyset.c"
+
+       succeed_if(ksGetSize(ks) == 102, "Problem loading keyset with 102 keys");
+       succeed_if(ksGetAlloc(ks) == 128, "alloc size wrong");
+
+       ksCopy (copy, ks);
+       compare_keyset(copy, ks, 0, 0);
+
+       ksClear (copy); // useless, just test for double free
+       ksCopy (copy, ks);
+       compare_keyset(copy, ks, 0, 0);
+
+       ksDel (copy);
+       ksDel (ks);
+}
+
+void test_ksDup()
+{
+       KeySet *ks=0;
+       KeySet *other=0;
+
+       printf("Test ks duplication\n");
+
+       exit_if_fail((ks=ksNew(0)) != 0, "could not create new keyset");
+       other=ksDup(ks);
+       succeed_if(other, "other creation failed");
+       succeed_if(ksGetSize(ks) == 0, "ks has keys");
+       succeed_if(ksGetSize(other) == 0, "other has keys");
+       ksDel (other);
+       ksDel (ks);
+
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       other=ksDup(ks);
+       succeed_if(other, "other creation failed");
+       succeed_if(ksGetSize(ks) == 1, "ks has no keys");
+       succeed_if(ksGetSize(other) == 1, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       other=ksDup(ks);
+       succeed_if(other, "other creation failed");
+       succeed_if(ksGetSize(ks) == 4, "ks has no keys");
+       succeed_if(ksGetSize(other) == 4, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+       
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       other=ksDup(ks);
+       succeed_if(other, "other creation failed");
+       keyDel (ksPop (other));
+       succeed_if(ksGetSize(ks) == 4, "ks has no keys");
+       succeed_if(ksGetSize(other) == 3, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+       
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       other=ksDup(ks);
+       succeed_if(other, "other creation failed");
+       keyDel(ksPop (other));
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 5, "could not append a key");
+       succeed_if(ksGetSize(ks) == 5, "ks has no keys");
+       succeed_if(ksGetSize(other) == 3, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+}
+
+void test_ksCopy()
+{
+       KeySet *ks=0;
+       KeySet *other=0;
+
+       printf("Test ks copy\n");
+
+       other = ksNew(0);
+       exit_if_fail((ks=ksNew(0)) != 0, "could not create new keyset");
+       succeed_if(ksCopy(other,ks)==1, "Copy failed");
+       succeed_if(other, "other creation failed");
+       succeed_if(ksGetSize(ks) == 0, "ks has keys");
+       succeed_if(ksGetSize(other) == 0, "other has keys");
+       ksDel (other);
+       ksDel (ks);
+
+       other = ksNew(0);
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if(ksCopy(other,ks)==1, "Copy failed");
+       succeed_if(other, "other creation failed");
+       succeed_if(ksGetSize(ks) == 1, "ks has no keys");
+       succeed_if(ksGetSize(other) == 1, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+
+       other = ksNew(0);
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       succeed_if(ksCopy(other,ks)==1, "Copy failed");
+       succeed_if(other, "other creation failed");
+       succeed_if(ksGetSize(ks) == 4, "ks has no keys");
+       succeed_if(ksGetSize(other) == 4, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+
+       other = ksNew(0);
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       succeed_if(ksCopy(other,ks)==1, "Copy failed");
+       succeed_if(other, "other creation failed");
+       keyDel (ksPop (other));
+       succeed_if(ksGetSize(ks) == 4, "ks has no keys");
+       succeed_if(ksGetSize(other) == 3, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+
+       other = ksNew(0);
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       succeed_if(ksCopy(other,ks)==1, "Copy failed");
+       succeed_if(other, "other creation failed");
+       keyDel(ksPop (other));
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 5, "could not append a key");
+       succeed_if(ksGetSize(ks) == 5, "ks has no keys");
+       succeed_if(ksGetSize(other) == 3, "other has no keys");
+       ksDel (other);
+       ksDel (ks);
+
+       other = ksNew(0);
+       exit_if_fail((ks=ksNew(1, keyNew(0), 0)) != 0, "could not create new keyset");
+       succeed_if (ksAppendKey(ks,keyNew("user/test", KEY_END)) == 2, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 3, "could not append a key");
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 4, "could not append a key");
+       succeed_if(ksCopy(other,ks)==1, "Copy failed");
+       succeed_if(other, "other creation failed");
+       keyDel(ksPop (other));
+       succeed_if (ksAppendKey(ks,keyNew(0)) == 5, "could not append a key");
+       succeed_if(ksGetSize(ks) == 5, "ks has no keys");
+       succeed_if(ksGetSize(other) == 3, "other has no keys");
+
+       succeed_if(ksCopy(ks,0)==0, "Clear failed");
+       succeed_if(ksGetSize(ks) == 0, "ks has keys");
+
+       succeed_if(ksCopy(other,0)==0, "Clear failed");
+       succeed_if(ksGetSize(other) == 0, "other has keys");
+       ksDel (other);
+       ksDel (ks);
+}
+
+void test_ksIterate()
+{
+       KeySet *ks=ksNew(0);
+       KeySet *other=ksNew(0);
+       Key * key;
+       int i;
+       char name [] = "user/n";
+       char output [] = "2 key not on its place";
+
+       printf("Test keyset iterate\n");
+       ksAppendKey(ks,keyNew("user/1", KEY_END));
+       ksAppendKey(ks,keyNew("user/2", KEY_END));
+       ksAppendKey(ks,keyNew("user/3", KEY_END));
+       ksAppendKey(ks,keyNew("user/4", KEY_END));
+       ksAppendKey(ks,keyNew("user/5", KEY_END));
+       succeed_if(ksGetSize(ks) == 5, "could not append 5 keys");
+
+       key = ksPop(ks);
+       succeed_if(strcmp(keyName(key), "user/5") == 0, "1 key not on first place");
+       succeed_if (keyDel (key) == 0, "could not del popped key");
+
+       succeed_if(ksAppend(other,ks) == 4, "could not append keys");
+
+       for (i=4; i>=1; i--)
+       {
+               key = ksPop(other);
+               succeed_if (key != 0, "got null pointer key");
+               name[5] = '0' + i;
+               output[0] = '0' + i;
+               succeed_if(strcmp(keyName(key), name) == 0, output);
+               keyDel (key);
+       }
+       
+       succeed_if (ksAppendKey(other, keyNew ("user/3", KEY_END)) == 1, "could not append one key");
+       key = ksPop(other);
+       succeed_if (key != 0, "got null pointer key");
+       succeed_if(strcmp(keyName(key), "user/3") == 0, "only key to pop not found");
+       succeed_if (keyDel (key) == 0, "could not del popped key");
+       ksDel(other);
+       ksDel(ks);
+
+       ks = ksNew(10,
+               keyNew("user/0", KEY_END),
+               keyNew("user/1", KEY_END),
+               keyNew("user/2", KEY_END),
+               keyNew("user/3", KEY_END),
+               0);
+       
+       other = ksNew(10,
+               keyNew("user/4", KEY_END),
+               keyNew("user/5", KEY_END),
+               keyNew("user/6", KEY_END),
+               keyNew("user/7", KEY_END),
+               0);
+
+       succeed_if(ksAppend(ks,other) == 8, "could not append keys");
+
+       for (i=7; i >= 0; i--)
+       {
+               key = ksPop(ks);
+               succeed_if (key != 0, "got null pointer key");
+               name[5] = '0' + i;
+               output[0] = '0' + i;
+               succeed_if(strcmp(keyName(key), name) == 0, output);
+               keyDel (key);
+       }
+       ksDel (ks);
+       ksDel (other);
+}
+
+void test_ksCursor()
+{
+       KeySet *ks=ksNew(0);
+       Key * key;
+       cursor_t cursor;
+       int i;
+       char name [] = "user/n";
+       char output [] = "n key not on its place";
+
+       printf("Test keyset cursor\n");
+
+       ksAppendKey(ks,keyNew("user/1", KEY_END));
+       ksAppendKey(ks,keyNew("user/2", KEY_END));
+       ksAppendKey(ks,keyNew("user/3", KEY_END));
+       cursor = ksGetCursor (ks);
+       ksAppendKey(ks,keyNew("user/4", KEY_END));
+       ksAppendKey(ks,keyNew("user/5", KEY_END));
+       succeed_if(ksGetSize(ks) == 5, "could not append 5 keys");
+
+       succeed_if (cursor == ksGetCursor(ks), "cursor after appending");
+       ksSetCursor (ks, cursor);
+       succeed_if (cursor == ksGetCursor(ks), "cursor set to 3");
+
+       cursor = ksGetCursor (ks);
+       key = ksPop(ks);
+       succeed_if (cursor == ksGetCursor(ks), "cursor should stay the same");
+       succeed_if(strcmp(keyName(key), "user/5") == 0, "1 key not on first place");
+       succeed_if (keyDel (key) == 0, "could not del popped key");
+
+       ksRewind (ks);
+       for (i=0; i<5; i++)
+       {
+               key = ksNext(ks);
+               if (i==1)
+               {
+                       cursor = ksGetCursor (ks);
+                       name[5] = '0' + i;
+                       output[0] = '0' + i;
+               }
+       }
+       ksSetCursor(ks, cursor);
+       key = ksCurrent (ks);
+
+       ksDel(ks);
+
+       ks = ksNew(10,
+               keyNew("user/0", KEY_END),
+               keyNew("user/1", KEY_END),
+               keyNew("user/2", KEY_END),
+               keyNew("user/3", KEY_END),
+               0);
+
+       ksRewind (ks);
+       for (i=0; i < 4; i++)
+       {
+               key = ksNext(ks);
+               if (i==1)
+               {
+                       cursor = ksGetCursor (ks);
+                       name[5] = '0' + i;
+                       output[0] = '0' + i;
+               }
+       }
+
+       ksSetCursor(ks, cursor);
+       key = ksCurrent (ks);
+       succeed_if(!strcmp (keyName(key), name), output);
+
+       ksDel (ks);
+}
+
+void test_ksSort()
+{
+       KeySet  *ks;
+       Key     *key, *k1, *k2;
+       int     i;
+
+       printf("Test ks sort\n");
+
+       printf ("Sort with no keyNeedRemove set\n");
+       ks=ksNew(0);
+       ksAppendKey(ks, keyNew("user/bname", KEY_END));
+       ksAppendKey(ks, keyNew("user/aname", KEY_END));
+       ksAppendKey(ks, keyNew("user/cname", KEY_END));
+       
+       ksSort (ks);
+       ksRewind(ks);
+       key = ksNext(ks);
+       succeed_if (strcmp (keyName (key), "user/aname") == 0, "a should be 1.");
+       
+       key = ksNext(ks);
+       succeed_if (strcmp (keyName (key), "user/bname") == 0, "b should be 2.");
+       
+       key = ksNext(ks);
+       succeed_if (strcmp (keyName (key), "user/cname") == 0, "c should be 3.");
+       ksDel (ks);
+       
+       ks=ksNew(0);
+       ksAppendKey(ks, keyNew("user/a", KEY_END));
+       ksAppendKey(ks, keyNew("user/e", KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/d", KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/g", KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_END));
+       ksAppendKey(ks, keyNew("user/f", KEY_END));
+
+       ksSort (ks);
+       ksRewind (ks);
+       for (i=0; (key=ksNext(ks)) != 0; i++)
+       {
+               switch (i)
+               {
+               case 0: succeed_if (strcmp (keyName (key), "user/a") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 1: succeed_if (strcmp (keyName (key), "user/b") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 2: succeed_if (strcmp (keyName (key), "user/b") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 3: succeed_if (strcmp (keyName (key), "user/c") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 4: succeed_if (strcmp (keyName (key), "user/c") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 5: succeed_if (strcmp (keyName (key), "user/d") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 6: succeed_if (strcmp (keyName (key), "user/e") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 7: succeed_if (strcmp (keyName (key), "user/f") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 8: succeed_if (strcmp (keyName (key), "user/g") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 9: succeed_if (strcmp (keyName (key), "user/h") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 10:succeed_if (strcmp (keyName (key), "user/h") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               default:succeed_if (0, "should not reach");
+                       break;
+               }
+       }
+       ksDel (ks);
+
+       printf ("Sort with some keyNeedRemove set\n");
+       ks=ksNew(0);
+       k1 = keyNew("user/aname", KEY_END);
+       ksAppendKey(ks,k1);
+
+       k2 = keyDup (k1);
+       keyRemove(k2);
+
+       succeed_if (keyGetRef(k2) == 0, "reference counter not resetted");
+       ksAppendKey(ks,k2);
+       succeed_if (keyGetRef(k2) == 1, "reference counter not incremented");
+       ksSort (ks);
+
+       ksRewind (ks);
+       key = ksNext(ks);
+       // printf ("%d\n", keyNeedRemove (key));
+       succeed_if (keyNeedRemove (key) == 1, "Removed key should be on first position");
+       ksDel(ks);
+       
+       ks=ksNew(0);
+       k1 = keyNew("user/aname", KEY_END);
+       k2 = keyDup (k1);
+       keyRemove(k2);
+       ksAppendKey(ks,k2);
+       ksAppendKey(ks,k1);
+       ksSort (ks);
+
+       ksRewind (ks);
+       key = ksNext(ks);
+       succeed_if (keyNeedRemove (key) == 1, "Removed key should be on first position");
+       ksDel(ks);
+
+       ks=ksNew(0);
+       ksAppendKey(ks, keyNew("user/a", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/e", KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/d", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/g", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_END));
+       ksAppendKey(ks, keyNew("user/f", KEY_REMOVE, KEY_END));
+
+       ksSort (ks);
+       ksRewind (ks);
+       // output_keyset(ks,0);
+       for (i=0; (key=ksNext(ks)) != 0; i++)
+       {
+               switch (i)
+               {
+               case 0: succeed_if (strcmp (keyName (key), "user/h") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 1: succeed_if (strcmp (keyName (key), "user/g") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 2: succeed_if (strcmp (keyName (key), "user/f") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 3: succeed_if (strcmp (keyName (key), "user/d") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 4: succeed_if (strcmp (keyName (key), "user/c") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 5: succeed_if (strcmp (keyName (key), "user/b") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 6: succeed_if (strcmp (keyName (key), "user/a") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 7: succeed_if (strcmp (keyName (key), "user/b") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 8: succeed_if (strcmp (keyName (key), "user/c") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 9: succeed_if (strcmp (keyName (key), "user/e") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 10:succeed_if (strcmp (keyName (key), "user/h") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               default:succeed_if (0, "should not reach");
+                       break;
+               }
+       }
+       ksDel (ks);
+
+       
+       printf ("Sort with all keyNeedRemove set\n");
+       ks=ksNew(0);
+       ksAppendKey(ks, keyNew("user/a", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/e", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/b/a", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/d", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/c/a", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/g", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/h/a", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/f", KEY_REMOVE, KEY_END));
+
+       ksSort(ks);
+       ksRewind (ks);
+       // output_keyset(ks,0);
+       for (i=0; (key=ksNext(ks)) != 0; i++)
+       {
+               switch (i)
+               {
+               case 0: succeed_if (strcmp (keyName (key), "user/h/a") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 1: succeed_if (strcmp (keyName (key), "user/h") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 2: succeed_if (strcmp (keyName (key), "user/g") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 3: succeed_if (strcmp (keyName (key), "user/f") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 4: succeed_if (strcmp (keyName (key), "user/e") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 5: succeed_if (strcmp (keyName (key), "user/d") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 6: succeed_if (strcmp (keyName (key), "user/c/a") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 7: succeed_if (strcmp (keyName (key), "user/c") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 8: succeed_if (strcmp (keyName (key), "user/b/a") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 9: succeed_if (strcmp (keyName (key), "user/b") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 10:succeed_if (strcmp (keyName (key), "user/a") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               default:succeed_if (0, "should not reach");
+                       break;
+               }
+       }
+       ksDel (ks);
+       
+       printf ("Sort with mixed keyNeedRemove set and subdirs\n");
+       ks=ksNew(0);
+       ksAppendKey(ks, keyNew("user/dir1/key1", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/dir1/key2", KEY_END));
+       ksAppendKey(ks, keyNew("user/dir1/key3", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/dir2",      KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/dir2/key1", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/dir3/key1", KEY_END));
+       ksAppendKey(ks, keyNew("user/dir3",      KEY_END));
+       ksAppendKey(ks, keyNew("user/dir3/key2", KEY_END));
+       ksAppendKey(ks, keyNew("user/dir4",      KEY_END));
+       ksAppendKey(ks, keyNew("user/dir5/key1", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/dir6/key1", KEY_END));
+
+       ksSort(ks);
+       ksRewind (ks);
+       // output_keyset(ks,0);
+       for (i=0; (key=ksNext(ks)) != 0; i++)
+       {
+               switch (i)
+               {
+               case 0: succeed_if (strcmp (keyName (key), "user/dir5/key1") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 1: succeed_if (strcmp (keyName (key), "user/dir2/key1") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 2: succeed_if (strcmp (keyName (key), "user/dir2") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 3: succeed_if (strcmp (keyName (key), "user/dir1/key3") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 4: succeed_if (strcmp (keyName (key), "user/dir1/key1") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 1, "wrong removed key information");
+                       break;
+               case 5: succeed_if (strcmp (keyName (key), "user/dir1/key2") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 6: succeed_if (strcmp (keyName (key), "user/dir3") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 7: succeed_if (strcmp (keyName (key), "user/dir3/key1") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 8: succeed_if (strcmp (keyName (key), "user/dir3/key2") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 9: succeed_if (strcmp (keyName (key), "user/dir4") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               case 10:succeed_if (strcmp (keyName (key), "user/dir6/key1") == 0, "wrong name found.");
+                       succeed_if (keyNeedRemove (key) == 0, "wrong removed key information");
+                       break;
+               default:succeed_if (0, "should not reach");
+                       break;
+               }
+       }
+       ksDel (ks);
+}
+
+void ksUnsort (KeySet *ks)
+{
+       Key *cur;
+       size_t size = 0;
+       KeySet *randks=ksNew(0); /*This is the final randomized keyset*/
+       KeySet *tempks=ksNew(0); /*Temporary storage for keys not chosen to be inserted*/
+
+       while (ksGetSize(ks) > 0)
+       {
+               ksRewind(ks);
+               size = ksGetSize(ks);
+               /* printf ("iterating %d\n", size); */
+               while ((cur=ksPop(ks)) != 0)
+               {
+                       /* printf ("\titerating %s\n", keyName(cur)); */
+                       if (!(rand()%size)) ksAppendKey(randks, cur);
+                       else ksAppendKey(tempks,cur);
+               }
+               ksAppend(ks, tempks);
+               ksCopy(tempks,0);
+       }
+
+       ksCopy (ks, randks);
+
+       ksDel (randks);
+       ksDel (tempks);
+}
+
+void test_ksLookup()
+{
+       printf ("Test lookup\n");
+
+       int i,j;
+       Key *cur=0;
+       Key *found=0;
+       Key *k[1000];
+       KeySet *ks = ksNew (30,
+               k[0]=keyNew ("user/rem3", KEY_REMOVE, KEY_DIR, KEY_END),
+               k[1]=keyNew ("user/rem2", KEY_REMOVE, KEY_DIR, KEY_END),
+               k[2]=keyNew ("user/rem1/key2", KEY_REMOVE, KEY_END),
+               k[3]=keyNew ("user/rem1/key1", KEY_REMOVE, KEY_END),
+               k[4]=keyNew ("user/rem1", KEY_REMOVE, KEY_DIR, KEY_END),
+               k[5]=keyNew ("user/dir1", KEY_DIR, KEY_END),
+               k[6]=keyNew ("user/dir1/key1", KEY_VALUE, "value1", KEY_END),
+               k[7]=keyNew ("user/dir1/key2", KEY_VALUE, "value2", KEY_END),
+               k[8]=keyNew ("user/dir1/key3", KEY_VALUE, "value3", KEY_END),
+               k[9]=keyNew ("user/dir1/key4", KEY_VALUE, "value4", KEY_END),
+               k[10]=keyNew ("user/dir1/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               k[11]=keyNew ("user/dir1/.inactive2", KEY_COMMENT, "additional information", KEY_END),
+               k[12]=keyNew ("user:max/dir2", KEY_DIR, KEY_END),
+               k[13]=keyNew ("user:max/dir2/key1", KEY_VALUE, "value1", KEY_END),
+               k[14]=keyNew ("user/dir2/key2", KEY_VALUE, "value2", KEY_END),
+               k[15]=keyNew ("user/dir2/key3", KEY_VALUE, "value3", KEY_END),
+               k[16]=keyNew ("user:hugo/dir2/key4", KEY_VALUE, "value4", KEY_END),
+               k[17]=keyNew ("user/dir3", KEY_DIR, KEY_END),
+               k[18]=keyNew ("user/dir3/key1", KEY_VALUE, "value1", KEY_END),
+               k[19]=keyNew ("user:sb/dir3/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               k[20]=keyNew ("user/dir3/.inactive2", KEY_COMMENT, "a users comment", KEY_END),
+               k[21]=keyNew ("user/dir4", KEY_DIR, KEY_END),
+               k[22]=keyNew ("user/dir5", KEY_DIR, KEY_END),
+               KS_END);
+
+       /* key 5 */
+       k[23] = keyNew ("user/DiR1", KEY_END);
+       /* key 6 */
+       k[24] = keyNew ("user/DiR1/KEY1", KEY_END);
+       k[25] = keyNew ("user:wrongowner/DiR1/KEY1", KEY_END);
+       k[26] = keyNew ("user:nop/DiR1/KEY1", KEY_END);
+       k[27] = keyNew ("user:wrongowner/dir1/key1", KEY_END);
+       k[28] = keyNew ("user:nop/dir1/key1", KEY_END);
+       /*key 13 */
+       k[29] = keyNew ("user:wrongowner/dir2/key1", KEY_END);
+       k[30] = keyNew ("user/dir2/key1", KEY_END);
+       k[31] = keyNew ("user:max/dir2/key1", KEY_END);
+
+       srand(23);
+
+       succeed_if (ksLookup(0, k[23], 0) == 0, "null pointer");
+       succeed_if (ksLookup(ks, 0, 0) == 0, "null pointer");
+
+       for (i=0; i<100; i++)
+       {
+               ksUnsort(ks);
+               for (j=0; j<5; j++)
+                       succeed_if (ksLookup(ks, k[j], 0)==0, "found removed key");
+               for (j=5; j<23;j++)
+                       succeed_if (ksLookup(ks, k[j], 0)==k[j], "did not found key");
+               succeed_if (ksLookup(ks, k[23], KDB_O_NOCASE) == k[5], "did not found key");
+               succeed_if (ksLookup(ks, k[23], 0) == 0, "found wrong key");
+               succeed_if (ksLookup(ks, k[24], KDB_O_NOCASE) == k[6], "did not found key");
+               succeed_if (ksLookup(ks, k[24], 0) == 0, "found wrong key");
+               succeed_if (ksLookup(ks, k[25], KDB_O_NOCASE) == k[6], "did not found key");
+               succeed_if (ksLookup(ks, k[25], KDB_O_WITHOWNER|KDB_O_NOCASE) == 0, "found wrong key");
+               succeed_if (ksLookup(ks, k[28], 0) == k[6], "did not found key");
+               succeed_if (ksLookup(ks, k[28], KDB_O_WITHOWNER) == 0, "found wrong key");
+               succeed_if (ksLookup(ks, k[31], KDB_O_WITHOWNER) == k[13], "did not found key");
+               /* Empty lines to add more tests:
+               succeed_if (ksLookup(ks, k[], ) == k[], "did not found key");
+               succeed_if (ksLookup(ks, k[], ) == 0, "found wrong key");
+               */
+       }
+
+       ksDel (ks);
+       for (i=23; i<32;i++) keyDel (k[i]);
+}
+
+void test_ksLookupByName()
+{
+       printf ("Test lookup by name\n");
+
+       int i,j;
+       Key *cur=0;
+       Key *found=0;
+       char *name[1000];
+       Key *k[1000];
+       KeySet *ks = ksNew (30,
+               k[0]=keyNew (name[0] = "user/rem3", KEY_REMOVE, KEY_DIR, KEY_END),
+               k[1]=keyNew (name[1] = "user/rem2", KEY_REMOVE, KEY_DIR, KEY_END),
+               k[2]=keyNew (name[2] = "user/rem1/key2", KEY_REMOVE, KEY_END),
+               k[3]=keyNew (name[3] = "user/rem1/key1", KEY_REMOVE, KEY_END),
+               k[4]=keyNew (name[4] = "user/rem1", KEY_REMOVE, KEY_DIR, KEY_END),
+               k[5]=keyNew (name[5] = "user/dir1", KEY_DIR, KEY_END),
+               k[6]=keyNew (name[6] = "user/dir1/key1", KEY_VALUE, "value1", KEY_END),
+               k[7]=keyNew (name[7] = "user/dir1/key2", KEY_VALUE, "value2", KEY_END),
+               k[8]=keyNew (name[8] = "user/dir1/key3", KEY_VALUE, "value3", KEY_END),
+               k[9]=keyNew (name[9] = "user/dir1/key4", KEY_VALUE, "value4", KEY_END),
+               k[10]=keyNew (name[10] ="user/dir1/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               k[11]=keyNew (name[11] ="user/dir1/.inactive2", KEY_COMMENT, "additional information", KEY_END),
+               k[12]=keyNew (name[12] ="user:max/dir2", KEY_DIR, KEY_END),
+               k[13]=keyNew (name[13] ="user:max/dir2/key1", KEY_VALUE, "value1", KEY_END),
+               k[14]=keyNew (name[14] ="user/dir2/key2", KEY_VALUE, "value2", KEY_END),
+               k[15]=keyNew (name[15] ="user/dir2/key3", KEY_VALUE, "value3", KEY_END),
+               k[16]=keyNew (name[16] ="user:hugo/dir2/key4", KEY_VALUE, "value4", KEY_END),
+               k[17]=keyNew (name[17] ="user/dir3", KEY_DIR, KEY_END),
+               k[18]=keyNew (name[18] ="user/dir3/key1", KEY_VALUE, "value1", KEY_END),
+               k[19]=keyNew (name[19] ="user:sb/dir3/.inactive1", KEY_COMMENT, "key is inactive", KEY_END),
+               k[20]=keyNew (name[20] ="user/dir3/.inactive2", KEY_COMMENT, "a users comment", KEY_END),
+               k[21]=keyNew (name[21] ="user/dir4", KEY_DIR, KEY_END),
+               k[22]=keyNew (name[22] ="user/dir5", KEY_DIR, KEY_END),
+               KS_END);
+
+       name[23] = "user/DiR1";
+       name[24] = "user/DiR1/KEY1";
+       name[25] = "user:wrongowner/DiR1/KEY1";
+       name[26] = "user:nop/DiR1/KEY1";
+       name[27] = "user:wrongowner/dir1/key1";
+       name[28] = "user:nop/dir1/key1";
+       name[29] = "user:wrongowner/dir2/key1";
+       name[30] = "user/dir2/key1";
+       name[31] = "user:max/dir2/key1";
+
+       srand(23);
+
+       succeed_if (ksLookupByName(0, name[23], 0) == 0, "null pointer");
+       succeed_if (ksLookup(ks, 0, 0) == 0, "null pointer");
+
+       for (i=0; i<100; i++)
+       {
+               ksUnsort(ks);
+               for (j=0; j<5; j++)
+                       succeed_if (ksLookupByName(ks, name[j], 0)==0, "found removed key");
+               for (j=5; j<23;j++)
+                       succeed_if (ksLookupByName(ks, name[j], 0)==k[j], "did not found key");
+               succeed_if (ksLookupByName(ks, name[23], KDB_O_NOCASE) == k[5], "did not found key");
+               succeed_if (ksLookupByName(ks, name[23], 0) == 0, "found wrong key");
+               succeed_if (ksLookupByName(ks, name[24], KDB_O_NOCASE) == k[6], "did not found key");
+               succeed_if (ksLookupByName(ks, name[24], 0) == 0, "found wrong key");
+               succeed_if (ksLookupByName(ks, name[25], KDB_O_NOCASE) == k[6], "did not found key");
+               succeed_if (ksLookupByName(ks, name[25], KDB_O_WITHOWNER|KDB_O_NOCASE) == 0, "found wrong key");
+               succeed_if (ksLookupByName(ks, name[28], 0) == k[6], "did not found key");
+               succeed_if (ksLookupByName(ks, name[28], KDB_O_WITHOWNER) == 0, "found wrong key");
+               succeed_if (ksLookupByName(ks, name[31], KDB_O_WITHOWNER) == k[13], "did not found key");
+               /* Empty lines to add more tests:
+               succeed_if (ksLookupByName(ks, name[], ) == name[], "did not found key");
+               succeed_if (ksLookupByName(ks, name[], ) == 0, "found wrong key");
+               */
+       }
+
+       ksDel (ks);
+}
+
+
+void test_ksLookupName()
+{
+       Key * found;
+       KeySet *ks= ksNew(0);
+       
+       printf ("Test lookup functions\n");
+       succeed_if (ksNeedSort (ks) == 1, "sort state not correct");
+
+       ksAppendKey(ks, keyNew("user/domain/key",  KEY_VALUE, "domainvalue",
+               KEY_OWNER, "markus", KEY_END));
+       ksAppendKey(ks, keyNew("user/single/key",  KEY_VALUE, "singlevalue", KEY_END));
+       ksAppendKey(ks, keyNew("user/named/key",   KEY_VALUE, "myvalue", KEY_END));
+       ksAppendKey(ks, keyNew("system/named/key", KEY_VALUE, "syskey",  KEY_END));
+       ksAppendKey(ks, keyNew("system/sysonly/key", KEY_VALUE, "sysonlykey",  KEY_END));
+       ksAppendKey(ks, keyNew("user/named/bin", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "binary\1\2data", KEY_END));
+       ksAppendKey(ks, keyNew("system/named/bin", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "sys\1bin\2", KEY_END));
+       ksAppendKey(ks, keyNew("system/named/key", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "syskey", KEY_END));
+       succeed_if (ksGetSize(ks) == 8, "could not append all keys");
+
+       succeed_if (ksNeedSort (ks) == 1, "sort state not correct");
+
+       // a positive testcase
+       found = ksLookupByName (ks, "user/named/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (ksNeedSort (ks) == 0, "sort state not correct");
+       ksAppendKey(ks, keyNew("user/single/key",  KEY_VALUE, "singlevalue", KEY_END));
+       succeed_if (ksNeedSort (ks) == 1, "sort state not correct");
+
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+       
+       // here you can't find the keys
+       succeed_if (ksLookupByName (ks, "named/key", 0) == 0, "not valid keyname");
+       succeed_if (ksNeedSort (ks) == 0, "sort state not correct");
+       succeed_if (ksLookupByName (ks, "u/named/key", 0) == 0, "not valid keyname");
+       succeed_if (ksLookupByName (ks, "usea/named/key", 0) == 0, "not valid keyname");
+       succeed_if (ksLookupByName (ks, " user/named/key", 0) == 0, "found key with bad prefix");
+
+       succeed_if (ksLookupByName (ks, "user/named/Key", 0) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "User/Named/key", 0) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "User/named/key", 0) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "user/NAMED/key", 0) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "USER/NAMED/KEY", 0) == 0, "found wrong case key");
+       
+       succeed_if (ksLookupByName (ks, "user/named/keys", 0) == 0, "wrong postfix");
+       succeed_if (ksLookupByName (ks, "user/named/key_", 0) == 0, "wrong postfix");
+
+       succeed_if (ksLookupByName (ks, "user/named/k/ey", 0) == 0, "seperation that should be");
+       succeed_if (ksLookupByName (ks, "user/na/med/key", 0) == 0, "seperation that should be");
+
+       succeed_if (ksLookupByName (ks, "system/domain/key", 0) == 0, "found key in wrong domain");
+       
+       //now try to find them, and compare value
+       found = ksLookupByName (ks, "user/domain/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "user/domain/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "domainvalue") == 0, "not correct value in found key");
+       
+       found = ksLookupByName (ks, "user/single/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "user/single/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "singlevalue") == 0, "not correct value in found key");
+       
+       found = ksLookupByName (ks, "system/named/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "system/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "syskey") == 0, "not correct value in found key");
+       
+       found = ksLookupByName (ks, "user/named/bin", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "user/named/bin") == 0, "name not correct in found key");
+       succeed_if (strncmp (keyValue(found), "binary\1\2data",10) == 0, "not correct value in found key");
+
+       found = ksLookupByName (ks, "user/named/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find same key again");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+
+       printf ("Test nocase lookup functions\n");
+       found = ksLookupByName (ks, "user/named/key", KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find same key again, nocase");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+
+       found = ksLookupByName (ks, "user/NameD/KeY", KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+
+       found = ksLookupByName (ks, "user/NameD/KEY", KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+       
+       printf ("Test cascading lookup functions\n");
+       found = ksLookupByName (ks, "/named/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "cascading search failed");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+
+       found = ksLookupByName (ks, "/single/Key", KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (strcmp (keyName(found), "user/single/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "singlevalue") == 0, "not correct value in found key");
+
+       found = ksLookupByName (ks, "/sysonly/key", 0);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (strcmp (keyName(found), "system/sysonly/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "sysonlykey") == 0, "not correct value in found key");
+
+       succeed_if (ksLookupByName (ks, "/named/", 0) == 0, "found part of key with cascading");
+       succeed_if (ksLookupByName (ks, "/named/keyd", 0) == 0, "found part of key with cascading, bad postfix");
+       
+       printf ("Test domain lookup functions\n");
+       found = ksLookupByName (ks, "user:markus/domain/key", KDB_O_WITHOWNER);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find domain key");
+       succeed_if (strcmp (keyValue(found), "domainvalue") == 0, "not correct value in domain key");
+       succeed_if (ksLookupByName (ks, "user:hugo/domain/key", KDB_O_WITHOWNER) == 0, "found key in wrong domain");
+       succeed_if (ksLookupByName (ks, "user:y/domain/key", KDB_O_WITHOWNER) == 0, "found key in wrong domain");
+       succeed_if (ksLookupByName (ks, "user:markuss/domain/key", KDB_O_WITHOWNER) == 0, "found key in wrong domain");
+       succeed_if (ksLookupByName (ks, "user:marku/domain/key", KDB_O_WITHOWNER) == 0, "found key in wrong domain");
+
+       ksDel(ks);
+       
+}
+
+void test_ksLookupNameRemove()
+{
+       Key * found;
+       KeySet *ks=ksNew(0);
+       printf ("Test lookup functions with removed keys\n");
+       ksAppendKey(ks, keyNew("user/a", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/e", KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/d", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/g", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_REMOVE, KEY_END));
+       ksAppendKey(ks, keyNew("user/h", KEY_END));
+       ksAppendKey(ks, keyNew("user/f", KEY_REMOVE, KEY_END));
+       
+       found = ksLookupByName (ks, "user/e", 0);
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/e") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/b", 0);
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/b") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/c", 0);
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/c") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/h", 0);
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/h") == 0, "name not correct in found key");
+
+       succeed_if (ksLookupByName (ks, "user/nonexists", 0) == 0, "found nonexists key");
+       succeed_if (ksLookupByName (ks, "user/g", 0) == 0, "found removed key");
+       succeed_if (ksLookupByName (ks, "user/f", 0) == 0, "found removed key");
+
+       ksDel (ks);
+}
+
+void test_ksLookupNameAll()
+{
+       Key * found;
+       cursor_t cursor;
+       KeySet *ks= ksNew(0);
+       printf ("Test lookup functions with KDB_O_NOALL\n");
+       ksAppendKey(ks, keyNew("user/a", KEY_END));
+       ksAppendKey(ks, keyNew("user/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/d", KEY_END));
+       ksAppendKey(ks, keyNew("user/e", KEY_END));
+       ksAppendKey(ks, keyNew("user/00", KEY_DIR, KEY_END));
+       ksAppendKey(ks, keyNew("user/00/a", KEY_END));
+       ksAppendKey(ks, keyNew("user/00/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/00/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/01", KEY_DIR, KEY_END));
+       ksAppendKey(ks, keyNew("user/01/a", KEY_END));
+       ksAppendKey(ks, keyNew("user/01/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/01/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/01/d", KEY_END));
+       ksAppendKey(ks, keyNew("user/02", KEY_DIR, KEY_END));
+       ksAppendKey(ks, keyNew("user/02/a", KEY_END));
+       ksAppendKey(ks, keyNew("user/02/b", KEY_END));
+       ksAppendKey(ks, keyNew("user/02/c", KEY_END));
+       ksAppendKey(ks, keyNew("user/02/d", KEY_END));
+       ksAppendKey(ks, keyNew("user/02/e", KEY_END));
+       ksAppendKey(ks, keyNew("user/02/f", KEY_END));
+
+       found = ksLookupByName (ks, "user/e", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/e") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/e", KDB_O_NOALL);
+       succeed_if (found == 0, "should not find");
+
+       ksRewind(ks);
+       found = ksLookupByName (ks, "user/a", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/a") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/e", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/e") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/00", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/00") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/01", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/01") == 0, "name not correct in found key");
+
+       found = ksNext (ks);
+       succeed_if (found != 0, "could not get next key");
+       succeed_if (strcmp (keyName(found), "user/01/a") == 0, "name not correct in next key");
+
+       found = ksLookupByName (ks, "user/02", KDB_O_NOALL);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/02") == 0, "name not correct in found key");
+
+       cursor = ksGetCursor (ks);
+       found = ksLookupByName (ks, "user/01", KDB_O_NOALL);
+       succeed_if (ksGetCursor(ks) == cursor, "cursor should not change");
+       succeed_if (found == 0, "should not find");
+
+       ksRewind(ks);
+       found = ksLookupByName (ks, "user/a", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/a") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/E", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/e") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/00", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/00") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/01", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/01") == 0, "name not correct in found key");
+
+       found = ksNext (ks);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not get next key");
+       succeed_if (strcmp (keyName(found), "user/01/a") == 0, "name not correct in next key");
+
+       found = ksLookupByName (ks, "user/02", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/02") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/02/F", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksCurrent(ks) == found, "current not set correctly");
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/02/f") == 0, "name not correct in found key");
+
+       cursor = ksGetCursor (ks);
+       found = ksLookupByName (ks, "user/a", KDB_O_NOALL | KDB_O_NOCASE);
+       succeed_if (ksGetCursor(ks) == cursor, "cursor should stay as is if not found");
+       succeed_if (found == 0, "should not find");
+
+       ksRewind(ks);
+       found = ksLookupByName (ks, "user/a", KDB_O_NOALL | KDB_O_WITHOWNER | KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/a") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/E", KDB_O_NOALL | KDB_O_WITHOWNER | KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (strcmp (keyName(found), "user/e") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/00", KDB_O_NOALL | KDB_O_WITHOWNER | KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/00") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user/01", KDB_O_NOALL | KDB_O_WITHOWNER | KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/01") == 0, "name not correct in found key");
+
+       found = ksNext (ks);
+       succeed_if (found != 0, "could not get next key");
+       succeed_if (strcmp (keyName(found), "user/01/a") == 0, "name not correct in next key");
+
+       found = ksLookupByName (ks, "user/02", KDB_O_NOALL | KDB_O_WITHOWNER | KDB_O_NOCASE);
+       succeed_if (found != 0, "could not find key");
+       succeed_if (keyIsDir (found) == 1, "should be dir");
+       succeed_if (strcmp (keyName(found), "user/02") == 0, "name not correct in found key");
+
+       found = ksLookupByName (ks, "user:notvalid/02/F", KDB_O_NOALL | KDB_O_WITHOWNER | KDB_O_NOCASE);
+       succeed_if (found == 0, "should not find key");
+
+       ksDel (ks);
+}
+
+/*
+void test_ksLookupValue()
+{
+       KeySet *ks = ksNew(0);
+       Key *found;
+       printf ("test lookups for values\n");
+       
+       ksAppendKey(ks, keyNew("user/1",  KEY_VALUE, "singlevalue", KEY_END));
+       ksAppendKey(ks, keyNew("user/2",   KEY_VALUE, "myvalue", KEY_END));
+       ksAppendKey(ks, keyNew("user/3", KEY_VALUE, "syskey",  KEY_END));
+       
+       ksAppendKey(ks, keyNew("user/b1", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "binary\1\2data", KEY_END));
+       ksAppendKey(ks, keyNew("user/b2", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "sys\1bin\2", KEY_END));
+       ksAppendKey(ks, keyNew("user/b3", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "sy\1\33\12\32skey", KEY_END));
+       succeed_if(ksGetSize(ks) == 6, "could not append keys");
+
+       succeed_if (ksLookupByString (ks, "value", 0) == 0, "found part of value");
+       succeed_if (ksLookupByString (ks, " singlevalue", 0) == 0, "prefix of value");
+       succeed_if (ksLookupByString (ks, "/singlevalue", 0) == 0, "prefix of value");
+       succeed_if (ksLookupByString (ks, "singlevalue ", 0) == 0, "postfix of value");
+
+       found = ksLookupByString(ks, "singlevalue", 0);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/1") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "singlevalue", 0);
+       succeed_if (found != 0, "could not find value again");
+       succeed_if (strcmp (keyName(found), "user/1") == 0, "not correct key found");
+       
+       found = ksLookupByString(ks, "myvalue", 0);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/2") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "syskey", 0);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/3") == 0, "not correct key found");
+
+       ksRewind(ks);
+       found = ksLookupByString(ks, "singlevalue", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/1") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "singlevalue", 0);
+       succeed_if (found != 0, "could not find value again");
+       succeed_if (strcmp (keyName(found), "user/1") == 0, "not correct key found");
+
+       ksRewind(ks);
+       found = ksLookupByString(ks, "myvalue", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/2") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "syskey", 0);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/3") == 0, "not correct key found");
+
+       // TODO: BUG found = ksLookupByString(ks, "singlevalue", KDB_O_NOALL);
+       // succeed_if (found == 0, "could find value");
+
+       // found = ksLookupByString(ks, "singlevalue", KDB_O_NOALL);
+       // succeed_if (found == 0, "found value again");
+
+       ksRewind(ks);
+       found = ksLookupByString(ks, "myvalue", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/2") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "syskey", 0);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/3") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "syskey", KDB_O_NOALL);
+       succeed_if (found != 0, "could not find value");
+       succeed_if (strcmp (keyName(found), "user/3") == 0, "not correct key found");
+
+       found = ksLookupByString(ks, "syskey", KDB_O_NOALL);
+       succeed_if (found == 0, "found value");
+
+       ksDel(ks);
+}
+*/
+
+//copied out from example      
+void test_ksExample()
+{
+       KeySet *ks=ksNew(0);
+       Key * key;
+
+       ksAppendKey(ks,keyNew(0));       // an empty key
+               
+       ksAppendKey(ks,keyNew("user/sw",              // the name of the key
+               KEY_END));                      // no more args
+               
+       ksAppendKey(ks,keyNew("user/tmp/ex1",
+               KEY_VALUE,"some data",          // set a string value
+               KEY_END));                      // end of args
+               
+       ksAppendKey(ks,keyNew("user/tmp/ex2",
+               KEY_VALUE,"some data",          // with a simple value
+               KEY_MODE,0777,                  // permissions
+               KEY_END));                      // end of args
+               
+       ksAppendKey(ks,keyNew("user/tmp/ex4",
+               KEY_TYPE,KEY_TYPE_BINARY,       // key type
+               KEY_SIZE,7,                     // assume binary length 7
+               KEY_VALUE,"some data",          // value that will be truncated in 7 bytes
+               KEY_COMMENT,"value is truncated",
+               KEY_OWNER,"root",               // owner (not uid) is root
+               KEY_UID,0,                      // root uid
+               KEY_END));                      // end of args
+
+       ksAppendKey(ks,keyNew("user/tmp/ex5",
+               KEY_TYPE, KEY_TYPE_BINARY,      // binary value
+               KEY_SIZE,7,
+               KEY_VALUE,"some data",          // value that will be truncated in 7 bytes
+               KEY_COMMENT,"value is truncated",
+               KEY_OWNER,"root",              // owner (not uid) is root
+               KEY_UID,0,                      // root uid
+               KEY_END));                      // end of args
+       
+       ksRewind(ks);
+       // end of example
+
+       key=ksNext(ks);
+       succeed_if(key != NULL, "keyNew: Unable to create a new empty key");
+       
+       key=ksNext(ks);
+       succeed_if(key != NULL, "keyNew: Unable to create a key with name");
+       succeed_if(strcmp(keyName(key), "user/sw") == 0, "keyNew: Key's name setted incorrectly");
+
+       ksDel(ks);
+}
+
+#define MAX_SIZE 200
+void test_ksCommonParentName()
+{
+       char ret [MAX_SIZE+1];
+       KeySet *ks = ksNew (10,
+               keyNew("system/sw/xorg/Monitors/Monitor1/vrefresh",0),
+               keyNew("system/sw/xorg/Monitors/Monitor1/hrefresh",0),
+               keyNew("system/sw/xorg/Monitors/Monitor2/vrefresh",0),
+               keyNew("system/sw/xorg/Monitors/Monitor2/hrefresh",0),
+               keyNew("system/sw/xorg/Devices/Device1/driver",0),
+               keyNew("system/sw/xorg/Devices/Device1/mode",0),KS_END);
+
+       printf ("Test common parentname\n");
+       ksSort (ks);
+
+       succeed_if (ksGetCommonParentName(ks, ret, MAX_SIZE) > 0, "could not find correct parentname");
+       succeed_if (strcmp (ret, "system/sw/xorg") == 0, "parentname not correct");
+       ksDel (ks);
+
+       ks = ksNew (10,
+               keyNew("system",0),
+               keyNew("user",0),KS_END);
+       succeed_if (ksGetCommonParentName(ks, ret, MAX_SIZE) == 0, "could find correct parentname");
+       succeed_if (strcmp (ret, "") == 0, "parentname not empty");
+       ksDel (ks);
+
+       ks = ksNew (10,
+               keyNew("system/some/thing",0),
+               keyNew("system/other/thing",0), KS_END);
+       succeed_if (ksGetCommonParentName(ks, ret, MAX_SIZE) == 7, "could find correct parentname");
+       succeed_if (strcmp (ret, "system") == 0, "parentname not empty");
+       ksDel (ks);
+
+       ks = ksNew (10,
+               keyNew("system/here/in/deep/goes/ok/thing",0),
+               keyNew("system/here/in/deep/goes/ok/other/thing",0),
+               KS_END);
+       succeed_if (ksGetCommonParentName(ks, ret, MAX_SIZE) > 0, "could find correct parentname");
+       succeed_if (strcmp (ret, "system/here/in/deep/goes/ok") == 0, "parentname not empty");
+       ksDel (ks);
+
+       ks = ksNew (10,
+               keyNew("system/here/in/deep/goes/ok/thing",0),
+               keyNew("system/here/in/deep/goes/ok/other/thing",0),
+               keyNew("user/unique/thing",0),KS_END);
+       succeed_if (ksGetCommonParentName(ks, ret, MAX_SIZE) == 0, "could find correct parentname");
+       succeed_if (strcmp (ret, "") == 0, "parentname not empty");
+       ksDel (ks);
+
+       ks = ksNew (10,
+               keyNew("user/unique/thing",0),KS_END);
+       succeed_if (ksGetCommonParentName(ks, ret, MAX_SIZE) > 0, "could find correct parentname");
+       succeed_if (strcmp (ret, "user/unique/thing") == 0, "parentname not empty");
+       ksDel (ks);
+}
+
+void test_ksAppend()
+{
+       int i;
+
+       printf ("Test appending keys\n");
+
+       KeySet *returned =
+#include "keyset.c"
+       KeySet *testDirectBelow =
+#include "dbelow.c"
+       KeySet *testReturned =
+#include "others.c"
+       Key *parentKey[2];
+       parentKey[0] = keyNew ("user/test/keyset", KEY_END);
+       parentKey[1] = keyNew ("user/test/keyset/dir1", KEY_END);
+       Key *current;
+
+       /* A real world example out in kdb.c */
+       for (i=0; i<2; i++)
+       {
+               KeySet *tmp = ksNew(ksGetSize(returned), KS_END);
+               KeySet *keys = ksNew (0);
+
+               /* add all keys direct below parentKey */
+               ksRewind (returned);
+               while ((current = ksPop(returned)) != 0)
+               {
+                       if (keyIsDirectBelow(parentKey[i], current))
+                       {
+                               ksAppendKey(keys, current);
+                       } else {
+                               ksAppendKey(tmp, current);
+                       }
+               }
+               ksAppend (returned, tmp);
+
+               /*
+               ksOutput (tmp, stdout, KDB_O_HEADER);
+               ksOutput (returned, stdout, KDB_O_HEADER);
+               printf (" ----- keys -------\n");
+               ksOutput (keys, stdout, KDB_O_HEADER);
+               */
+
+               if (!i)
+               {
+                       compare_keyset (returned, testReturned, 0, 0);
+                       compare_keyset (keys, testDirectBelow, 0, 0);
+
+                       succeed_if (ksGetSize (tmp) == 84, "size not correct");
+                       succeed_if (ksGetSize (returned) == 84, "size not correct");
+                       succeed_if (ksGetSize (keys) == 18, "size not correct");
+
+                       succeed_if (ksGetAlloc (tmp) == 102, "alloc not correct");
+                       succeed_if (ksGetAlloc (returned) == 128, "alloc not correct");
+                       succeed_if (ksGetAlloc (keys) == 32, "alloc not correct");
+               }
+
+               ksAppend (returned, keys); /* add the keys back */
+
+               ksDel (tmp);
+               ksDel (keys);
+
+       }
+
+       keyDel (parentKey[0]);
+       keyDel (parentKey[1]);
+
+       ksDel (testReturned);
+       ksDel (testDirectBelow);
+       ksDel (returned);
+}
+
+
+
+/**A functional mode to keys.
+ *
+ * Instead of writing your own loop you can write
+ * a function working with a key and pass it to
+ * this method.
+ *
+ * The function will be executed for all keys in
+ * the keyset.
+ *
+ * @param ks the keyset to work with
+ * @param func the function to execute on every key of the keyset
+ * @return the sum of all return values
+ * @return -1 if any function returned -1, the execution will stop there, but
+ *     ksCurrent() will tell you where it stopped.
+ * @see ksFilter()
+ */
+int ksForEach (KeySet *ks, int (*func) (Key *k))
+{
+       int rc = 0;
+       int ret = 0;
+       Key *current;
+
+       cursor_t cursor = ksGetCursor (ks);
+       ksRewind (ks);
+       while ((current = ksNext (ks)) != 0)
+       {
+               rc = func (current);
+               if (rc == -1) return -1;
+               ret += rc;
+       }
+       ksSetCursor(ks, cursor);
+       return ret;
+}
+
+
+/**Filter a keyset.
+ *
+ * filter is executed for every key in the keyset result. When it returns 0,
+ * the key will be dropped, when it returns 1 it will be ksAppendKey()ed to result,
+ * when it returns -1 the processing will be stopped. You can use ksCurrent()
+ * on input to see where the problem was. Because of this input is not const,
+ * apart from ksCurrent() the input will not be changed. The keys that have
+ * been in result before will stay untouched.
+ *
+ * @param result is the keyset where keys are added.
+ * @param input is the keyset the filter works on.
+ * @param filter is the function to execute on every key of the keyset to decide if
+ *     it should be ksAppendKey()ed to the result.
+ * @return the number of keys added on success
+ * @return 0 when nothing was done
+ * @return -1 when filter returned an error (-1), ksCurrent() of input will
+ *     be the problematic key.
+ * @see ksForEach()
+ **/
+int ksFilter (KeySet *result, KeySet *input, int (*filter) (Key *k))
+{
+       int rc = 0;
+       int ret = 0;
+       Key *current;
+
+       cursor_t cursor = ksGetCursor (input);
+       ksRewind (input);
+       while ((current = ksNext (input)) != 0)
+       {
+               rc = filter (current);
+               if (rc == -1) return -1;
+               else if (rc != 0)
+               {
+                       ++ ret;
+                       ksAppendKey(result, keyDup (current));
+               }
+       }
+       ksSetCursor(input, cursor);
+       return ret;
+}
+
+
+Key *global_a;
+
+int add_string (Key *check) { return keySetString (check, "string"); }
+int add_comment (Key *check) { return keySetComment (check, "comment"); }
+int has_a (Key *check) { return keyName(check)[5]=='a'; }
+int below_a (Key *check) { return keyIsBelow(global_a, check); }
+int direct_below_a (Key *check) { return keyIsDirectBelow(global_a, check); }
+
+int sum_helper (Key *check) { return atoi(keyValue(check)); }
+int below_30 (Key *check) { return atoi(keyValue(check))<30; }
+int find_80 (Key *check) { int n=atoi(keyValue(check)); return n>70?-1:1; }
+
+void test_ksFunctional()
+{
+       Key *found;
+       Key *current;
+       KeySet *out;
+       KeySet *ks = ksNew (64,
+               keyNew ("user/a/1", KEY_END),
+               keyNew ("user/a/2", KEY_END),
+               keyNew ("user/a/b/1", KEY_END),
+               keyNew ("user/a/b/2", KEY_END),
+               keyNew ("user/ab/2", KEY_END),
+               keyNew ("user/b/1", KEY_END),
+               keyNew ("user/b/2", KEY_END),
+               KS_END);
+       global_a = keyNew ("user/a", KEY_END);
+
+       printf ("Test functional style\n");
+
+       ksForEach (ks, add_string);
+       ksForEach (ks, add_comment);
+
+       ksRewind (ks);
+       while ((current = ksNext(ks)) != 0)
+       {
+               succeed_if (strcmp (keyValue (current), "string") == 0, "for each did not add string");
+               succeed_if (strcmp (keyComment (current), "comment") == 0, "for each did not add comment");
+       }
+
+       out = ksNew (0);
+       succeed_if (ksGetSize(ks) == 7, "initial size wrong");
+       succeed_if (ksGetSize(out) == 0, "initial size wrong");
+       ksFilter (out, ks, has_a);
+       succeed_if (ksGetSize(out) == 5, "has_a cutted more then the user/b");
+       ksDel (out);
+
+       out = ksNew (0);
+       ksFilter (out, ks, below_a);
+       succeed_if (ksGetSize(out) == 4, "below_a cutted more then the user/ab/2");
+       ksDel (out);
+
+       out = ksNew (0);
+       ksFilter (out, ks, direct_below_a);
+       succeed_if (ksGetSize(out) == 2, "direct_below_a cutted more then the user/a/b/*");
+       ksDel (out);
+
+       ksDel (ks);
+       keyDel (global_a); global_a = 0;
+
+       KeySet *values = ksNew (64,
+               keyNew ("user/a", KEY_VALUE, "40", KEY_END),
+               keyNew ("user/b", KEY_VALUE, "20", KEY_END),
+               keyNew ("user/c", KEY_VALUE, "80", KEY_END),
+               keyNew ("user/d", KEY_VALUE, "24", KEY_END),
+               keyNew ("user/e", KEY_VALUE, "32", KEY_END),
+               keyNew ("user/f", KEY_VALUE, "12", KEY_END),
+               keyNew ("user/g", KEY_VALUE, "43", KEY_END),
+               KS_END);
+
+       succeed_if (ksForEach (values, sum_helper) == 251, "could not sum up");
+
+       KeySet *values_below_30 = ksNew (0);
+       ksFilter (values_below_30, values, below_30);
+       succeed_if (ksGetSize (values_below_30) == 3, "could not filter out everything above 30");
+       succeed_if (ksForEach (values_below_30, sum_helper) == 56, "could not sum up");
+
+       succeed_if (ksForEach (values, find_80) == -1, "did not find 80");
+       found = ksCurrent (values);
+       succeed_if (ksLookupByName (values, "user/c", 0) == found, "did not find 80");
+       /*succeed_if (ksLookupByString (values, "80", 0) == found, "lookup by value did not find 80");*/
+       ksDel (values);
+       ksDel (values_below_30);
+}
+
+void test_ksLookupPop()
+{
+       printf ("Test ksLookup with KDB_O_POP\n");
+
+       Key * found;
+       Key *a, *b, *c;
+       KeySet *small =ksNew (5,
+                       a=keyNew ("user/a", KEY_END),
+                       b=keyNew ("user/b", KEY_END),
+                       c=keyNew ("user/c", KEY_END), KS_END);
+
+       ksRewind (small);
+       ksNext(small);
+       succeed_if (ksCurrent(small) == a, "current not set correctly");
+
+       succeed_if (ksGetSize(small) == 3, "could not append all keys");
+       found = ksLookupByName (small, "user/a", KDB_O_POP);
+       succeed_if (found == a, "not correct key");
+       succeed_if (strcmp (keyName(found), "user/a") == 0, "name not correct in found key");
+       succeed_if (ksCurrent(small) == 0, "current not set correctly");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       ksNext(small);
+       ksNext(small);
+       succeed_if (ksCurrent(small) == c, "current not set correctly");
+
+       succeed_if (ksGetSize(small) == 2, "could not append all keys");
+       found = ksLookupByName (small, "user/b", KDB_O_POP);
+       succeed_if (found == b, "not correct key");
+       succeed_if (strcmp (keyName(found), "user/b") == 0, "name not correct in found key");
+       succeed_if (ksCurrent(small) == c, "current not set correctly");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       succeed_if (ksGetSize(small) == 1, "could not append all keys");
+       found = ksLookupByName (small, "user/b", KDB_O_POP);
+       succeed_if (found == 0, "found something, but shouldnt");
+       succeed_if (ksCurrent(small) == c, "current not set correctly");
+
+       succeed_if (ksGetSize(small) == 1, "could not append all keys");
+       found = ksLookupByName (small, "user/c", KDB_O_POP);
+       succeed_if (found == c, "not correct key");
+       succeed_if (strcmp (keyName(found), "user/c") == 0, "name not correct in found key");
+       succeed_if (ksCurrent(small) == 0, "current not set correctly");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       succeed_if (ksGetSize(small) == 0, "could not append all keys");
+       found = ksLookupByName (small, "user/d", KDB_O_POP);
+       succeed_if (found == 0, "found something, but shouldnt");
+       succeed_if (ksCurrent(small) == 0, "current not set correctly");
+
+       ksDel (small);
+
+       KeySet *ks= ksNew(0);
+
+       succeed_if (ksNeedSort (ks) == 1, "sort state not correct");
+
+       ksAppendKey(ks, keyNew("user/domain/key",  KEY_VALUE, "domainvalue",
+               KEY_OWNER, "markus", KEY_END));
+       ksAppendKey(ks, keyNew("user/single/key",  KEY_VALUE, "singlevalue", KEY_END));
+       ksAppendKey(ks, keyNew("user/named/key",   KEY_VALUE, "myvalue", KEY_END));
+       ksAppendKey(ks, keyNew("system/named/key", KEY_VALUE, "syskey",  KEY_END));
+       ksAppendKey(ks, keyNew("system/sysonly/key", KEY_VALUE, "sysonlykey",  KEY_END));
+       ksAppendKey(ks, keyNew("user/named/bin", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "binary\1\2data", KEY_END));
+       ksAppendKey(ks, keyNew("system/named/bin", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "sys\1bin\2", KEY_END));
+       ksAppendKey(ks, keyNew("system/named/key", KEY_TYPE, KEY_TYPE_BINARY, KEY_SIZE, 10,
+               KEY_VALUE, "syskey", KEY_END));
+       succeed_if (ksGetSize(ks) == 8, "could not append all keys");
+
+       succeed_if (ksNeedSort (ks) == 1, "sort state not correct");
+
+       // a positive testcase
+       found = ksLookupByName (ks, "user/named/key", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 7, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (ksNeedSort (ks) == 0, "sort state not correct");
+       ksAppendKey(ks, keyNew("user/single/key",  KEY_VALUE, "singlevalue", KEY_END));
+       succeed_if (ksGetSize(ks) == 8, "did not pop key");
+       succeed_if (ksNeedSort (ks) == 1, "sort state not correct");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       // here you can't find the keys
+       succeed_if (ksLookupByName (ks, "named/key", KDB_O_POP) == 0, "not valid keyname");
+       succeed_if (ksNeedSort (ks) == 0, "sort state not correct");
+       succeed_if (ksLookupByName (ks, "u/named/key", KDB_O_POP) == 0, "not valid keyname");
+       succeed_if (ksLookupByName (ks, "usea/named/key", KDB_O_POP) == 0, "not valid keyname");
+       succeed_if (ksLookupByName (ks, " user/named/key", KDB_O_POP) == 0, "found key with bad prefix");
+
+       succeed_if (ksLookupByName (ks, "user/named/Key", KDB_O_POP) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "User/Named/key", KDB_O_POP) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "User/named/key", KDB_O_POP) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "user/NAMED/key", KDB_O_POP) == 0, "found wrong case key");
+       succeed_if (ksLookupByName (ks, "USER/NAMED/KEY", KDB_O_POP) == 0, "found wrong case key");
+       
+       succeed_if (ksLookupByName (ks, "user/named/keys", KDB_O_POP) == 0, "wrong postfix");
+       succeed_if (ksLookupByName (ks, "user/named/key_", KDB_O_POP) == 0, "wrong postfix");
+
+       succeed_if (ksLookupByName (ks, "user/named/k/ey", KDB_O_POP) == 0, "seperation that should be");
+       succeed_if (ksLookupByName (ks, "user/na/med/key", KDB_O_POP) == 0, "seperation that should be");
+
+       succeed_if (ksLookupByName (ks, "system/domain/key", KDB_O_POP) == 0, "found key in wrong domain");
+       
+       //now try to find them, and compare value
+       found = ksLookupByName (ks, "user/domain/key", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 7, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "user/domain/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "domainvalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       found = ksLookupByName (ks, "user/single/key", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 6, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "user/single/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "singlevalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       found = ksLookupByName (ks, "system/named/key", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 5, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "system/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "syskey") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       found = ksLookupByName (ks, "user/named/bin", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 4, "pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found != 0, "did not found correct name");
+       succeed_if (strcmp (keyName(found), "user/named/bin") == 0, "name not correct in found key");
+       succeed_if (strncmp (keyValue(found), "binary\1\2data",10) == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       found = ksLookupByName (ks, "user/named/key", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 4, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found == 0, "could find same key again");
+
+       printf ("Test nocase lookup functions\n");
+       found = ksLookupByName (ks, "user/named/key", KDB_O_NOCASE | KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 4, "key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found == 0, "could find same key again, nocase");
+
+       ksAppendKey(ks, keyNew("user/named/key",   KEY_VALUE, "myvalue", KEY_END));
+       found = ksLookupByName (ks, "user/NameD/KeY", KDB_O_NOCASE | KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 4, "did not pop key");
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       ksAppendKey(ks, keyNew("user/named/key",   KEY_VALUE, "myvalue", KEY_END));
+       found = ksLookupByName (ks, "user/NameD/KEY", KDB_O_NOCASE | KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 4, "did not pop key");
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       ksAppendKey(ks, keyNew("user/named/key",   KEY_VALUE, "myvalue", KEY_END));
+       printf ("Test cascading lookup functions\n");
+       found = ksLookupByName (ks, "/named/key", KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 4, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found != 0, "cascading search failed");
+       succeed_if (strcmp (keyName(found), "user/named/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "myvalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       found = ksLookupByName (ks, "/single/Key", KDB_O_NOCASE | KDB_O_POP);
+       succeed_if (ksGetSize(ks) == 3, "did not pop key");
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (strcmp (keyName(found), "user/single/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "singlevalue") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       found = ksLookupByName (ks, "/sysonly/key", KDB_O_POP);
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (ksGetSize(ks) == 2, "did not pop key");
+       succeed_if (found != 0, "could not find same key again, nocase used");
+       succeed_if (strcmp (keyName(found), "system/sysonly/key") == 0, "name not correct in found key");
+       succeed_if (strcmp (keyValue(found), "sysonlykey") == 0, "not correct value in found key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       succeed_if (ksLookupByName (ks, "/named/", KDB_O_POP) == 0, "found part of key with cascading");
+       succeed_if (ksLookupByName (ks, "/named/keyd", KDB_O_POP) == 0, "found part of key with cascading, bad postfix");
+
+       ksAppendKey(ks, keyNew("user:markus/domain/key",   KEY_VALUE, "domainvalue", KEY_END));
+       printf ("Test domain lookup functions\n");
+       found = ksLookupByName (ks, "user:markus/domain/key", KDB_O_WITHOWNER | KDB_O_POP);
+       succeed_if (ksCurrent(ks) == 0, "current not set correctly");
+       succeed_if (ksGetSize(ks) == 2, "did not pop key");
+       succeed_if (found != 0, "could not find domain key");
+       succeed_if (strcmp (keyValue(found), "domainvalue") == 0, "not correct value in domain key");
+       succeed_if (ksLookupByName (ks, "user:hugo/domain/key", KDB_O_WITHOWNER | KDB_O_POP) == 0, "found key in wrong domain");
+       succeed_if (ksLookupByName (ks, "user:y/domain/key", KDB_O_WITHOWNER | KDB_O_POP) == 0, "found key in wrong domain");
+       succeed_if (ksLookupByName (ks, "user:markuss/domain/key", KDB_O_WITHOWNER | KDB_O_POP) == 0, "found key in wrong domain");
+       succeed_if (ksLookupByName (ks, "user:marku/domain/key", KDB_O_WITHOWNER | KDB_O_POP) == 0, "found key in wrong domain");
+       succeed_if (ksGetSize(ks) == 2, "did not pop key");
+       succeed_if (keyDel (found) == 0, "could not del popped key");
+
+       ksDel(ks);
+       
+}
+
+int main()
+{
+       printf("KEYSET       TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_ksNew();
+       test_ksEmpty();
+       test_ksReference();
+       test_ksDup();
+       test_ksCopy();
+       test_ksResize();
+       test_ksIterate();
+       test_ksCursor();
+       test_ksSort();
+       test_ksLookup();
+       test_ksLookupByName();
+       test_ksLookupName();
+       test_ksLookupNameRemove();
+       test_ksLookupNameAll();
+       /*test_ksLookupValue();*/
+       test_ksExample();
+       test_ksCommonParentName();
+       test_ksAppend();
+       test_ksFunctional();
+       test_ksLookupPop();
+
+       printf("\ntest_ks RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
+
diff --git a/tests/test_mount.c b/tests/test_mount.c
new file mode 100644 (file)
index 0000000..07023e8
--- /dev/null
@@ -0,0 +1,437 @@
+/*************************************************************************** 
+ *           test_mount.c  - Test suite for testing backend mounting
+ *                  -------------------
+ *  begin                : Thu Nov 6 2007
+ *  copyright            : (C) 2007 by Patrick Sabin
+ *  email                : patricksabin@gmx.at
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+KeySet *get_hosts ()
+{
+       return
+       ksNew( 25 ,
+       keyNew ("user/tests/hosts"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/tests/hosts/gateway.markus-raab.org"
+               , KEY_DIR
+               , KEY_VALUE, "192.168.0.1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/gateway.markus-raab.org/alias00"
+               , KEY_VALUE, "gateway"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/gateway.markus-raab.org/alias01"
+               , KEY_VALUE, "gw"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/home.markus-raab.org"
+               , KEY_DIR
+               , KEY_VALUE, "81.52.51.112"
+               , KEY_COMMENT, "comment"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/home.markus-raab.org/alias00"
+               , KEY_VALUE, "home"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/kirabyte.markus-raab.org"
+               , KEY_DIR
+               , KEY_VALUE, "192.168.0.5"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/kirabyte.markus-raab.org/alias00"
+               , KEY_VALUE, "kirabyte"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/kirabyte.markus-raab.org/alias01"
+               , KEY_VALUE, "kira"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/kirabyte.markus-raab.org/alias02"
+               , KEY_VALUE, "k"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/localhost"
+               , KEY_VALUE, "127.0.0.1"
+               , KEY_COMMENT, "will lose that comment"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/markusbyte"
+               , KEY_DIR
+               , KEY_VALUE, "192.168.0.3"
+               , KEY_COMMENT, "this is my home"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/markusbyte/alias00"
+               , KEY_VALUE, "markus"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/printer"
+               , KEY_VALUE, "192.168.1.5"
+               , KEY_COMMENT, " thats for dynamic cups printing"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/superbyte"
+               , KEY_VALUE, "192.168.0.2"
+               , KEY_COMMENT, " my server at home!"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),KS_END);
+}
+
+#define BUFFER_SIZE 256
+
+void test_failhosts(const char * file)
+{
+       KeySet *ks = ksNew( 25 ,
+       keyNew ("user/tests/hosts"
+               , KEY_DIR
+               , KEY_TYPE, 0
+       , KEY_END),
+       keyNew ("user/tests/hosts/gateway.markus-raab.org"
+               , KEY_DIR
+               , KEY_VALUE, "192.168.0.1"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/gateway.markus-raab.org/ERROR/DEEP"
+               , KEY_VALUE, "errorvalue"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),
+       keyNew ("user/tests/hosts/superbyte"
+               , KEY_VALUE, "192.168.0.2"
+               , KEY_COMMENT, " my server at home!"
+               , KEY_TYPE, KEY_TYPE_STRING
+       , KEY_END),KS_END);
+       KDB *kdb = kdbOpen();
+       Key *mnt;
+       KeySet *conf;
+
+
+       printf("Test fail writehosts\n");
+
+       succeed_if (kdbMount(kdb,mnt=keyNew("user/tests/hosts",KEY_VALUE,"hosts", KEY_END),
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, file, KEY_END), KS_END)) == 0,
+               "could not mount hosts");
+       succeed_if (kdbSet(kdb,ks,keyNew("user/tests/hosts",KEY_END),KDB_O_DEL) == -1, "setting keys did not fail");
+
+       /*keyOutput (ksCurrent(ks), stdout, KEY_VALUE|KEY_COMMENT);*/
+       succeed_if (strcmp(keyName(ksCurrent(ks)), "user/tests/hosts/gateway.markus-raab.org/ERROR/DEEP") == 0, "name not equal");
+       succeed_if (strcmp(keyValue(ksCurrent(ks)), "errorvalue") == 0, "value not equal");
+       succeed_if (!keyIsDir (ksCurrent(ks)), "key is a directory");
+       succeed_if (keyIsString (ksCurrent(ks)), "key is not a string");
+       succeed_if (keyGetType(ksCurrent(ks)) == KEY_TYPE_STRING, "key is not exactly a string");
+
+       ksDel (conf);
+       keyDel(mnt);
+
+       /*
+       ksOutput (ks, stdout, 0);
+       ksGenerate (ks, stdout, 0);
+       */
+
+       ksDel (ks);
+       kdbClose (kdb);
+}
+
+void test_readhosts(const char * file)
+{
+       char *keys[]={  "user/tests/hosts/markusbyte",
+                       "user/tests/hosts/printer",
+                       "user/tests/hosts/superbyte"};
+       KDB *kdb = kdbOpen();
+       Key *mnt;
+       Key *key;
+       KeySet *ks=ksNew(0);
+       KeySet *conf;
+       KeySet *hosts_ks = get_hosts();
+       char buffer[BUFFER_SIZE+1];
+       int i, c;
+
+       printf("Test mount readhosts\n");
+
+       succeed_if (kdbMount(kdb,mnt=keyNew("user/tests/hosts",KEY_VALUE,"hosts", KEY_END),
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, file, KEY_END), KS_END)) == 0,
+               "could not mount hosts");
+       succeed_if (kdbGet(kdb,ks,keyNew("user/tests/hosts",KEY_END),KDB_O_DEL) == 15, "could not get keys");
+       /*
+       ksGenerate (hosts_ks, stdout, KDB_O_HEADER);
+       ksGenerate (ks, stdout, KDB_O_HEADER);
+       */
+       compare_keyset (hosts_ks, ks, 0, 0);
+
+       key = keyNew ("user/tests/hosts/gateway.markus-raab.org", KEY_END);
+       succeed_if (kdbGetKey (kdb, key) == 0, "could not get single key");
+       succeed_if (strcmp(keyName(key), "user/tests/hosts/gateway.markus-raab.org") == 0, "name not equal");
+       succeed_if (strcmp(keyValue(key), "192.168.0.1") == 0, "value not equal");
+       succeed_if (keyIsDir (key), "key is not a directory");
+       succeed_if (keyIsString (key), "key is not a string");
+       succeed_if (keyGetType(key) == KEY_TYPE_STRING, "key is not exactly a string");
+       keyDel (key);
+
+       key = keyNew ("user/tests/hosts/kirabyte.markus-raab.org/alias01", KEY_END);
+       succeed_if (kdbGetKey (kdb, key) == 0, "could not get single key");
+       succeed_if (strcmp(keyName(key), "user/tests/hosts/kirabyte.markus-raab.org/alias01") == 0, "name not equal");
+       succeed_if (strcmp(keyValue(key), "kira") == 0, "value not equal");
+       succeed_if (!keyIsDir (key), "key is a directory");
+       succeed_if (keyIsString (key), "key is not a string");
+       succeed_if (keyGetType(key) == KEY_TYPE_STRING, "key is not exactly a string");
+       keyDel (key);
+
+       key = keyNew ("user/tests/hosts/localhost", KEY_END);
+       succeed_if (kdbGetKey (kdb, key) == 0, "could not get single key");
+       succeed_if (strcmp(keyName(key), "user/tests/hosts/localhost") == 0, "name not equal");
+       succeed_if (strcmp(keyValue(key), "127.0.0.1") == 0, "value not equal");
+       succeed_if (strcmp(keyComment(key), "will lose that comment") == 0, "comment not equal");
+       succeed_if (!keyIsDir (key), "key is a directory");
+       succeed_if (keyIsString (key), "key is not a string");
+       succeed_if (keyGetType(key) == KEY_TYPE_STRING, "key is not exactly a string");
+       keyDel (key);
+
+       succeed_if (kdbGetString (kdb, "user/tests/hosts/localhost", buffer, BUFFER_SIZE) != -1, "could not get value");
+       succeed_if (strcmp (buffer, "127.0.0.1") == 0, "value not correct");
+
+
+       for (c=0; c<3; c++) {
+               succeed_if (kdbGetString(kdb,keys[c],buffer,sizeof(buffer)) != -1, "could not GetString");
+               switch (c)
+               {
+                       case 0: succeed_if (strcmp(buffer, "192.168.0.3") == 0, "value not correct");
+                               break;
+                       case 1: succeed_if (strcmp(buffer, "192.168.1.5") == 0, "value not correct");
+                               break;
+                       case 2: succeed_if (strcmp(buffer, "192.168.0.2") == 0, "value not correct");
+                               break;
+                       default:succeed_if (0, "default branch");
+               }
+       }
+
+       for (i=0; i< BUFFER_SIZE; i++) buffer[i] = 0;
+       succeed_if (kdbGetString (kdb, "user/tests/hosts/localhost", buffer, 3) == -1, "could get value");
+       /*succeed_if (errno == KDB_ERR_TRUNC, "error not correct");*/
+       /*printf ("%s\n", buffer);*/
+       succeed_if (strcmp (buffer, "") == 0, "value not correct");
+
+       ksDel (conf);
+       keyDel(mnt);
+
+       /*ksOutput (ks, stdout, 0);
+       ksGenerate (ks, stdout, 0);*/
+
+       ksDel (hosts_ks);
+       ksDel (ks);
+       kdbClose (kdb);
+}
+
+void test_writehosts(const char *file)
+{
+       KDB *kdb = kdbOpen();
+       Key *mnt;
+       KeySet *conf;
+       KeySet *ks = get_hosts();
+
+
+       printf("Test mount writehosts\n");
+
+       succeed_if (kdbMount(kdb,mnt=keyNew("user/tests/hosts",KEY_VALUE,"hosts", KEY_END),
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, file, KEY_END), KS_END)) == 0,
+               "could not mount hosts");
+       succeed_if (kdbSet(kdb,ks,keyNew("user/tests/hosts",KEY_END),KDB_O_DEL) == 15, "could not set keys");
+       ksDel (conf);
+       keyDel(mnt);
+
+       /*ksOutput (ks, stdout, 0);
+       ksGenerate (ks, stdout, 0);*/
+
+       ksDel (ks);
+       kdbClose (kdb);
+}
+
+void test_passwd(const char *file)
+{
+       KDB *kdb = kdbOpen();
+       KeySet *ks;
+
+       printf("Test mount passwd\n");
+
+       /* get passwd*/
+       printf("=====TEST passwd backend ====\n");
+       ks=ksNew(0);
+       kdbGet(kdb,ks,keyNew("system/users",KEY_END),KDB_O_DEL);
+       /* ksOutput(ks, stdout, 0); */
+       ksDel(ks);
+
+       ks=ksNew(0);
+       kdbGet(kdb,ks,keyNew("system/users/patrick",KEY_END),KDB_O_DEL);
+       /* ksOutput(ks,stdout, 0); */
+       ksDel(ks);
+
+       kdbClose (kdb);
+
+}
+
+void test_readfstab(const char *file)
+{
+       KDB *kdb = kdbOpen();
+       KeySet *ks;
+       KeySet *conf;
+       Key* mnt;
+
+       printf("Test mount readfstab\n");
+
+       succeed_if (kdbMount (kdb, mnt=keyNew("user/tests/filesystems",KEY_VALUE, "fstab", KEY_END),
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, file, KEY_END), KS_END)) == 0,
+               "could not mount fstab");
+       keyDel(mnt);
+       ksDel (conf);
+
+       ks=ksNew(0);
+       int i;
+       succeed_if ((i=kdbGet(kdb,ks,keyNew("user/tests/filesystems",KEY_END),KDB_O_DEL)) == 15, "could not get keys");
+
+       /*
+       printf ("%d", i);
+       ksOutput (ks, stdout, 0);
+       ksGenerate (ks,stdout, 0);
+       */
+       Key *key = ksLookupByName(ks, "user/tests/filesystems/rootfs/device",0);
+       succeed_if (key, "rootfs device not found");
+       succeed_if (strcmp( "/dev/sda6", keyValue(key)) == 0, "rootfs device not correct");
+
+       ksDel (ks);
+       kdbClose (kdb);
+}
+
+
+void test_writefstab(const char * file)
+{
+       KDB *kdb = kdbOpen();
+       KeySet *ks;
+       KeySet *conf;
+       Key* mnt;
+
+       FILE *fstab_writer = fopen (file, "w");
+       fprintf (fstab_writer, "\n"); /* make empty */
+       fclose (fstab_writer);
+
+       printf("Test mount writefstab\n");
+
+       succeed_if (kdbMount (kdb, mnt=keyNew("user/tests/filesystems",KEY_VALUE, "fstab", KEY_END),
+               conf=ksNew (2,keyNew("system/path", KEY_VALUE, file, KEY_END), KS_END)) == 0,
+               "could not mount fstab");
+       keyDel(mnt);
+       ksDel (conf);
+
+       ks = ksNew( 22 ,
+                       keyNew ("user/tests/filesystems"
+                               , KEY_VALUE, "filesystems"
+                               , KEY_COMMENT, ""
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs"
+                               , KEY_VALUE, "non-rootfs"
+                               , KEY_COMMENT, "pseudo name"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs/device"
+                               , KEY_VALUE, "/dev/sda6"
+                               , KEY_COMMENT, "Device or Label"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs/dumpfreq"
+                               , KEY_VALUE, "0"
+                               , KEY_COMMENT, "Dump frequency in days"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs/mpoint"
+                               , KEY_VALUE, "/"
+                               , KEY_COMMENT, "Moint point"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs/options"
+                               , KEY_VALUE, "defaults,errors=remount-ro"
+                               , KEY_COMMENT, "Fileuser/tests specific options. See mount(8)"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs/passno"
+                               , KEY_VALUE, "1"
+                               , KEY_COMMENT, "Pass number on parallel fsck"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/rootfs/type"
+                               , KEY_VALUE, "jfs"
+                               , KEY_COMMENT, "Fileuser/tests type. See fs(5)"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00"
+                               , KEY_VALUE, "non-swapfs"
+                               , KEY_COMMENT, "pseudo name"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00/device"
+                               , KEY_VALUE, "/dev/sda10"
+                               , KEY_COMMENT, "Device or Label"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00/dumpfreq"
+                               , KEY_VALUE, "0"
+                               , KEY_COMMENT, "Dump frequency in days"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00/mpoint"
+                               , KEY_VALUE, "none"
+                               , KEY_COMMENT, "Moint point"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00/options"
+                               , KEY_VALUE, "sw"
+                               , KEY_COMMENT, "Fileuser/tests specific options. See mount(8)"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00/passno"
+                               , KEY_VALUE, "0"
+                               , KEY_COMMENT, "Pass number on parallel fsck"
+                       , KEY_END),
+                       keyNew ("user/tests/filesystems/swap00/type"
+                               , KEY_VALUE, "swap"
+                               , KEY_COMMENT, "Fileuser/tests type. See fs(5)"
+                       , KEY_END),
+                       KS_END);
+       succeed_if (kdbSet (kdb, ks, keyNew ("user/tests/filesystems",0) ,KDB_O_DEL) == 15, "could not set keys");
+       /*printf ("%d\n", kdbSet (kdb, ks, keyNew ("user/tests/filesystems",0) ,KDB_O_DEL));*/
+       ksDel (ks);
+       kdbClose (kdb);
+}
+
+
+
+int main()
+{
+       printf("MOUNT       TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_writehosts(".kdb/hosts_mount");
+       test_readhosts(".kdb/hosts_mount");
+       // test_failhosts(".kdb/hosts_mount");
+
+       // test_writefstab(".kdb/fstab_mount");
+       // test_readfstab(".kdb/fstab_mount");
+
+       // test_passwd();
+
+       printf("\ntest_mount RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
+
diff --git a/tests/test_script.sh b/tests/test_script.sh
new file mode 100755 (executable)
index 0000000..803e42c
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+#test_script.sh
+#shell test suite for kdb command
+
+#variables
+nbError=0
+nbTest=0
+
+if [ "z$srcdir" = 'z' ]; then
+  HOME=.
+else
+  HOME=$srcdir
+fi
+export HOME
+
+VALUE="value"
+ROOT="user/tests/script"
+FILE="$HOME/.kdb/$ROOT/value"
+DIR="$HOME/.kdb/$ROOT"
+USER="`id -un`"
+GROUP="`id -gn`"
+DATE="`date \"+%b %d %H:%M\"`"
+
+export KDB_DIR=".kdb"
+
+#define succeed_if(x,y) nbTest++; if (!(x)) { nbError++; printf("%s:%d: error in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y);}
+#succeed if the previous command was successful
+succeed_if ()
+{
+       if [ $? != "0" ]
+       then
+               nbError=$(( $nbError + 1 ))
+               echo error: $*
+       fi
+       nbTest=$(( $nbTest + 1 ))
+}
+
+#define exit_if_fail(x,y) nbTest++; if (!(x)) { printf("%s:%d: fatal in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y); exit(1); }
+#fails and exits the program if the previous command failed
+exit_if_fail ()
+{
+       if [ $? != "0" ]
+       then
+               echo fatal: $*
+               exit 1
+       fi
+       nbTest=$(( $nbTest + 1 ))
+}
+
+echo
+echo ELEKTRA SCRIPTS TESTS
+echo
+
+echo "testing set and get keys"
+
+kdb -m "775" set $ROOT
+exit_if_fail "could not set root"
+
+[ -d "$DIR" ]
+succeed_if "Directory not existing"
+
+kdb set "$ROOT/value" "$VALUE"
+exit_if_fail "could not set value"
+
+[ -f "$FILE" ]
+succeed_if "File not existing"
+
+#echo "testing ls subcommand"
+
+#[ "x`kdb ls $ROOT 2> /dev/null`" = "x$ROOT/value" ]
+#succeed_if "cant ls $ROOT (may mean that $ROOT folder is not clean)"
+
+#[ "x`kdb get $ROOT/value 2> /dev/null`" = "x$VALUE" ]
+#exit_if_fail "cant get $ROOT/value"
+
+#[ "x`kdb get -f $ROOT/value 2> /dev/null`" = "x$VALUE" ]
+#succeed_if "-f option"
+
+#[ "x`kdb get -d $ROOT/value 2> /dev/null`" = "x$ROOT/value=$VALUE" ]
+#succeed_if "-d option"
+
+#[ "x`kdb get -l $ROOT/value 2> /dev/null`" = "x$ROOT/value=$VALUE" ]
+#succeed_if "-l option"
+
+#[ "x`kdb get -s $ROOT/value 2> /dev/null`" = "xvalue=\"$VALUE\"" ]
+#succeed_if "-s option"
+
+kdb rm $ROOT/value
+succeed_if "can remove user/test/value"
+
+[ ! -f "$FILE" ]
+succeed_if "File yet existing after delete"
+
+kdb rm $ROOT
+
+[ ! -d "$DIR" ]
+succeed_if "Directory yet existing after delete"
+
+
+#############
+# xml tests
+#############
+
+echo "testing xml importing and exporting"
+
+kdb import fstab.xml
+succeed_if "importing fstab.xml failed"
+
+kdb export user/tests/fstab > fstab-gen.xml
+succeed_if "exporting user/tests/fstab failed"
+
+diff fstab-gen.xml fstab-cmp.xml
+succeed_if "xml files are not the same"
+
+rm fstab-gen.xml
+succeed_if "could not rm key-gen.xml"
+
+kdb rm -r user/tests/fstab
+
+echo test_script.sh RESULTS: $nbTest "test(s)" done $nbError "error(s)".
+#exit $nbError
+
diff --git a/tests/test_serialize.c b/tests/test_serialize.c
new file mode 100644 (file)
index 0000000..82747d7
--- /dev/null
@@ -0,0 +1,159 @@
+/***************************************************************************
+ *          test_serialize.c  - serializing test suite
+ *                  -------------------
+ *  begin                : Mon 26 Nov 2007
+ *  copyright            : (C) 2007 by Markus Raab
+ *  email                : elektra@markus-raab.org
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+#define MAX_SIZE 1000
+
+void output_serialize (void* data, size_t length)
+{
+       size_t i;
+       for (i=0; i<length; i++)
+       {
+               printf ("%o %c ", ((char*)data)[i], ((char*)data)[i]);
+       }
+       printf ("\n");
+}
+
+void test_emptykey ()
+{
+       char buffer[MAX_SIZE+1];
+       Key *key;
+       Key *get;
+
+       printf("Test empty key\n");
+
+       key = keyNew (0);
+       // printf ("%d\n", keyGetSerializedSize(key));
+       // succeed_if( keyGetSerializedSize(key) == 68, "empty key size wrong (32bit)");
+       keySerialize (key, buffer, MAX_SIZE);
+       // output_serialize (buffer, keyGetSerializedSize (key));
+       get = keyUnserialize (buffer);
+       // keyOutput (get);
+       // keyGenerate (get);
+       succeed_if (!compare_key (key,get,0), "did not get the same key after serializing");
+       keyDel (key);
+       keyDel (get);
+
+       key = keyNew (0);
+       // succeed_if( keyGetSerializedSize(key) == 68, "empty key size wrong (32bit)");
+       keySerialize (key, buffer, MAX_SIZE);
+       get = keyCompose (buffer);
+       // keyOutput (get);
+       // keyGenerate (get);
+       succeed_if (!compare_key (key,get,0), "did not get the same key after serializing");
+       keyDel (key);
+}
+
+void test_allkey ()
+{
+       char buffer[MAX_SIZE+1];
+       Key *key;
+       Key *get;
+
+       printf("Test key with value/comment\n");
+
+       key = keyNew ("user",
+               KEY_VALUE, "value",
+               KEY_COMMENT, "comment",
+               KEY_END);
+       // printf ("%d\n", keyGetSerializedSize(key));
+       // succeed_if( keyGetSerializedSize(key) == 94, "empty key size wrong (32bit)");
+       keySerialize (key, buffer, MAX_SIZE);
+       // output_serialize (buffer, keyGetSerializedSize (key));
+       get = keyUnserialize (buffer);
+       // keyOutput (get);
+       // keyGenerate (get);
+       succeed_if (!compare_key (key,get,0), "did not get the same key after serializing");
+       keyDel (key);
+       keyDel (get);
+
+       key = keyNew ("user",
+               KEY_VALUE, "value",
+               KEY_COMMENT, "comment",
+               KEY_END);
+       // succeed_if( keyGetSerializedSize(key) == 94, "empty key size wrong (32bit)");
+       keySerialize (key, buffer, MAX_SIZE);
+       get = keyCompose (buffer);
+       // keyOutput (get);
+       // keyGenerate (get);
+       succeed_if (!compare_key (key,get,0), "did not get the same key after serializing");
+       keyDel (key);
+}
+
+void test_xml(char *file)
+{
+       char buffer[MAX_SIZE+1];
+       Key *cur;
+       Key *key;
+       Key *get;
+       KeySet *ks = ksNew (0);
+
+       exit_if_fail( ksFromXMLfile(ks, file) == 0, "ksFromXMLfile failed.");
+
+       printf("Test key from xml file %s\n", file);
+
+       ksRewind(ks);
+       ksSort(ks);
+       while ( (cur = ksNext(ks)) )
+       {
+               key = keyDup (cur);
+               keySerialize (key, buffer, MAX_SIZE);
+               // output_serialize (buffer, keyGetSerializedSize (key));
+               get = keyUnserialize (buffer);
+               // keyOutput (get);
+               // keyGenerate (get);
+               succeed_if (!compare_key (key,get,0), "did not get the same key after serializing");
+               keyDel (get);
+               keyDel (key);
+
+               key = keyDup (cur);
+               keySerialize (key, buffer, MAX_SIZE);
+               get = keyCompose (buffer);
+               // keyOutput (get);
+               // keyGenerate (get);
+               succeed_if (!compare_key (key,get,0), "did not get the same key after serializing");
+               keyDel (key);
+       }
+       ksDel (ks);
+}
+
+int main()
+{
+       printf("SERIALIZE TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_emptykey();
+       test_allkey();
+       test_xml(srcdir_file("key.xml"));
+       test_xml(srcdir_file("keyset.xml"));
+       printf("\ntest_serialize RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
diff --git a/tests/test_split.c b/tests/test_split.c
new file mode 100644 (file)
index 0000000..716cb1f
--- /dev/null
@@ -0,0 +1,671 @@
+/*************************************************************************** 
+ *           test_split.c  - Test suite for splitted keyset data structure
+ *                  -------------------
+ *  begin                : Fri 21 Mar 2008
+ *  copyright            : (C) 2008 by Markus Raab
+ *  email                : elektra@markus-raab.org
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+/*Needs private declarations*/
+#include <kdbbackend.h>
+
+void test_create()
+{
+       Split *split;
+       split = malloc (sizeof (Split));
+       split->keysets = 0;
+       split->handles = 0;
+       init_splitted_keysets (split);
+
+
+       printf ("Test create and resize split\n");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+
+       resize_splitted_keysets (split);
+       split->keysets[0] = ksNew(0);
+       succeed_if (split->no == 1, "resize not correct");
+       resize_splitted_keysets (split);
+       split->keysets[1] = ksNew(0);
+       succeed_if (split->no == 2, "resize not correct");
+       resize_splitted_keysets (split);
+       split->keysets[2] = ksNew(0);
+       succeed_if (split->no == 3, "resize not correct");
+
+       free_splitted_keysets (split);
+}
+
+
+void test_emptysplit()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (0);
+       Key *parentKey = 0;
+       unsigned long options = 0;
+       Split *split = split_keyset (handle, ks, parentKey, options);
+
+       printf ("Test empty split\n");
+       succeed_if (split->no == 0, "empty requires no data");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+
+       free_splitted_keysets (split);
+       ksDel (ks);
+       kdbClose (handle);
+}
+
+void test_easysplit()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               3,
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               KS_END);
+       Key *parentKey = 0;
+       unsigned long options = 0;
+       Split *split = split_keyset (handle, ks, parentKey, options);
+
+       printf ("Test easy split\n");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 1, "everything is in one keyset");
+       compare_keyset (split->keysets[0], ks, 0, 0);
+
+       free_splitted_keysets (split);
+       ksDel (ks);
+       kdbClose (handle);
+}
+
+void test_singlesplit()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               3,
+               keyNew ("user/valid/key", KEY_END),
+               KS_END);
+       Key *parentKey = 0;
+       unsigned long options = 0;
+       Split *split = split_keyset (handle, ks, parentKey, options);
+
+       printf ("Test single split\n");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 1, "everything is in one keyset");
+       compare_keyset (split->keysets[0], ks, 0, 0);
+
+       free_splitted_keysets (split);
+       ksDel (ks);
+       kdbClose (handle);
+}
+
+void test_mount()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               5,
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split1 = ksNew (
+               3,
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split2 = ksNew (
+               3,
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       Key *parentKey = 0;
+       unsigned long options = 0;
+       Key *mnt;
+       KeySet *config;
+       Split *split;
+
+       kdbMount (handle, mnt=keyNew ("user", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+       kdbMount (handle, mnt=keyNew ("system", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+
+       split = split_keyset (handle, ks, parentKey, options);
+       /*ksOutput (split->keysets[0], stdout, KDB_O_HEADER);*/
+
+
+       printf ("Test mount split\n");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->syncbits[0] == 1, "user part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "system part need to by synced");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       ksDel (ks);
+       ksDel (split1);
+       ksDel (split2);
+       kdbClose (handle);
+}
+
+void test_optimize()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               5,
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split1 = ksNew (
+               3,
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split2 = ksNew (
+               3,
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       Key *parentKey = 0;
+       unsigned long options = 0;
+       Key *mnt;
+       KeySet *config;
+       Split *split;
+       Key *key;
+
+       kdbMount (handle, mnt=keyNew ("user", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+       kdbMount (handle, mnt=keyNew ("system", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+
+       ksRewind (ks);
+       while ((key = ksNext(ks)) != 0)
+       {
+               if (keyIsUser(key) == 1) key->flags &= ~KEY_FLAG_SYNC;
+       }
+
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       printf ("Test optimization split\n");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 0, "user part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "system part not optimized");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+
+
+       ksRewind (ks);
+       while ((key = ksNext(ks)) != 0)
+       {
+               key->flags = 0;
+       }
+
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 0, "user part not optimized");
+       succeed_if (split->syncbits[1] == 0, "system part not optimized");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       options = KDB_O_SYNC;
+       free_splitted_keysets (split);
+
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user part ignoresync");
+       succeed_if (split->syncbits[1] == 1, "system part ignoresync");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+
+
+       ksRewind (ks);
+       while ((key = ksNext(ks)) != 0)
+       {
+               key->flags = KEY_FLAG_SYNC;
+       }
+
+       split = split_keyset (handle, ks, parentKey, options);
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "optimized too much");
+       succeed_if (split->syncbits[1] == 1, "optimized too much");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+
+
+       ksDel (ks);
+       ksDel (split1);
+       ksDel (split2);
+       kdbClose (handle);
+}
+
+void test_removed()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               5,
+               keyNew ("user/valid/key2", KEY_REMOVE, KEY_END),
+               keyNew ("user/valid/key1", KEY_REMOVE, KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split1 = ksNew (
+               3,
+               keyNew ("user/valid/key2", KEY_REMOVE, KEY_END),
+               keyNew ("user/valid/key1", KEY_REMOVE, KEY_END),
+               KS_END);
+       KeySet *split2 = ksNew (
+               3,
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       Key *parentKey = 0;
+       unsigned long options = 0;
+       Key *mnt;
+       KeySet *config;
+       Split *split;
+       Key *key;
+
+       kdbMount (handle, mnt=keyNew ("user", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+       kdbMount (handle, mnt=keyNew ("system", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       printf ("Test optimization split with removed keys\n");
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[1] == 1, "user part need to be synced");
+       succeed_if (split->syncbits[0] == 1, "second part need to by synced");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+
+
+       ksRewind (ks);
+       while ((key = ksNext(ks)) != 0)
+       {
+               /*only removed, no sync*/
+               if (keyIsUser(key) == 1) key->flags &= ~KEY_FLAG_SYNC;
+       }
+
+       split = split_keyset (handle, ks, parentKey, options);
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 0, "optimized too much");
+       succeed_if (split->syncbits[1] == 1, "user part does not need to be synced");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+
+       options = KDB_O_SYNC;
+       split = split_keyset (handle, ks, parentKey, options);
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[1] == 1, "user part ignoresync");
+       succeed_if (split->syncbits[0] == 1, "optimized too much");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+
+       ksDel (ks);
+       ksDel (split1);
+       ksDel (split2);
+       kdbClose (handle);
+}
+
+void test_easyparent()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               8,
+               keyNew ("user/valid", KEY_END),
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               keyNew ("system/valid", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split1 = ksNew (
+               5,
+               keyNew ("user/valid", KEY_END),
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split2 = ksNew (
+               5,
+               keyNew ("system/valid", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       unsigned long options = 0;
+       Key *parentKey;
+       Key *mnt;
+       KeySet *config;
+       Split *split;
+
+       printf ("Test parent separation of user and system\n");
+
+       /*ksOutput (ks, stdout, 0);
+       ksSort (ks);
+       ksOutput (ks, stdout, 0);*/
+
+
+       parentKey = keyNew ("user", KEY_END);
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       /*printf ("%d\n", split->no);*/
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "system part need to be synced");
+       succeed_if (split->belowparents[0] == 1, "user part is below parent");
+       succeed_if (split->belowparents[1] == 0, "system part is not below parent");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       keyDel (parentKey);
+
+       parentKey = keyNew ("system", KEY_END);
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "system part need to be synced");
+       succeed_if (split->belowparents[0] == 0, "user part is not below parent");
+       succeed_if (split->belowparents[1] == 1, "system part is below parent");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       keyDel (parentKey);
+
+       kdbMount (handle, mnt=keyNew ("user", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+       kdbMount (handle, mnt=keyNew ("system", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+
+       parentKey = keyNew ("user", KEY_END);
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "system part need to be synced");
+       succeed_if (split->belowparents[0] == 1, "user part is below parent");
+       succeed_if (split->belowparents[1] == 0, "system part is not below parent");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       keyDel (parentKey);
+
+       parentKey = keyNew ("system", KEY_END);
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       succeed_if (split->no == 2, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "system part need to be synced");
+       succeed_if (split->belowparents[0] == 0, "user part is not below parent");
+       succeed_if (split->belowparents[1] == 1, "system part is below parent");
+       succeed_if (compare_keyset (split->keysets[0], split1, 0, 0) == 0, "user keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       keyDel (parentKey);
+
+       ksDel (ks);
+       ksDel (split1);
+       ksDel (split2);
+       kdbClose (handle);
+}
+
+void test_parent()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               18,
+               keyNew ("user/valid", KEY_END),
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               keyNew ("user/invalid", KEY_END),
+               keyNew ("user/invalid/key1", KEY_END),
+               keyNew ("user/invalid/key2", KEY_END),
+               keyNew ("system/valid", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split0 = ksNew (
+               9,
+               keyNew ("user/valid", KEY_END),
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split1 = ksNew (
+               9,
+               keyNew ("user/invalid", KEY_END),
+               keyNew ("user/invalid/key1", KEY_END),
+               keyNew ("user/invalid/key2", KEY_END),
+               KS_END);
+       KeySet *split2 = ksNew (
+               9,
+               keyNew ("system/valid", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       unsigned long options = 0;
+       Key *parentKey;
+       Key *mnt;
+       KeySet *config;
+       Split *split;
+
+       printf ("Test parent separation of subpath\n");
+
+       /*ksOutput (ks, stdout, 0);
+       ksSort (ks);
+       ksOutput (ks, stdout, 0);*/
+
+       kdbMount (handle, mnt=keyNew ("user", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+       kdbMount (handle, mnt=keyNew ("system", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+
+
+       parentKey = keyNew ("user/valid", KEY_END);
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       /*printf ("%d\n", split->no);*/
+       succeed_if (split->no == 3, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user valid part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "user invalid part need to by synced");
+       succeed_if (split->syncbits[2] == 1, "system part need to be synced");
+       succeed_if (split->belowparents[0] == 1, "user valid part is below parent");
+       succeed_if (split->belowparents[1] == 0, "user invalid part is not below parent");
+       succeed_if (split->belowparents[2] == 0, "system part is not below parent");
+       succeed_if (compare_keyset (split->keysets[0], split0, 0, 0) == 0, "user valid keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split1, 0, 0) == 0, "user invalid keyset not correct");
+       succeed_if (compare_keyset (split->keysets[2], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       keyDel (parentKey);
+
+       ksDel (ks);
+       ksDel (split0);
+       ksDel (split1);
+       ksDel (split2);
+       kdbClose (handle);
+}
+
+
+void test_mountparent()
+{
+       KDB *handle = kdbOpen();
+       KeySet *ks = ksNew (
+               18,
+               keyNew ("user/valid", KEY_END),
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               keyNew ("user/valid/mount", KEY_END),
+               keyNew ("user/valid/mount/key1", KEY_END),
+               keyNew ("user/valid/mount/key2", KEY_END),
+               keyNew ("system/valid", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split0 = ksNew (
+               9,
+               keyNew ("user/valid", KEY_END),
+               keyNew ("user/valid/key1", KEY_END),
+               keyNew ("user/valid/key2", KEY_END),
+               KS_END);
+       KeySet *split1 = ksNew (
+               9,
+               keyNew ("user/valid/mount", KEY_END),
+               keyNew ("user/valid/mount/key1", KEY_END),
+               keyNew ("user/valid/mount/key2", KEY_END),
+               KS_END);
+       KeySet *split2 = ksNew (
+               9,
+               keyNew ("system/valid", KEY_END),
+               keyNew ("system/valid/key1", KEY_END),
+               keyNew ("system/valid/key2", KEY_END),
+               KS_END);
+       unsigned long options = 0;
+       Key *parentKey;
+       Key *mnt;
+       KeySet *config;
+       Split *split;
+
+       printf ("Test parent separation with mounted backend below\n");
+
+       /*ksOutput (ks, stdout, 0);
+       ksSort (ks);
+       ksOutput (ks, stdout, 0);*/
+
+       kdbMount (handle, mnt=keyNew ("user/valid/mount", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+       kdbMount (handle, mnt=keyNew ("system", KEY_VALUE, "filesys", KEY_END), config=ksNew(0));
+       keyDel (mnt); ksDel (config);
+
+
+       parentKey = keyNew ("user/valid", KEY_END);
+       split = split_keyset (handle, ks, parentKey, options);
+
+
+       succeed_if (split->keysets, "did not alloc keysets array");
+       succeed_if (split->handles, "did not alloc handles array");
+       /*printf ("%d\n", split->no);*/
+       succeed_if (split->no == 3, "not splitted according user, system");
+       succeed_if (split->syncbits[0] == 1, "user valid part need to by synced");
+       succeed_if (split->syncbits[1] == 1, "user invalid part need to by synced");
+       succeed_if (split->syncbits[2] == 1, "system part need to be synced");
+       succeed_if (split->belowparents[0] == 1, "user valid part is below parent");
+       succeed_if (split->belowparents[1] == 1, "user mounted part is not below parent");
+       succeed_if (split->belowparents[2] == 0, "system part is not below parent");
+       succeed_if (compare_keyset (split->keysets[0], split0, 0, 0) == 0, "user valid keyset not correct");
+       succeed_if (compare_keyset (split->keysets[1], split1, 0, 0) == 0, "user invalid keyset not correct");
+       succeed_if (compare_keyset (split->keysets[2], split2, 0, 0) == 0, "system keyset not correct");
+
+       free_splitted_keysets (split);
+       keyDel (parentKey);
+
+       ksDel (ks);
+       ksDel (split0);
+       ksDel (split1);
+       ksDel (split2);
+       kdbClose (handle);
+}
+
+
+
+int main()
+{
+       printf("SPLIT       TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_create();
+       test_emptysplit();
+       test_easysplit();
+       test_singlesplit();
+       test_mount();
+       test_optimize();
+       test_removed();
+       test_easyparent();
+       test_parent();
+       test_mountparent();
+
+       printf("\ntest_split RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
+
diff --git a/tests/test_stream.c b/tests/test_stream.c
new file mode 100644 (file)
index 0000000..a82c544
--- /dev/null
@@ -0,0 +1,59 @@
+/***************************************************************************
+ *          test_stream.c  -  streaming test suite
+ *                  -------------------
+ *  begin                : Mon 26 Nov 2007
+ *  copyright            : (C) 2007 by Markus Raab
+ *  email                : elektra@markus-raab.org
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+void test_keyoutput ()
+{
+       KeySet          *ks;
+       FILE *fout = stdout;
+
+       printf("Testing Read and write xml\n");
+       ks = ksNew (0);
+
+//     fout = fopen ("key.txt", "w");
+       exit_if_fail( ksFromXMLfile(ks, srcdir_file ("key.xml")) == 0, "ksFromXMLfile(key.xml) failed.");
+//     compare_files ("key.xml");
+
+//     ksOutput (ks, fout, KEY_VALUE | KEY_COMMENT);
+//     ksOutput (ks, fout, KDB_O_SHOWMETA);
+       ksOutput (ks, fout, KDB_O_SHOWFLAGS);
+       ksDel (ks);
+}
+
+int main()
+{
+       printf("STREAM TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_keyoutput();
+       printf("\ntest_stream RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
diff --git a/tests/test_trie.c b/tests/test_trie.c
new file mode 100644 (file)
index 0000000..1054066
--- /dev/null
@@ -0,0 +1,110 @@
+/*************************************************************************** 
+ *           test_trie.c  - Test suite for trie data structure
+ *                  -------------------
+ *  begin                : Thu Oct 24 2007
+ *  copyright            : (C) 2007 by Patrick Sabin
+ *  email                : patricksabin@gmx.at
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <tests.h>
+
+/*Needs private declarations*/
+#include <kdbprivate.h>
+
+void *mapper(void *s,char *backend)
+{
+       return s;
+}
+
+void test_kdbTrie()
+{
+       KDB *kdb = kdbOpen();
+
+       KeySet *ks=ksNew(0);
+
+       /*
+       k = keyNew ("user/template/key",0);
+       s=kdbGetBackend(kdb,k);
+       exit_if_fail(s, "Got null pointer from kdbGetBackend");
+       succeed_if(!strcmp("libelektra-template",kdbhGetBackendName(s)), "kdbGetBackend: didn't get the correct value");
+       keyDel (k);
+       */
+
+       /*
+       k = keyNew ("user/key",0);
+       s=kdbGetBackend(kdb,k);
+       exit_if_fail(s, "Got null pointer from kdbGetBackend");
+       succeed_if(!strcmp("libelektra-filesys",kdbhGetBackendName(s)), "kdbGetBackend: didn't get the correct value");
+       keyDel (k);
+       
+       k = keyNew ("system/key",0);
+       s=kdbGetBackend(kdb,k);
+       exit_if_fail(s, "Got null pointer from kdbGetBackend");
+       succeed_if(!strcmp("libelektra-filesys",kdbhGetBackendName(s)), "kdbGetBackend: didn't get the correct value");
+       keyDel (k);
+
+       k = keyNew ("system/filesystems/hda/",0);
+       s=kdbGetBackend(kdb,k);
+       exit_if_fail(s, "Got null pointer from kdbGetBackend");
+       succeed_if(!strcmp("libelektra-fstab",kdbhGetBackendName(s)), "kdbGetBackend: didn't get the correct value");
+       keyDel (k);
+       
+       printf ("Looking up dynamic mounting\n");
+       printf(BACKEND_DIR "fstab2/config/path\n");
+       ksAppendKey(ks,keyNew(BACKEND_DIR "fstab2/config/path",KEY_VALUE,"/tmp/fstab2",0));
+       kdbMount(kdb, mountpoint=keyNew("system/fstab/",0), "fstab",ks);
+
+       k = keyNew ("system/fstab/dev/hda",0);
+       s=kdbGetBackend(kdb,k);
+       exit_if_fail(s, "Got null pointer from kdbGetBackend");
+       succeed_if(!strcmp("libelektra-fstab",kdbhGetBackendName(s)), "kdbGetBackend: didn't get the correct value");
+       keyDel (k);
+
+       kdbUnmount(kdb,mountpoint);
+       keyDel(mountpoint);
+
+       k = keyNew ("system/fstab/dev/hda",0);
+       s=kdbGetBackend(kdb,k);
+       exit_if_fail(s, "Got null pointer from kdbGetBackend");
+       succeed_if(!strcmp("libelektra-filesys",kdbhGetBackendName(s)), "kdbGetBackend: didn't get the correct value");
+       keyDel (k);
+       */
+       /**/
+
+       ksDel (ks);
+       kdbClose (kdb);
+}
+
+int main()
+{
+       printf("TRIE       TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+
+       test_kdbTrie();
+
+       printf("\ntest_trie RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
+
diff --git a/tests/test_type.c b/tests/test_type.c
new file mode 100644 (file)
index 0000000..f2bf36e
--- /dev/null
@@ -0,0 +1,79 @@
+/***************************************************************************
+ *          test_type.c  -  Typing related test suite
+ *                  -------------------
+ *  begin                : Thu Aug 03 2006
+ *  copyright            : (C) 2008 Markus Raab
+ *  email                : elektra@markus-raab.org
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "tests.h"
+
+enum
+{
+       KEY_TYPE_COLOR=KEY_TYPE_STRING+4
+};
+
+void test_typeMax(void)
+{
+       Key *k1 = keyNew(0);
+
+       succeed_if (keyGetType(0) == -1, "null pointer check");
+       succeed_if (keySetType(0, KEY_TYPE_COLOR) == -1, "null pointer check");
+
+       succeed_if (keySetType(k1, 256) == -1, "type to large");
+       succeed_if (keySetType(k1, 257) == -1, "type to large");
+       keyDel (k1);
+}
+
+void test_typeColor(void)
+{
+       Key *k1 = keyNew ("user/sw/oyranos/current/color1",
+               KEY_VALUE, "#4B52CA",
+               KEY_COMMENT, "a custom color",
+               KEY_TYPE, KEY_TYPE_COLOR,
+               KEY_END);
+       Key *k2 = keyNew ("user/sw/oyranos/current/color2",
+               KEY_VALUE, "green",
+               KEY_COMMENT, "the green color",
+               KEY_TYPE, KEY_TYPE_COLOR,
+               KEY_END);
+
+       succeed_if (keyGetType(k1) == KEY_TYPE_COLOR, "not type color");
+       succeed_if (keyGetType(k2) == KEY_TYPE_COLOR, "not type color");
+       succeed_if (keyIsString(k1) == 1, "key should be string");
+       succeed_if (keyIsString(k2) == 1, "key should be string");
+       keyDel (k1);
+       keyDel (k2);
+
+       k1 = keyNew (0);
+
+       succeed_if (keySetType(k1, KEY_TYPE_COLOR) == 0, "could not set color");
+       succeed_if (keyGetType(k1) == KEY_TYPE_COLOR, "not type color");
+       succeed_if (keyIsString(k1) == 1, "key should be string");
+       keyDel (k1);
+}
+
+int main()
+{
+       printf("KEY  TYPE  TESTS\n");
+       printf("==================\n\n");
+
+       init ();
+       test_typeMax();
+       test_typeColor();
+
+       printf("\ntest_key RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+
+       return nbError;
+}
diff --git a/tests/test_validate.sh b/tests/test_validate.sh
new file mode 100755 (executable)
index 0000000..65a70fd
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+VALIDATOR=/usr/bin/xmllint
+
+[ "z$srcdir" = 'z' ] && srcdir=.
+
+SCHEMA=$srcdir/../xmlschema/elektra.xsd
+
+[ -x "$VALIDATOR" ] || exit 0
+
+echo
+echo ELEKTRA VALIDATE TESTS
+echo ========================================
+echo
+
+$VALIDATOR --schema "$SCHEMA" $srcdir/key.xml > /dev/null || exit 5
+$VALIDATOR --schema "$SCHEMA" $srcdir/keyset.xml > /dev/null || exit 5
+
+$VALIDATOR --schema "$SCHEMA" $srcdir/filesys.xml > /dev/null || exit 5
+$VALIDATOR --schema "$SCHEMA" $srcdir/fstab.xml > /dev/null || exit 5
+$VALIDATOR --schema "$SCHEMA" $srcdir/passwd.xml > /dev/null || exit 5
+
+exit 0
+
diff --git a/tests/test_xml.c b/tests/test_xml.c
new file mode 100644 (file)
index 0000000..c031657
--- /dev/null
@@ -0,0 +1,226 @@
+/***************************************************************************
+ *          test_kdb.c  -  Backend test suite
+ *                -------------------
+ *  begin                : Thu Aug 03 2006
+ *  copyright            : (C) 2006 by Yannick Lecaillez
+ *  email                : sizon5@gmail.com
+ ****************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+
+#include <tests.h>
+
+void test_readwrite()
+{
+       KeySet          *ks;
+       FILE *fout;
+
+       printf("Testing Read and write xml\n");
+
+       fout = fopen (srcdir_file("key-gen.xml"), "w");
+       ks = ksNew(0);
+       exit_if_fail( ksFromXMLfile(ks, srcdir_file("key.xml")) == 0, "ksFromXMLfile(key.xml) failed.");
+       ksToStream (ks, fout, KDB_O_HEADER);
+       fclose (fout);
+
+       compare_files (srcdir_file("key.xml"));
+       unlink (srcdir_file("key-gen.xml"));
+
+       ksDel (ks);
+}
+
+void test_readwrite_hier()
+{
+       KeySet          *ks;
+       FILE *fout;
+
+       printf("Testing Read and write xml\n");
+
+       fout = fopen (srcdir_file("key-gen.xml"), "w");
+       ks = ksNew(0);
+       exit_if_fail( ksFromXMLfile(ks, srcdir_file("key.xml")) == 0, "ksFromXMLfile(key.xml) failed.");
+       ksToStream (ks, fout, KDB_O_HIER | KDB_O_HEADER);
+       fclose (fout);
+
+       compare_files (srcdir_file("key.xml"));
+       unlink (srcdir_file("key-gen.xml"));
+
+       ksDel (ks);
+}
+
+
+void test_key()
+{
+       KeySet          *ks;
+       Key             *cur;
+       int             counter;
+
+       printf("Testing Key from xml\n");
+
+       ks = ksNew(0);
+       exit_if_fail( ksFromXMLfile(ks, srcdir_file("key.xml")) == 0, "ksFromXMLfile(key.xml) failed.");
+       counter = 0;
+       ksRewind(ks);
+       while ( (cur = ksNext(ks)) ) {
+               counter ++;
+               /* Prepend key root */  
+               //snprintf(buf, sizeof(buf), "%s/%s", root, keyName(cur));
+               //keySetName(cur, buf);
+
+               /* Make tests ... */    
+               // printf ("counter: %d - %s\n", counter, (char*)keyValue(cur));
+               switch (counter)
+               {
+               case 1: succeed_if(strcmp (keyValue(cur), "StringValue") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "String key with\nstandard name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 0, "key is active");
+                       succeed_if(keyIsString(cur) == 1, "key is not string");
+                       succeed_if(keyGetUID(cur) == 20, "could not get uid value");
+                       break;
+               case 2: succeed_if(strcmp (keyValue(cur), "DirectoryValue") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "Directory key with standard name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 0, "key is active");
+                       succeed_if(keyIsString(cur) == 1, "key is not string");
+                       break;
+               case 3: succeed_if(strcmp (keyValue(cur), "BinaryValue") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "Binary key with standard name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 0, "key is active");
+                       succeed_if(keyIsBinary(cur) == 1, "key is not binary");
+                       succeed_if(keyGetGID(cur) == 40, "could not get gid value");
+                       break;
+               case 4: succeed_if(strcmp (keyValue(cur), "StringValue") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "String key with hidden name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 1, "key is inactive");
+                       succeed_if(keyIsString(cur) == 1, "key is not string");
+                       succeed_if(keyGetUID(cur) == 0, "could not get uid value");
+                       succeed_if(keyGetGID(cur) == 20, "could not get gid value");
+                       break;
+               case 5: succeed_if(strcmp (keyValue(cur), "DirectoryValue") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "Directory key with hidden name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 1, "key is inactive");
+                       succeed_if(keyIsString(cur) == 1, "key is not string");
+                       break;
+               case 6: succeed_if(strcmp (keyValue(cur), "BinaryValue") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "Binary key with hidden name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 1, "key is inactive");
+                       succeed_if(keyIsBinary(cur) == 1, "key is not binary");
+                       succeed_if(keyGetType(cur) == 30, "not correct type");
+                       succeed_if(keyGetMode(cur) == 0440, "could not get mode");
+                       break;
+                       // <key type="string" basename="Ug.ly:St@ri€n.g Key" value="With a string value"><comment>string key with ugly name</comment></key>
+               case 7: succeed_if(strcmp (keyValue(cur), "With a string value") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "string key with ugly name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 0, "key is active");
+                       succeed_if(keyIsString(cur) == 1, "key is not string");
+                       break;
+                       // <key type="directory" basename="Ug.ly:Dir@ect€ory Key"><comment>Directory with ugly name</comment></key>
+               case 8: succeed_if(strcmp (keyValue(cur), "") == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "Directory with ugly name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 0, "key is active");
+                       succeed_if(keyIsString(cur) == 1, "key is not string");
+                       break;
+                       // <key type="binary" basename="Ug.ly:Bin@a€ry Key"><comment>Binary key with ugly name</comment></key>
+               case 9: succeed_if(keyValue(cur) == 0, "value not correct");
+                       succeed_if(strcmp (keyComment(cur), "Binary key with ugly name") == 0, "comment not correct");
+                       succeed_if(keyIsInactive (cur) == 0, "key is active");
+                       succeed_if(keyIsBinary(cur) == 1, "key is not binary");
+                       succeed_if(keyGetMode(cur) == 230, "could not get mode");
+                       break;
+
+               }
+       }
+       
+       ksDel(ks);
+}
+
+void test_keyset()
+{
+       KeySet          *ks;
+       Key             *cur;
+       int             counter;
+
+       printf("Testing KeySet from xml\n");
+
+       ks = ksNew(0);
+       exit_if_fail( ksFromXMLfile(ks, srcdir_file("keyset.xml")) == 0, "ksFromXMLfile(key.xml) failed.");
+       counter = 0;
+       ksRewind(ks);
+       while ( (cur = ksNext(ks)) ) {
+               counter ++;
+
+               /* Make tests ... */    
+               // printf ("counter: %d - %s\n", counter, keyName(cur));
+               switch (counter) {
+                       // <key type="43" basename="0-27042916" value="0 216905227"><comment>2551516588474823843</comment></key>
+               case 1: succeed_if (strcmp (keyName(cur),"user/tests/filesys/0-27042916") == 0,"name of first key not correct");
+                       succeed_if (strcmp (keyValue(cur),"0 216905227") == 0,"value of first key not correct");
+                       succeed_if (strcmp (keyComment(cur),"2551516588474823843") == 0,"comment of first key not correct");
+                       succeed_if (keyGetType(cur) == 43, "wrong key type found");
+                       break;
+                       // <key type="253" basename="1-2449524622" value="1 1679328197"><comment>3246436893195629244</comment></key>
+               case 2: succeed_if (strcmp (keyName(cur),"user/tests/filesys/1-2449524622") == 0,"name of 2. key not correct");
+                       succeed_if (strcmp (keyValue(cur),"1 1679328197") == 0,"value of 2. key not correct");
+                       succeed_if (strcmp (keyComment(cur),"3246436893195629244") == 0,"comment of 2. key not correct");
+                       succeed_if (keyGetType(cur) == 253, "wrong key type found");
+                       break;
+                       // <key type="string" basename="dir-1-0">
+               case 3: succeed_if (strcmp (keyName(cur),"user/tests/filesys/dir-1-0") == 0,"name of 3. key not correct");
+                       succeed_if (keyIsDir (cur), "Is not directory, but should be");
+                       succeed_if (keyGetType(cur) == KEY_TYPE_STRING, "wrong key type found");
+                       break;
+                       // <key type="114" basename="0-294164813" value="0 216245011"><comment>18454108762891828026</comment></key>
+               case 4: succeed_if (strcmp (keyName(cur),"user/tests/filesys/dir-1-0/0-294164813") == 0,"name of 4. key not correct");
+                       succeed_if (strcmp (keyValue(cur),"0 216245011") == 0,"value of 4. key not correct");
+                       succeed_if (strcmp (keyComment(cur),"18454108762891828026") == 0,"comment of 4. key not correct");
+                       succeed_if (keyGetType(cur) == 114, "wrong key type found");
+                       break;
+                       // <key type="135" basename="1-1479930365" value="1 2732423037"><comment>24597295372375238</comment></key>
+               case 5: succeed_if (strcmp (keyName(cur),"user/tests/filesys/dir-1-0/1-1479930365") == 0,"name of 4. key not correct");
+                       succeed_if (strcmp (keyValue(cur),"1 2732423037") == 0,"value of 4. key not correct");
+                       succeed_if (strcmp (keyComment(cur),"24597295372375238") == 0,"comment of 4. key not correct");
+                       succeed_if (keyGetType(cur) == 135, "wrong key type found");
+                       break;
+                       // <key type="string" basename="dir-2-0">
+               case 6: succeed_if (strcmp (keyName(cur),"user/tests/filesys/dir-1-0/dir-2-0") == 0,"name of 3. key not correct");
+                       succeed_if (keyIsDir (cur), "Is not directory, but should be");
+                       succeed_if (keyGetType(cur) == KEY_TYPE_STRING, "wrong key type found");
+                       break;
+                       // <key type="144" basename="0-215571059" value="0 264857705"><comment>2188631490667217086</comment></key>
+               case 7: succeed_if (strcmp (keyName(cur),"user/tests/filesys/dir-1-0/dir-2-0/0-215571059") == 0,"name of 4. key not correct");
+                       succeed_if (strcmp (keyValue(cur),"0 264857705") == 0,"value of 4. key not correct");
+                       succeed_if (strcmp (keyComment(cur),"2188631490667217086") == 0,"comment of 4. key not correct");
+                       succeed_if (keyGetType(cur) == 144, "wrong key type found");
+                       break;
+               }
+       }
+       
+       ksDel(ks);
+}
+
+
+int main()
+{
+       printf("\n");
+       printf("ELEKTRA BACKENDS TEST SUITE\n");
+       printf("========================================\n\n");
+
+       init ();
+
+       test_key();
+       test_keyset();
+
+       /*
+       test_readwrite();
+       test_readwrite_hier();
+       */
+
+       printf("\ntest_xml RESULTS: %d test(s) done. %d error(s).\n", nbTest, nbError);
+       
+       return nbError;
+}
diff --git a/tests/tests.c b/tests/tests.c
new file mode 100644 (file)
index 0000000..3671c6b
--- /dev/null
@@ -0,0 +1,276 @@
+#include "tests.h"
+
+int nbError;
+int nbTest;
+
+int nbStreaming;
+uid_t nbUid;
+gid_t nbGid;
+
+char file [MAX_PATH_LENGTH];
+char srcdir [MAX_PATH_LENGTH];
+
+#ifdef HAVE_CLEARENV
+int clearenv();
+#endif
+
+/**Does some useful startup.
+ */
+int init (void)
+{
+       setlocale (LC_ALL, "");
+
+       nbUid = getuid();
+       nbGid = getgid();
+
+       if (getenv ("srcdir"))
+       {
+               strcpy (srcdir, getenv ("srcdir"));
+       } else {
+               strcpy (srcdir, ".");
+               warn_if_fail (0, "srcdir not set, will try current directory");
+       }
+#ifdef HAVE_CLEARENV
+       clearenv();
+#else
+       unsetenv("HOME");
+       unsetenv("USER");
+       unsetenv("KDB_HOME");
+       unsetenv("KDB_USER");
+       unsetenv("KDB_DIR");
+#endif
+
+#ifdef HAVE_SETENV
+       setenv("KDB_HOME",".",1);
+#endif
+
+       nbStreaming =  loadToolsLib();
+       warn_if_fail (nbStreaming == 0, "Unable to load elektratools, no stream testing will be done");
+       return 0;
+}
+
+/**Create a root key for a backend.
+ *
+ * @return a allocated root key */
+Key * create_root_key (const char *backendName)
+{
+       Key *root = keyNew ("user/tests", KEY_END);
+       /*Make mount point beneath root, and do all tests here*/
+       keySetDir(root);
+       keySetUID(root, nbUid);
+       keySetGID(root, nbGid);
+       keyAddBaseName (root, backendName);
+       keySetString (root, backendName);
+       keySetComment (root, "backend root key for tests");
+       keySetString (root, backendName);
+       return root;
+}
+
+/**Create a configuration keyset for a backend.
+ *
+ * @return a allocated configuration keyset for a backend*/
+KeySet *create_conf (const char *filename)
+{
+       return ksNew (2,
+               keyNew("system/path", KEY_VALUE, ".kdb/hosts_kdb", KEY_END),
+               KS_END);
+}
+
+
+int compare_line_files (const char *filename, const char *genfilename)
+{
+       FILE *forg, *fgen;
+       char bufferorg [BUFFER_LENGTH + 1];
+       char buffergen [BUFFER_LENGTH + 1];
+       int line = 0;
+
+       forg = fopen (filename, "r");
+       fgen = fopen (genfilename, "r");
+       exit_if_fail (forg && fgen, "could not open file");
+
+       while ( fgets (bufferorg, BUFFER_LENGTH, forg) &&
+               fgets (buffergen, BUFFER_LENGTH, fgen))
+       {
+               line ++;
+               if (strncmp (bufferorg, buffergen, BUFFER_LENGTH))
+               {
+                       printf ("In file %s, line %d.\n", filename, line);
+                       fclose (forg); fclose (fgen);
+                       succeed_if (0, "comparing lines failed");
+                       return 0;
+               }
+       }
+       fclose (forg); fclose (fgen);
+       return 1;
+}
+
+
+/**Compare two files line by line.
+ *
+ * Fails when there are any differences.
+ *
+ * The original file is passed as parameter.
+ * It will be compared with the file -gen.
+ *
+ * file.xml -> file-gen.xml (xml comparator)
+ * file.txt -> file-gen.txt (line comparator)
+ * file.c -> file-gen.c (c comparator)
+ *
+ */
+int compare_files (const char * filename)
+{
+       char genfilename [MAX_PATH_LENGTH];
+       char * dot = strrchr (filename, '.');
+
+       exit_if_fail (dot != 0, "could not find extension in file");
+
+       strncpy (genfilename, filename, dot- filename);
+       /* does not terminate string, but strcat need it, so: */
+       genfilename[dot-filename] = 0;
+       strcat  (genfilename, "-gen");
+       if (!strcmp (dot, ".xml"))
+       {
+               strcat (genfilename, ".xml");
+       } else if (!strcmp (dot, ".txt"))
+       {
+               strcat (genfilename, ".txt");
+       } else if (!strcmp (dot, ".c"))
+       {
+               strcat (genfilename, ".c");
+       }
+
+       return compare_line_files (filename, genfilename);
+}
+
+
+int compare_key (Key *k1, Key *k2, KDBCap *cap)
+{
+       int     ret;
+       int     err = nbError;
+
+       /* printf ("compare: "); keyOutput (k1, stdout); keyOutput (k2, stdout); printf ("\n"); */
+       ret = keyCompare(k1, k2);
+
+       if (cap)
+       {
+               succeed_if ((ret & KEY_NAME) == 0 , "compare key: NAME not equal");
+               if (!kdbcGetnoTypes(cap))       succeed_if ((ret & KEY_TYPE) == 0 , "compare key: TYPE not equal");
+               if (!kdbcGetnoValue(cap))       succeed_if ((ret & KEY_VALUE) == 0 , "compare key: VALUE not equal");
+               if (!kdbcGetnoOwner(cap))       succeed_if ((ret & KEY_OWNER) == 0 , "compare key: OWNER not equal");
+               if (!kdbcGetnoComment(cap))     succeed_if ((ret & KEY_COMMENT) == 0 , "compare key: COMMENT not equal");
+               if (!kdbcGetnoUID(cap))         succeed_if ((ret & KEY_UID) == 0 , "compare key: UID not equal");
+               if (!kdbcGetnoGID(cap))         succeed_if ((ret & KEY_GID) == 0 , "compare key: GID not equal");
+               if (!kdbcGetnoMode(cap))        succeed_if ((ret & KEY_MODE ) == 0 , "compare key: MODE  not equal");
+       } else {
+               succeed_if ((ret & KEY_NAME) == 0 , "compare key: NAME not equal");
+               succeed_if ((ret & KEY_TYPE) == 0 , "compare key: TYPE not equal");
+               succeed_if ((ret & KEY_VALUE) == 0 , "compare key: VALUE not equal");
+               succeed_if ((ret & KEY_OWNER) == 0 , "compare key: OWNER not equal");
+               succeed_if ((ret & KEY_COMMENT) == 0 , "compare key: COMMENT not equal");
+               succeed_if ((ret & KEY_UID) == 0 , "compare key: UID not equal");
+               succeed_if ((ret & KEY_GID) == 0 , "compare key: GID not equal");
+               succeed_if ((ret & KEY_MODE ) == 0 , "compare key: MODE  not equal");
+       }
+
+       return err-nbError;
+}
+
+/**Compare two keysets.
+ * Compare if two keysets contain the same keys.
+ * There is a filter for ks, to remove inactive keys, directories and non-directories
+ * You can use:
+ * - option_t::KDB_O_NODIR 
+ *   remove all directories (keyIsDir)
+ * - option_t::KDB_O_DIRONLY 
+ *   remove everything but directories (!keyIsDir)
+ * - option_t::KDB_O_INACTIVE (keyIsInactive) 
+ *   remove all inactive keys
+ * - option_t::KDB_O_STATONLY
+ *   remove all value/comments
+ * */
+int compare_keyset (KeySet *ks, KeySet *ks2, int filter, KDBCap *cap)
+{
+       Key     *key = 0;
+       Key     *key2 = 0;
+       int     size = 0;
+       int     dup = 0;
+       int     err = nbError;
+
+       // I would have a _true_ ksCompare() ...
+       ksSort(ks); ksRewind(ks);
+       ksSort(ks2); ksRewind(ks2);
+
+       //SYNC with ksOutput
+       while ((key = ksNext(ks)) != 0)
+       {
+               if (filter & KDB_O_NODIR)    if (keyIsDir (key)) continue;
+               if (filter & KDB_O_DIRONLY)  if (!keyIsDir (key)) continue;
+               if (filter & KDB_O_INACTIVE) if (keyIsInactive (key)) continue;
+               if (filter & KDB_O_STATONLY)
+               {
+                       if (!kdbcGetnoStat(cap))
+                       {       // only remove values for backends which are capable of stating keys.
+                               key = keyDup (key); // use a duplicate here...
+                               dup = 1;
+                               keySetRaw (key, NULL, 0);
+                               keySetComment (key, NULL);
+                               keySetType (key, KEY_TYPE_UNDEFINED);
+                       }
+               }
+               key2 = ksNext(ks2);
+               if (!key2)
+               {
+                       succeed_if (0, "Will break, did not find corresponding key2");
+                       if (dup) keyDel (key);
+                       break;
+               }
+
+               size ++;
+               compare_key (key, key2, cap);
+               if (dup) keyDel (key);
+       }
+
+       if (size == 0) succeed_if (0, "real size was 0");
+       if ( size != ksGetSize(ks2) ) {
+               printf ("%d, %d\n", (int)ksGetSize(ks), (int)ksGetSize(ks2));
+               succeed_if( 0, "There are less keys fetched than keys which have been submitted.");
+       }
+       if ( err-nbError )
+       {
+               if (key && key2) printf ("error comparing %s - %s\n", keyName(key), keyName(key2));
+               else printf ("error comparing null key\n");
+       }
+       return err-nbError;
+}
+
+
+int loadToolsLib(void) {
+       kdbLibHandle dlhandle=0;
+
+       kdbLibInit();
+
+       dlhandle=kdbLibLoad("libelektratools");
+       if (dlhandle == 0) {
+               return 1;
+       }
+       
+       ksFromXMLfile=(KSFromXMLfile)kdbLibSym(dlhandle,"ksFromXMLfile");
+       ksFromXML=(KSFromXML)kdbLibSym(dlhandle,"ksFromXML");
+
+       ksToStream = (output) kdbLibSym (dlhandle, "ksToStream");
+       ksOutput   = (output) kdbLibSym (dlhandle, "ksOutput");
+       ksGenerate = (output) kdbLibSym (dlhandle, "ksGenerate");
+
+       return 0;
+}
+
+/* return file name in srcdir.
+ * No bound checking on file size, may overflow. */
+char * srcdir_file(const char * fileName)
+{
+       strcpy(file, srcdir);
+       strcat(file, "/");
+       strcat(file, fileName);
+       return file;
+}
+
diff --git a/tests/tests.h b/tests/tests.h
new file mode 100644 (file)
index 0000000..34dfa16
--- /dev/null
@@ -0,0 +1,84 @@
+/**Some common functions in use for testing framework*/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the BSD License (revised).                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef TESTS_H
+#define TESTS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#define DYN_LINK
+#include <kdbtools.h>
+#include <kdbloader.h>
+#include <kdbbackend.h>
+
+#define KEY_ROOT "user/tests"
+#define KEY_LENGTH 1024
+#define BUFFER_LENGTH 4096
+
+#define MODE_WRITE 4
+#define MODE_READ 2
+#define MODE_DIR 1
+
+extern int nbError;
+extern int nbTest;
+extern int nbStreaming;
+extern uid_t nbUid;
+extern gid_t nbGid;
+
+int init();
+
+KSFromXMLfile ksFromXMLfile;
+KSFromXML ksFromXML;
+
+output ksToStream;
+output ksOutput;
+output ksGenerate;
+
+#define warn_if_fail(x,y) {nbTest++; if (!(x)) { printf("%s:%d: warn in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y); }}
+#define succeed_if(x,y) {nbTest++; if (!(x)) { nbError++; printf("%s:%d: error in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y); }}
+#define exit_if_fail(x,y) {nbTest++; if (!(x)) { printf("%s:%d: fatal in %s: %s\n", __FILE__, __LINE__, __FUNCTION__, y); exit(1); }}
+
+int compare_key (Key *k1, Key *k2, KDBCap *cap);
+int compare_keyset (KeySet *ks, KeySet *ks2, int filter, KDBCap *cap);
+int compare_files (const char * filename);
+
+int loadToolsLib(void);
+char *srcdir_file(const char * fileName);
+
+Key * create_root_key (const char *backendName);
+KeySet *create_conf (const char *filename);
+
+#endif
+
diff --git a/xmlschema/Makefile.am b/xmlschema/Makefile.am
new file mode 100644 (file)
index 0000000..b5d455d
--- /dev/null
@@ -0,0 +1,4 @@
+elektraxsddir = $(datadir)/sgml/elektra-$(PACKAGE_VERSION)
+EXTRA_DIST = elektra.xsd
+elektraxsd_DATA = elektra.xsd
+
diff --git a/xmlschema/elektra.xsd b/xmlschema/elektra.xsd
new file mode 100644 (file)
index 0000000..d9c1e77
--- /dev/null
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- By RĂ©mi <remipouak@yahoo.fr> -->
+
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+       xmlns="http://www.libelektra.org"
+       targetNamespace="http://www.libelektra.org"
+       attributeFormDefault="unqualified"
+       elementFormDefault="qualified">
+
+       <xsd:annotation>
+               <xsd:documentation>
+               This elektra schema lets you validate your Elektra XML Application.
+               This is the unstable version, please refer to elektra stable.
+               It validates against anything with a valid key and keyset skeleton,
+               arbitrary attributes and elements are accepted.
+
+               The targetNamespace is "http://www.libelektra.org".
+               To use the schema and the namespace, make sure that the root element
+               keyset has the following attributes:
+               xmlns="http://www.libelektra.org"
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:schemaLocation="http://www.libelektra.org elektra.xsd"
+
+               Neither the elements (by using default namespace xmlns=) nor the attributes
+               (because of attributeFormDefault="unqualified" here)
+               needs to be qualifed then.
+
+               The stylesheet comes like the rest of Elektra-Project in BSD Licence.
+               </xsd:documentation>
+       </xsd:annotation>
+       
+       <xsd:element name="keyset"         type="KeySet_type"/>
+
+       <xsd:complexType name="KeySet_type">
+               <xsd:sequence>
+                       <xsd:element name="key"        minOccurs="0" maxOccurs="unbounded"/>
+                       <xsd:element name="keyset"     minOccurs="0" maxOccurs="unbounded"/>
+               </xsd:sequence>
+       
+               <xsd:attribute name="parent"   type="xsd:string"   use="optional">
+                       <xsd:annotation>
+                               <xsd:documentation>
+                               A parent indicates a directory key name that contains the keys
+                               under this KeySet. But, if some key happens to have an absolute
+                               name and is inside a keyset element, its absolute name wins.
+                               </xsd:documentation>
+                       </xsd:annotation>
+               </xsd:attribute>
+       </xsd:complexType>
+
+       <xsd:complexType name="Key_type">
+               <xsd:sequence>
+                       <xsd:element name="value"      type="xsd:string" minOccurs="0"/>
+                       <xsd:element name="comment"    type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
+
+                       <xsd:element name="key"        minOccurs="0" maxOccurs="unbounded"/>
+               </xsd:sequence>
+
+               <xsd:attribute name="id"       type="xsd:ID"       use="optional"/>
+               <xsd:attribute name="name"     type="xsd:string"   use="optional">
+                       <xsd:annotation>
+                               <xsd:documentation>
+                               A key must have a name (absolute) or a basename (relative). If both appear,
+                               the absolute name wins.
+                               </xsd:documentation>
+                       </xsd:annotation>
+               </xsd:attribute>
+               
+               <xsd:attribute name="parent"   type="xsd:string"   use="optional">
+                       <xsd:annotation>
+                               <xsd:documentation>
+                               A parent can't appear in the same entry that has an absolute name.
+                               If parent appears together, name wins.
+                               </xsd:documentation>
+                       </xsd:annotation>
+               </xsd:attribute>
+               
+               <xsd:attribute name="basename" type="xsd:string"   use="optional">
+                       <xsd:annotation>
+                               <xsd:documentation>
+                               A basename can't appear in the same entry that has an absolute name.
+                               If basename appears together, name wins. Absolute name = parent + basename.
+                               </xsd:documentation>
+                       </xsd:annotation>
+               </xsd:attribute>
+               
+               <xsd:attribute name="value" type="xsd:string"   use="optional">
+                       <xsd:annotation>
+                               <xsd:documentation>
+                               Short values can be put as an attribute in the key element, to improve
+                               readability. But, if a value element appears latter, it wins over the attribute.
+                               </xsd:documentation>
+                       </xsd:annotation>
+               </xsd:attribute>
+               
+               <xsd:attribute name="type"     type="value_type"   use="optional" default="string"/>
+               <xsd:attribute name="uid"      type="xsd:string"   use="optional"/>
+               <xsd:attribute name="gid"      type="xsd:string"   use="optional"/>
+               <xsd:attribute name="mode"     type="mode_type"    use="optional"/>
+               <xsd:attribute name="atime"    type="xsd:dateTime" use="optional"/>
+               <xsd:attribute name="ctime"    type="xsd:dateTime" use="optional"/>
+               <xsd:attribute name="mtime"    type="xsd:dateTime" use="optional"/>
+       </xsd:complexType>
+
+       <xsd:simpleType name="value_type">
+               <xsd:restriction base="xsd:string">
+                       <xsd:pattern value="[0-9]*|undefined|string|binary|directory|link"/>
+               </xsd:restriction>
+       </xsd:simpleType>
+
+       <xsd:simpleType name="mode_type">
+               <xsd:restriction base="xsd:string">
+                       <xsd:pattern value="[0-7]{4}"/>
+               </xsd:restriction>
+       </xsd:simpleType>
+       
+</xsd:schema>