From: root Date: Wed, 20 May 2009 14:46:48 +0000 (+0200) Subject: Initial import X-Git-Tag: v0.0.2^0 X-Git-Url: https://saetta.ns0.it/gitweb?a=commitdiff_plain;h=56b10994b7583b5d2661bb01f8c4dcf758d1d0aa;p=zakconfi%2Flibzakconfi Initial import --- 56b10994b7583b5d2661bb01f8c4dcf758d1d0aa diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..948556f --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Andrea Zagli diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..95b742d --- /dev/null +++ b/ChangeLog @@ -0,0 +1,11 @@ +2006-11-01: Andrea Zagli + + * libconfi.c: solved a problem about quoting reserved words + +2006-09-06: Andrea Zagli + + * libconfi.c: some bugfixes + +2006-08-18: Andrea Zagli + + * libconfi.c: escaped sql strings with gdao_strescape() diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..40fce2d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,8 @@ +DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc + +SUBDIRS = src tests data docs + +EXTRA_DIST = libconfi.pc.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libconfi.pc diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..91bbd34 --- /dev/null +++ b/README @@ -0,0 +1,22 @@ +Configurations' database can be created with this sql commands + +CREATE TABLE configs ( + id integer NOT NULL, + name varchar(100) DEFAULT '', + description varchar(255) DEFAULT '', + CONSTRAINT configs_pkey PRIMARY KEY (id), + CONSTRAINT name_unique UNIQUE (name) +); + +CREATE TABLE "values" ( + id_configs integer NOT NULL, + id integer NOT NULL, + id_parent integer, + "key" varchar(50) DEFAULT '', + value text DEFAULT '', + description varchar(255) DEFAULT '', + CONSTRAINT values_pkey PRIMARY KEY (id_configs, id), + CONSTRAINT values_name_unique UNIQUE (id_configs, id_parent, "key") +); + +or from gConfi diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..0aae42d --- /dev/null +++ b/autogen.sh @@ -0,0 +1,98 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir +PROJECT=libconfi +TEST_TYPE=-f +FILE=configure.ac + +DIE=0 + +have_libtool=false +if libtoolize --version < /dev/null > /dev/null 2>&1 ; then + libtool_version=`libtoolize --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'` + case $libtool_version in + 1.4*|1.5*) + have_libtool=true + ;; + esac +fi +if $have_libtool ; then : ; else + echo + echo "You must have libtool 1.4 installed to compile $PROJECT." + echo "Install the appropriate package for your distribution," + echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/" + DIE=1 +fi + +(gtkdocize --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have gtk-doc installed to compile $PROJECT." + echo "Install the appropriate package for your distribution," + echo "or get the source tarball at http://ftp.gnome.org/pub/GNOME/sources/gtk-doc/" + DIE=1 +} + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROJECT." + echo "Install the appropriate package for your distribution," + echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/" + DIE=1 +} + +if automake --version < /dev/null > /dev/null 2>&1 ; then + AUTOMAKE=automake + ACLOCAL=aclocal +else + echo + echo "You must have automake 1.7.x installed to compile $PROJECT." + echo "Install the appropriate package for your distribution," + echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/" + DIE=1 +fi + +if test "$DIE" -eq 1; then + exit 1 +fi + +test $TEST_TYPE $FILE || { + echo "You must run this script in the top-level $PROJECT directory" + exit 1 +} + +if test -z "$AUTOGEN_SUBDIR_MODE"; then + if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." + fi +fi + +rm -rf autom4te.cache + +# README and INSTALL are required by automake, but may be deleted by clean +# up rules. to get automake to work, simply touch these here, they will be +# regenerated from their corresponding *.in files by ./configure anyway. +touch README INSTALL + +$ACLOCAL || exit $? + +libtoolize --force || exit $? +gtkdocize || exit $? + +autoheader || exit $? + +$AUTOMAKE --add-missing || exit $? +autoconf || exit $? +cd $ORIGDIR || exit $? + +if test -z "$AUTOGEN_SUBDIR_MODE"; then + $srcdir/configure --enable-maintainer-mode $AUTOGEN_CONFIGURE_ARGS "$@" || exit $? + + echo + echo "Now type 'make' to compile $PROJECT." +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..2b4adbe --- /dev/null +++ b/configure.ac @@ -0,0 +1,46 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([libconfi], [0.0.2], [azagli@inwind.it]) +AC_CONFIG_SRCDIR([src/libconfi.c]) +AC_CONFIG_HEADER([config.h]) + +AM_INIT_AUTOMAKE() +AM_MAINTAINER_MODE() + +AC_CANONICAL_SYSTEM + +AC_LIBTOOL_WIN32_DLL + +# Checks for programs. +AC_PROG_CC +AC_PROG_LIBTOOL +GTK_DOC_CHECK(1.0) + +# Checks for libraries. +PKG_CHECK_MODULES(LIBCONFI, [glib-2.0 >= 2.8.0 + libgdaobj >= 0.0.2]) + +AC_SUBST(LIBCONFI_CFLAGS) +AC_SUBST(LIBCONFI_LIBS) + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST + +# Checks for library functions. + +AC_CONFIG_FILES([ + libconfi.pc + Makefile + src/Makefile + tests/Makefile + data/Makefile + docs/Makefile + docs/reference/Makefile + docs/reference/version.xml +]) + +AC_OUTPUT diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..42a69b2 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,3 @@ +EXTRA_DIST = \ + confi.sql \ + schema_db.dia diff --git a/data/confi.sql b/data/confi.sql new file mode 100644 index 0000000..fa47c75 --- /dev/null +++ b/data/confi.sql @@ -0,0 +1,18 @@ +CREATE TABLE configs ( + id integer NOT NULL, + name varchar(100) DEFAULT '', + description varchar(255) DEFAULT '', + CONSTRAINT configs_pkey PRIMARY KEY (id), + CONSTRAINT name_unique UNIQUE (name) +); + +CREATE TABLE "values" ( + id_configs integer NOT NULL, + id integer NOT NULL, + id_parent integer, + "key" varchar(50) DEFAULT '', + value text DEFAULT '', + description varchar(255) DEFAULT '', + CONSTRAINT values_pkey PRIMARY KEY (id_configs, id), + CONSTRAINT values_name_unique UNIQUE (id_configs, id_parent, "key") +); diff --git a/data/schema_db.dia b/data/schema_db.dia new file mode 100644 index 0000000..3e4ea31 Binary files /dev/null and b/data/schema_db.dia differ diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..f3ddc22 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = reference diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am new file mode 100644 index 0000000..e1f8a1c --- /dev/null +++ b/docs/reference/Makefile.am @@ -0,0 +1,78 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=libconfi + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../../src + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/src/*.h +CFILE_GLOB=$(top_srcdir)/src/*.c + +# Header files to ignore when scanning. +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES= + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files=version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +INCLUDES=-I$(top_srcdir)/src $(LIBCONFI_CFLAGS) +GTKDOC_LIBS=$(top_builddir)/src/libconfi.la $(LIBCONFI_LIBS) + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in diff --git a/docs/reference/libconfi-docs.sgml b/docs/reference/libconfi-docs.sgml new file mode 100644 index 0000000..fa30fb7 --- /dev/null +++ b/docs/reference/libconfi-docs.sgml @@ -0,0 +1,16 @@ + + +]> + + + libconfi Reference Manual + for libconfi &version; + + + + libconfi + + + diff --git a/docs/reference/libconfi-overrides.txt b/docs/reference/libconfi-overrides.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/reference/libconfi-sections.txt b/docs/reference/libconfi-sections.txt new file mode 100644 index 0000000..ddc2de5 --- /dev/null +++ b/docs/reference/libconfi-sections.txt @@ -0,0 +1,20 @@ +
+libconfi +Confi +Confi +confi_get_type +confi_new +confi_get_configs_list +confi_get_tree +confi_set_root +confi_add_key +confi_add_key_with_value +confi_key_set_key +confi_remove_path +confi_path_get_value +confi_path_set_value +confi_path_move +confi_remove +confi_destroy +
+ diff --git a/docs/reference/libconfi-undocumented.txt b/docs/reference/libconfi-undocumented.txt new file mode 100644 index 0000000..e8ffb3e --- /dev/null +++ b/docs/reference/libconfi-undocumented.txt @@ -0,0 +1,23 @@ +38% symbol docs coverage. +8 symbols documented. +2 symbols incomplete. +13 not documented. + + +Confi +confi_add_key +confi_add_key_with_value +confi_get_configs_list +confi_get_tree +confi_get_type +confi_key_set_key +confi_new +confi_path_get_value +confi_path_move +confi_path_set_value +confi_remove (Returns) +confi_remove_path (Returns) +confi_set_root + + +libconfi:Short_Description diff --git a/docs/reference/libconfi.types b/docs/reference/libconfi.types new file mode 100644 index 0000000..81e97d1 --- /dev/null +++ b/docs/reference/libconfi.types @@ -0,0 +1,3 @@ +#include + +confi_get_type diff --git a/docs/reference/tmpl/libconfi-unused.sgml b/docs/reference/tmpl/libconfi-unused.sgml new file mode 100644 index 0000000..7cbf668 --- /dev/null +++ b/docs/reference/tmpl/libconfi-unused.sgml @@ -0,0 +1,41 @@ + + + + + +@obj: + + + + + + +@klass: + + + + + + +@obj: + + + + + + +@obj: + + + + + + +@klass: + + + + + + + diff --git a/docs/reference/tmpl/libconfi.sgml b/docs/reference/tmpl/libconfi.sgml new file mode 100644 index 0000000..5132058 --- /dev/null +++ b/docs/reference/tmpl/libconfi.sgml @@ -0,0 +1,190 @@ + +Confi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@Returns: + + + + + + + +@gda_client: +@provider_id: +@cnc_string: +@name: +@root: +@create: +@Returns: + + + + + + + +@gda_client: +@provider_id: +@cnc_string: +@filter: +@Returns: + + + + + + + +@confi: +@Returns: + + + + + + + +@confi: +@root: +@Returns: + + + + + + + +@confi: +@parent: +@key: +@Returns: + + + + + + + +@confi: +@parent: +@key: +@value: +@Returns: + + + + + + + +@confi: +@ck: +@Returns: + + + + + + + +@confi: +@path: +@Returns: + + + + + + + +@confi: +@path: +@Returns: + + + + + + + +@confi: +@path: +@value: +@Returns: + + + + + + + +@confi: +@path: +@parent: +@Returns: + + + + + + + +@confi: +@Returns: + + + + + + + +@confi: + + diff --git a/docs/reference/version.xml.in b/docs/reference/version.xml.in new file mode 100644 index 0000000..a24f987 --- /dev/null +++ b/docs/reference/version.xml.in @@ -0,0 +1 @@ +@PACKAGE_VERSION@ diff --git a/libconfi.pc.in b/libconfi.pc.in new file mode 100644 index 0000000..eefd698 --- /dev/null +++ b/libconfi.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: Library to manage configuration with libgdaobj +Version: @PACKAGE_VERSION@ +Requires: glib-2.0, libgdaobj +Libs: -L${libdir} -lconfi +Cflags: -I${includedir} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..9fc1bc6 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) \ + $(LIBCONFI_CFLAGS) + +LIBS = $(LIBCONFI_LIBS) + +lib_LTLIBRARIES = libconfi.la + +libconfi_la_SOURCES = libconfi.c + +libconfi_la_LDFLAGS = -no-undefined + +include_HEADERS = libconfi.h diff --git a/src/libconfi.c b/src/libconfi.c new file mode 100644 index 0000000..798fcc3 --- /dev/null +++ b/src/libconfi.c @@ -0,0 +1,1012 @@ +/* + * Copyright (C) 2005-2006 Andrea Zagli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#include "libconfi.h" + +enum +{ + PROP_0, + PROP_ID_CONFIG, + PROP_NAME, + PROP_DESCRIPTION, + PROP_ROOT +}; + +static void confi_class_init (ConfiClass *klass); +static void confi_init (Confi *confi); + +static void confi_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void confi_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static gchar *path_normalize (Confi *confi, const gchar *path); +static GdaDataModel *path_get_data_model (Confi *confi, const gchar *path); +static gchar *path_get_value_from_db (Confi *confi, const gchar *path); +static void get_children (Confi *confi, GNode *parentNode, gint idParent, gchar *path); +static gboolean confi_delete_id_from_db_values (Confi *confi, gint id); +static gboolean confi_remove_path_traverse_func (GNode *node, gpointer data); + +#define CONFI_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TYPE_CONFI, ConfiPrivate)) + +typedef struct _ConfiPrivate ConfiPrivate; +struct _ConfiPrivate + { + GdaO *gdao; + gint id_config; + gchar *name, + *description, + *root; + GHashTable *values; + + gchar chrquot; + }; + +GType +confi_get_type (void) +{ + static GType confi_type = 0; + + if (!confi_type) + { + static const GTypeInfo confi_info = + { + sizeof (ConfiClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) confi_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (Confi), + 0, /* n_preallocs */ + (GInstanceInitFunc) confi_init, + NULL + }; + + confi_type = g_type_register_static (G_TYPE_OBJECT, "Confi", + &confi_info, 0); + } + + return confi_type; +} + +static void +confi_class_init (ConfiClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (ConfiPrivate)); + + object_class->set_property = confi_set_property; + object_class->get_property = confi_get_property; + + g_object_class_install_property (object_class, PROP_ID_CONFIG, + g_param_spec_int ("id_config", + "Configuraton ID", + "The configuration ID", + 0, G_MAXINT, 0, + G_PARAM_READABLE)); + g_object_class_install_property (object_class, PROP_NAME, + g_param_spec_string ("name", + "Configuraton Name", + "The configuration name", + "", + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_DESCRIPTION, + g_param_spec_string ("description", + "Configuraton Description", + "The configuration description", + "", + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_ROOT, + g_param_spec_string ("root", + "Configuraton Root", + "The configuration root", + "/", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); +} + +static void +confi_init (Confi *confi) +{ +} + +/** + * confi_new: + * @gda_client: a #GdaClient. If it's NULL, it will be created a new one. + * @provider_id: the provider id to connect. + * @cnc_string: the connection string to use to connect to database that + * contains configuration. + * @name: configuration's name. + * @root: + * @create: whether create a config into database if @name doesn't exists. + * + * Returns: the newly created #Confi object, or NULL if it fails. + */ +Confi +*confi_new (GdaClient *gda_client, + const gchar *provider_id, + const gchar *cnc_string, + const gchar *name, + const gchar *root, + gboolean create) +{ + if (name == NULL) return NULL; + + GdaDataModel *dm; + gchar *sql; + gint id = 0; + + Confi *confi = CONFI (g_object_new (confi_get_type (), NULL)); + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + priv->gdao = gdao_new_from_string (gda_client, provider_id, cnc_string); + if (priv->gdao == NULL) + { + /* TO DO */ + return NULL; + } + + priv->chrquot = gdao_get_chr_quoting (priv->gdao); + + /* check if config exists */ + sql = g_strdup_printf ("SELECT id, name FROM configs WHERE name = '%s'", + gdao_strescape (name, NULL)); + dm = gdao_query (priv->gdao, sql); + if (dm == NULL || gda_data_model_get_n_rows (dm) == 0) + { + if (create) + { + /* saving a new config into database */ + dm = gdao_query (priv->gdao, "SELECT MAX(id) FROM configs"); + if (dm != NULL) + { + id = gdao_data_model_get_value_integer_at (dm, 0, 0); + } + id++; + + if (gdao_execute (priv->gdao, g_strdup_printf ("INSERT INTO configs " + "(id, name, description) " + "VALUES (%d, '%s', '')", + id, gdao_strescape (name, NULL))) == -1) + { + return NULL; + } + } + else + { + /* TO DO */ + return NULL; + } + } + else + { + id = gdao_data_model_get_value_integer_at (dm, 0, 0); + } + + g_object_set (G_OBJECT (confi), + "name", name, + "root", root, + NULL); + priv->id_config = id; + priv->values = g_hash_table_new (g_str_hash, g_str_equal); + + return confi; +} + +/** + * confi_get_configs_list: + * @gda_client: a #GdaClient. If it's NULL, it will be created a new one. + * @provider_id: the provider id to connect. + * @cnc_string: the connection string to use to connect to database that + * contains configuration. + * @filter: + * + * Returns: a #GList of #Confi. If there's no configurations, returns a valid + * #GList but with a unique NULL element. + */ +GList +*confi_get_configs_list (GdaClient *gda_client, + const gchar *provider_id, + const gchar *cnc_string, + const gchar *filter) +{ + GList *lst = NULL; + gchar *sql, *where = ""; + + GdaO *gdao = gdao_new_from_string (gda_client, provider_id, cnc_string); + + if (gdao == NULL) + { + return NULL; + } + + if (filter != NULL && strcmp (g_strstrip (g_strdup (filter)), "") != 0) + { + where = g_strdup_printf (" WHERE name LIKE '%s'", filter); + } + + sql = g_strdup_printf ("SELECT * FROM configs%s", where); + + GdaDataModel *dmConfigs = gdao_query (gdao, sql); + if (dmConfigs != NULL) + { + gint row, id, + rows = gda_data_model_get_n_rows (dmConfigs); + Confi *confi; + ConfiPrivate *priv; + + if (rows > 0) + { + for (row = 0; row < rows; row++) + { + confi = confi_new (gda_client, provider_id, cnc_string, + gdao_data_model_get_field_value_stringify_at (dmConfigs, row, "name"), + NULL, FALSE); + + priv = CONFI_GET_PRIVATE (confi); + priv->id_config = gdao_data_model_get_field_value_integer_at (dmConfigs, row, "id"); + + g_object_set (G_OBJECT (confi), + "description", gdao_data_model_get_field_value_stringify_at (dmConfigs, row, "description"), + NULL); + + lst = g_list_append (lst, confi); + } + } + else + { + lst = g_list_append (lst, NULL); + } + } + + gdao_free (gdao); + + return lst; +} + +/** + * confi_get_tree: + * @confi: a #Confi object. + * + */ +GNode +*confi_get_tree (Confi *confi) +{ + gchar *path = ""; + GNode *node; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + ConfiKey *ck = (ConfiKey *)g_malloc0 (sizeof (ConfiKey)); + + ck->id_config = priv->id_config; + ck->id = 0; + ck->id_parent = 0; + ck->key = g_strdup ("/"); + + node = g_node_new (ck); + + get_children (confi, node, 0, path); + + return node; +} + +/** + * confi_set_root: + * @confi: a #Confi object. + * @root: the root. + * + */ +gboolean +confi_set_root (Confi *confi, const gchar *root) +{ + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + gchar *root_; + + if (root == NULL) + { + root_ = g_strdup ("/"); + } + else + { + root_ = g_strstrip (g_strdup (root)); + if (strcmp (root_, "") == 0) + { + root_ = g_strdup ("/"); + } + else + { + if (root_[0] != '/') + { + root_ = g_strconcat ("/", root_, NULL); + } + if (root_[strlen (root_) - 1] != '/') + { + root_ = g_strconcat (root_, "/", NULL); + } + } + } + + priv->root = root_; + + return TRUE; +} + +/** + * confi_add_key: + * @confi: a #Confi object. + * @parent: the path where add the key. + * @key: the key's name. + * + * Returns: a #ConfigKey struct filled with data from the key just added. + */ +ConfiKey +*confi_add_key (Confi *confi, const gchar *parent, const gchar *key) +{ + ConfiKey *ck = NULL; + GdaDataModel *dmParent; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + gint id_parent; + gchar *parent_ = g_strstrip (g_strdup (parent)), + *key_ = g_strstrip (g_strdup (key)); + if (strcmp (parent_, "") == 0) + { + id_parent = 0; + } + else + { + dmParent = path_get_data_model (confi, path_normalize (confi, parent_)); + if (dmParent == NULL) + { + id_parent = -1; + } + else + { + id_parent = gdao_data_model_get_field_value_integer_at (dmParent, 0, "id"); + } + } + + if (id_parent > -1) + { + gchar *sql; + gint id = 0; + GdaDataModel *dm; + + /* find new id */ + sql = g_strdup_printf ("SELECT MAX(id) FROM %cvalues%c " + "WHERE id_configs = %d ", + priv->chrquot, priv->chrquot, + priv->id_config); + dm = gdao_query (priv->gdao, sql); + if (dm != NULL) + { + id = gdao_data_model_get_value_integer_at (dm, 0, 0); + } + id++; + + sql = g_strdup_printf ("INSERT INTO %cvalues%c " + "(id_configs, id, id_parent, %ckey%c, value) " + "VALUES (%d, %d, %d, '%s', '%s')", + priv->chrquot, priv->chrquot, + priv->chrquot, priv->chrquot, + priv->id_config, + id, + id_parent, + gdao_strescape (key_, NULL), + ""); + if (gdao_execute (priv->gdao, sql) == -1) + { + /* TO DO */ + return NULL; + } + + ck = (ConfiKey *)g_malloc0 (sizeof (ConfiKey)); + ck->id_config = priv->id_config; + ck->id = id; + ck->id_parent = id_parent; + ck->key = g_strdup (key_); + ck->value = g_strdup (""); + ck->description = g_strdup (""); + if (id_parent == 0) + { + ck->path = g_strdup (""); + } + else + { + ck->path = g_strdup (parent_); + } + } + + return ck; +} + +/** + * confi_add_key_with_value: + * @confi: a #Confi object. + * @parent: the path where add the key. + * @key: the key's name. + * @value: the key's value. + * + * Returns: a #ConfigKey struct filled with data from the key just added. + */ +ConfiKey +*confi_add_key_with_value (Confi *confi, const gchar *parent, const gchar *key, const gchar *value) +{ + ConfiKey *ck = confi_add_key (confi, parent, key); + + if (ck != NULL) + { + gchar *path = ""; + if (ck->id_parent != 0) + { + path = g_strconcat (ck->path, "/", key, NULL); + } + else + { + path = ck->key; + } + + if (!confi_path_set_value (confi, path, value)) + { + ck = NULL; + } + else + { + ck->value = g_strdup (value); + } + } + + return ck; +} + +/** + * confi_key_set_key: + * @confi: a #Confi object. + * @ck: a #ConfiKey struct. + * + */ +gboolean +confi_key_set_key (Confi *confi, + ConfiKey *ck) +{ + gboolean ret = FALSE; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + gchar *sql = g_strdup_printf ("UPDATE %cvalues%c SET " + "%ckey%c = '%s', value = '%s', description = '%s' " + "WHERE id_configs = %d " + "AND id = %d", + priv->chrquot, priv->chrquot, + priv->chrquot, priv->chrquot, + gdao_strescape (ck->key, NULL), + gdao_strescape (ck->value, NULL), + gdao_strescape (ck->description, NULL), + priv->id_config, + ck->id); + + ret = (gdao_execute (priv->gdao, sql) >= 0); + + return ret; +} + +/** + * confi_remove_path: + * @confi: a #Confi object. + * @path: the path to remove. + * + * Removes @path and every child key. + */ +gboolean +confi_remove_path (Confi *confi, const gchar *path) +{ + gboolean ret = FALSE; + GdaDataModel *dm; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + dm = path_get_data_model (confi, path_normalize (confi, path)); + + if (dm != NULL && gda_data_model_get_n_rows (dm) > 0) + { + gchar *path_ = g_strdup (path); + + /* removing every child key */ + GNode *node, *root; + gint id = gdao_data_model_get_field_value_integer_at (dm, 0, "id"); + + node = g_node_new (path_); + get_children (confi, node, id, path_); + + root = g_node_get_root (node); + + if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1) + { + g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, confi_remove_path_traverse_func, (gpointer)confi); + } + + /* removing the path */ + ret = confi_delete_id_from_db_values (confi, id); + } + else + { + g_warning ("Path %s doesn't exists.", path); + } + + return ret; +} + +/** + * confi_path_get_value: + * @confi: a #Confi object. + * @path: the path from which retrieving the value. + * + * Returns: the configuration's value as a string. + */ +gchar +*confi_path_get_value (Confi *confi, const gchar *path) +{ + gchar *ret = NULL, + *path_; + gpointer *gp; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + path_ = path_normalize (confi, path); + if (path_ == NULL) + { + return NULL; + } + + gp = g_hash_table_lookup (priv->values, (gconstpointer)path_); + + if (gp == NULL) + { + /* load value from db if path exists */ + ret = path_get_value_from_db (confi, path_); + + if (ret != NULL) + { + /* and insert it on values */ + g_hash_table_insert (priv->values, (gpointer)path_, (gpointer)ret); + } + else + { + /* TO DO */ + } + } + else + { + ret = g_strdup ((gchar *)gp); + } + + return ret; +} + +/** + * confi_path_set_value: + * @confi: a #Confi object. + * @path: the key's path. + * @value: the value to set. + * + */ +gboolean +confi_path_set_value (Confi *confi, const gchar *path, const gchar *value) +{ + gboolean ret = FALSE; + GdaDataModel *dm = path_get_data_model (confi, path_normalize (confi, path)); + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + if (dm != NULL && gda_data_model_get_n_rows (dm) > 0) + { + gchar *sql = g_strdup_printf ("UPDATE %cvalues%c SET value = '%s' " + "WHERE id_configs = %d " + "AND id = %d ", + priv->chrquot, priv->chrquot, + gdao_strescape (value, NULL), + priv->id_config, + gdao_data_model_get_field_value_integer_at (dm, 0, "id")); + ret = (gdao_execute (priv->gdao, sql) >= 0); + } + else + { + g_warning ("Path %s doesn't exists.", path); + } + + return ret; +} + +/** + * confi_path_move: + * @confi: a #Confi object. + * @path: the key's path to move. + * @parent: the path where add the key. + * + */ +gboolean +confi_path_move (Confi *confi, const gchar *path, const gchar *parent) +{ + gboolean ret = TRUE; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + GdaDataModel *dmPath = path_get_data_model (confi, path_normalize (confi, path)); + if (dmPath == NULL) return FALSE; + + GdaDataModel *dmParent = path_get_data_model (confi, path_normalize (confi, parent)); + if (dmParent == NULL) return FALSE; + + gchar *sql = g_strdup_printf ("UPDATE %cvalues%c " + "SET id_parent = %d " + "WHERE id_configs = %d " + "AND id = %d", + priv->chrquot, priv->chrquot, + gdao_data_model_get_field_value_integer_at (dmParent, 0, "id"), + priv->id_config, + gdao_data_model_get_field_value_integer_at (dmPath, 0, "id")); + + ret = (gdao_execute (priv->gdao, sql) >= 0); + + return ret; +} + +/** + * confi_path_get_confi_key: + * @confi: a #Confi object. + * @path: the key's path to get. + * + */ +ConfiKey +*confi_path_get_confi_key (Confi *confi, const gchar *path) +{ + gchar *path_; + ConfiKey *ck; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + path_ = path_normalize (confi, path); + if (path_ == NULL) + { + return NULL; + } + + GdaDataModel *dm = path_get_data_model (confi, path_); + if (dm == NULL || gda_data_model_get_n_rows (dm) <= 0) + { + return NULL; + } + + ck = (ConfiKey *)g_malloc0 (sizeof (ConfiKey)); + ck->id_config = gdao_data_model_get_field_value_integer_at (dm, 0, "id_configs"); + ck->id = gdao_data_model_get_field_value_integer_at (dm, 0, "id"); + ck->id_parent = gdao_data_model_get_field_value_integer_at (dm, 0, "id_parent"); + ck->key = gdao_data_model_get_field_value_stringify_at (dm, 0, "key"); + ck->value = gdao_data_model_get_field_value_stringify_at (dm, 0, "value"); + ck->description = gdao_data_model_get_field_value_stringify_at (dm, 0, "description"); + ck->path = g_strdup (path_); + + return ck; +} + +/** + * confi_remove: + * @confi: a #Confi object. + * + * Remove a configuration from databases and destroy the relative object. + */ +gboolean +confi_remove (Confi *confi) +{ + gboolean ret = TRUE; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + if (gdao_execute (priv->gdao, + g_strdup_printf ("DELETE FROM %cvalues%c WHERE id_configs = %d", + priv->chrquot, priv->chrquot, + priv->id_config)) == -1) + { + ret = FALSE; + } + else + { + if (gdao_execute (priv->gdao, + g_strdup_printf ("DELETE FROM configs WHERE id = %d", + priv->id_config)) == -1) + { + ret = FALSE; + } + else + { + confi_destroy (confi); + } + } + + return ret; +} + +/** + * confi_destroy: + * @confi: a #Confi object. + * + * Destroy the #Confi object, freeing memory. + */ +void +confi_destroy (Confi *confi) +{ + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + gdao_free (priv->gdao); + g_hash_table_destroy (priv->values); + g_free (priv->name); + g_free (priv->description); + g_free (priv->root); +} + +/* PRIVATE */ +static gchar +*path_normalize (Confi *confi, const gchar *path) +{ + gchar *ret, + *lead; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + if (path == NULL) + { + return NULL; + } + + ret = g_strstrip (g_strdup (path)); + if (strcmp (ret, "") == 0) + { + return NULL; + } + else if (ret[strlen (ret) - 1] == '/') + { + return NULL; + } + + /* removing leading '/' */ + for (;;) + { + if (ret[0] == '/') + { + lead = g_strdup (ret + 1); + ret = g_strchug (lead); + } + else + { + break; + } + } + + ret = g_strconcat (priv->root, ret, NULL); + + return ret; +} + +static GdaDataModel +*path_get_data_model (Confi *confi, const gchar *path) +{ + gchar **tokens, *sql, *token; + gint i = 0, id_parent = 0; + GdaDataModel *dm = NULL; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + if (path == NULL) return NULL; + + tokens = g_strsplit (path, "/", 0); + if (tokens == NULL) return NULL; + + while (tokens[i] != NULL) + { + token = g_strstrip (g_strdup (tokens[i])); + if (strcmp (token, "") != 0) + { + sql = g_strdup_printf ("SELECT * " + "FROM %cvalues%c " + "WHERE id_configs = %d " + "AND id_parent = %d " + "AND %ckey%c = '%s'", + priv->chrquot, priv->chrquot, + priv->id_config, + id_parent, + priv->chrquot, priv->chrquot, + gdao_strescape (token, NULL)); + dm = gdao_query (priv->gdao, sql); + if (dm == NULL || gda_data_model_get_n_rows (dm) != 1) + { + /* TO DO */ + g_warning ("Unable to find key «%s».", token); + dm = NULL; + break; + } + id_parent = gdao_data_model_get_field_value_integer_at (dm, 0, "id"); + } + + i++; + } + + return dm; +} + +static gchar +*path_get_value_from_db (Confi *confi, const gchar *path) +{ + gchar *ret = NULL; + GdaDataModel *dm; + + dm = path_get_data_model (confi, path); + if (dm != NULL) + { + ret = gdao_data_model_get_field_value_stringify_at (dm, 0, "value"); + } + + return ret; +} + +static void +get_children (Confi *confi, GNode *parentNode, gint idParent, gchar *path) +{ + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + gchar *sql = g_strdup_printf ("SELECT * FROM %cvalues%c " + "WHERE id_configs = %d AND " + "id_parent = %d", + priv->chrquot, priv->chrquot, + priv->id_config, + idParent); + + GdaDataModel *dm = gdao_query (priv->gdao, sql); + if (dm != NULL) + { + gint i, rows = gda_data_model_get_n_rows (dm); + for (i = 0; i < rows; i++) + { + GNode *newNode; + ConfiKey *ck = (ConfiKey *)g_malloc0 (sizeof (ConfiKey)); + + ck->id_config = priv->id_config; + ck->id = gdao_data_model_get_field_value_integer_at (dm, i, "id"); + ck->id_parent = gdao_data_model_get_field_value_integer_at (dm, i, "id_parent"); + ck->key = g_strdup (gdao_data_model_get_field_value_stringify_at (dm, i, "key")); + ck->value = g_strdup (gdao_data_model_get_field_value_stringify_at (dm, i, "value")); + ck->description = g_strdup (gdao_data_model_get_field_value_stringify_at (dm, i, "description")); + ck->path = g_strdup (path); + + newNode = g_node_append_data (parentNode, ck); + + get_children (confi, newNode, ck->id, g_strconcat (path, (strcmp (path, "") == 0 ? "" : "/"), ck->key, NULL)); + } + } +} + +static void +confi_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + Confi *confi = CONFI (object); + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + switch (property_id) + { + case PROP_NAME: + priv->name = g_strdup (g_value_get_string (value)); + gdao_execute (priv->gdao, g_strdup_printf ("UPDATE configs " + "SET name = '%s' " + "WHERE id = %d", + gdao_strescape (priv->name, NULL), + priv->id_config)); + break; + + case PROP_DESCRIPTION: + priv->description = g_strdup (g_value_get_string (value)); + gdao_execute (priv->gdao, g_strdup_printf ("UPDATE configs " + "SET description = '%s' " + "WHERE id = %d", + gdao_strescape (priv->description, NULL), + priv->id_config)); + break; + + case PROP_ROOT: + confi_set_root (confi, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +confi_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + Confi *confi = CONFI (object); + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + switch (property_id) + { + case PROP_ID_CONFIG: + g_value_set_int (value, priv->id_config); + break; + + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case PROP_DESCRIPTION: + g_value_set_string (value, priv->description); + break; + + case PROP_ROOT: + g_value_set_string (value, priv->root); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +confi_delete_id_from_db_values (Confi *confi, gint id) +{ + gboolean ret = FALSE; + + ConfiPrivate *priv = CONFI_GET_PRIVATE (confi); + + gchar *sql = g_strdup_printf ("DELETE FROM %cvalues%c " + "WHERE id_configs = %d " + "AND id = %d", + priv->chrquot, priv->chrquot, + priv->id_config, + id); + + return (gdao_execute (priv->gdao, sql) >= 0); +} + +static gboolean +confi_remove_path_traverse_func (GNode *node, gpointer data) +{ + ConfiKey *ck = (ConfiKey *)node->data; + if (ck->id != 0) + { + confi_delete_id_from_db_values ((Confi *)data, ck->id); + } + + return FALSE; +} diff --git a/src/libconfi.h b/src/libconfi.h new file mode 100644 index 0000000..04bb9ca --- /dev/null +++ b/src/libconfi.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2006 Andrea Zagli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __LIBCONFI_H__ +#define __LIBCONFI_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + + +#define TYPE_CONFI (confi_get_type ()) +#define CONFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CONFI, Confi)) +#define CONFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_CONFI, ConfiClass)) +#define IS_CONFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CONFI)) +#define IS_CONFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CONFI)) +#define CONFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CONFI, ConfiClass)) + + +typedef struct _Confi Confi; +typedef struct _ConfiClass ConfiClass; + +struct _Confi + { + GObject parent; + }; + +struct _ConfiClass + { + GObjectClass parent_class; + }; + +typedef struct + { + gint id_config, + id, + id_parent; + gchar *key, + *value, + *description, + *path; + } ConfiKey; + +GType confi_get_type (void) G_GNUC_CONST; + +Confi *confi_new (GdaClient *gda_client, + const gchar *provider_id, + const gchar *cnc_string, + const gchar *name, + const gchar *root, + gboolean create); + +GList *confi_get_configs_list (GdaClient *gda_client, + const gchar *provider_id, + const gchar *cnc_string, + const gchar *filter); + +GNode *confi_get_tree (Confi *confi); + +gboolean confi_set_root (Confi *confi, + const gchar *root); + +ConfiKey *confi_add_key (Confi *confi, + const gchar *parent, + const gchar *key); +ConfiKey *confi_add_key_with_value (Confi *confi, + const gchar *parent, + const gchar *key, + const gchar *value); + +gboolean confi_key_set_key (Confi *confi, + ConfiKey *ck); + +gboolean confi_remove_path (Confi *confi, + const gchar *path); + +gchar *confi_path_get_value (Confi *confi, + const gchar *path); +gboolean confi_path_set_value (Confi *confi, + const gchar *path, + const gchar *value); + +gboolean confi_path_move (Confi *confi, + const gchar *path, + const gchar *parent); + +ConfiKey *confi_path_get_confi_key (Confi *confi, + const gchar *path); + +gboolean confi_remove (Confi *confi); + +void confi_destroy (Confi *confi); + + +G_END_DECLS + +#endif /* __LIBCONFI_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..d7edfb4 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = $(LIBCONFI_CFLAGS) \ + -I../src + +LIBS = $(LIBCONFI_LIBS) \ + -L../src -lconfi + +noinst_PROGRAMS = test diff --git a/tests/test.c b/tests/test.c new file mode 100644 index 0000000..20b78d5 --- /dev/null +++ b/tests/test.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2006 Andrea Zagli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +gboolean +traverse_func (GNode *node, + gpointer data) +{ + ConfiKey *ck = (ConfiKey *)node->data; + if (ck->id != 0) + { + g_fprintf (stderr, "%s%s%s\n", ck->path, strcmp (ck->path, "") == 0 ? "" : "/", ck->key); + } + + return FALSE; +} + +int +main (int argc, char **argv) +{ + GdaClient *gda_client; + Confi *confi; + GNode *tree; + + gda_init ("test", "0.0.1", argc, argv); + gda_client = gda_client_new (); + if (gda_client == NULL) + { + g_fprintf (stderr, "Errore nell'inizializzazione del gda_client\n"); + return 0; + } + + confi = confi_new (gda_client, "PostgreSQL", "HOSTADDR=127.0.0.1;PORT=5432;DATABASE=confi;HOST=localhost;USER=postgres", "Default", NULL, FALSE); + if (confi == NULL) + { + g_fprintf (stderr, "Errore nell'inizializzazione della configurazione\n"); + return 0; + } + + g_fprintf (stderr, "Value from key \"folder/key1/key1_2\"\n%s\n\n", confi_path_get_value (confi, "folder/key1/key1_2")); + + g_fprintf (stderr, "Traversing the entire tree\n"); + tree = confi_get_tree (confi); + g_node_traverse (tree, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse_func, NULL); + g_fprintf (stderr, "\n"); + + g_fprintf (stderr, "Setting root \"key2\"\n"); + confi_set_root (confi, "key2"); + g_fprintf (stderr, "Value from key \"key2-1\" %s\n", confi_path_get_value (confi, "key2-1")); + + confi_destroy (confi); + + return 0; +}