From b740cae8b12391eb9fcefdcdf8843d227044c14f Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Thu, 19 Jul 2012 18:14:37 -0400
Subject: [PATCH 06/11] [INI] The implementation of the new interface

The three files in the patch implement the functions
of the new interface. It is mostly inspired by the old
interface except that the string value is replaced with
the value object that hides all the metadata about
the value itself. The main function to get value
now allows more than one value per key.
---
 ini/ini_get_array_valueobj.c |  389 +++++++++++++++++++++
 ini/ini_get_valueobj.c       |  794 ++++++++++++++++++++++++++++++++++++++++++
 ini/ini_list_valueobj.c      |  136 +++++++
 3 files changed, 1319 insertions(+), 0 deletions(-)
 create mode 100644 ini/ini_get_array_valueobj.c
 create mode 100644 ini/ini_get_valueobj.c
 create mode 100644 ini/ini_list_valueobj.c

diff --git a/ini/ini_get_array_valueobj.c b/ini/ini_get_array_valueobj.c
new file mode 100644
index 0000000..03fdd60
--- /dev/null
+++ b/ini/ini_get_array_valueobj.c
@@ -0,0 +1,389 @@
+/*
+    INI LIBRARY
+
+    Value interpretation functions for arrays of values
+    and corresponding memory cleanup functions.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2012
+
+    INI 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 3 of the License, or
+    (at your option) any later version.
+
+    INI 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 INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <locale.h>
+#include "config.h"
+#include "trace.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "ini_defines.h"
+#include "ini_configobj.h"
+
+/*
+ * Internal contants to indicate how
+ * to process the lists of strings.
+ */
+#define EXCLUDE_EMPTY   0
+#define INCLUDE_EMPTY   1
+
+/* Maximum number of separators supported. Do not make it less than 3. */
+#define MAX_SEP_LEN     3
+
+/* Arrays of stings */
+static char **get_str_cfg_array(struct value_obj *vo,
+                                int include,
+                                const char *sep,
+                                int *size,
+                                int *error)
+{
+    char *copy = NULL;
+    char *dest = NULL;
+    char locsep[MAX_SEP_LEN + 1];
+    uint32_t lensep;
+    const char *buff;
+    uint32_t count = 0;
+    uint32_t len = 0;
+    uint32_t resume_len;
+    char **array;
+    const char *start;
+    char *start_array;
+    uint32_t i, j;
+    uint32_t dlen;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Get value and length - no error checking as we checked it above
+     * and there is no other reson the function could to fail.
+     */
+    value_get_concatenated(vo, &buff);
+    value_get_concatenated_len(vo, &dlen);
+
+    /* Handle the separators */
+    if (sep == NULL) {
+        locsep[0] = ',';
+        locsep[1] = '\0';
+        lensep = 2;
+    }
+    else {
+        strncpy(locsep, sep, MAX_SEP_LEN);
+        locsep[MAX_SEP_LEN] = '\0';
+        lensep = strlen(locsep) + 1;
+    }
+
+    /* Allocate memory for the copy of the string */
+    copy = malloc(dlen);
+    if (copy == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if (error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Suppress warning */
+    start = buff;
+
+    /* Loop through the string */
+    dest = copy;
+    for(i = 0; i < dlen; i++) {
+        for(j = 0; j < lensep; j++) {
+            if(buff[i] == locsep[j]) {
+                /* If we found one of the separators trim spaces around */
+                resume_len = len;
+                while (len > 0) {
+                    if (isspace(start[len - 1])) len--;
+                    else break;
+                }
+                TRACE_INFO_STRING("Current:", start);
+                TRACE_INFO_NUMBER("Length:", len);
+                if (len > 0) {
+                    /* Save block aside */
+                    memcpy(dest, start, len);
+                    count++;
+                    dest += len;
+                    *dest = '\0';
+                    dest++;
+                }
+                else if(include) {
+                    count++;
+                    *dest = '\0';
+                    dest++;
+                }
+                if (locsep[j] == '\0') break; /* We are done */
+
+                /* Move forward and trim spaces if any */
+                start += resume_len + 1;
+                i++;
+                TRACE_INFO_STRING("Other pointer :", buff + i);
+                while ((i < dlen) && (isspace(*start))) {
+                    i++;
+                    start++;
+                }
+                len = -1; /* Len will be increased in the loop */
+                i--; /* i will be increased so we need to step back */
+                TRACE_INFO_STRING("Remaining buffer after triming spaces:",
+                                  start);
+                break;
+            }
+        }
+        len++;
+    }
+
+    /* Now we know how many items are there in the list */
+    array = malloc((count + 1) * sizeof(char *));
+    if (array == NULL) {
+        free(copy);
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if (error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Loop again to fill in the pointers */
+    start_array = copy;
+    for (i = 0; i < count; i++) {
+        TRACE_INFO_STRING("Token :", start_array);
+        TRACE_INFO_NUMBER("Item :", i);
+        array[i] = start_array;
+        /* Move to next item */
+        while(*start_array) start_array++;
+        start_array++;
+    }
+    array[count] = NULL;
+
+    if (error) *error = EOK;
+    if (size) *size = count;
+    /* If count is 0 the copy needs to be freed */
+    if (count == 0) free(copy);
+
+    TRACE_FLOW_EXIT();
+    return array;
+}
+
+/* Get array of strings from item eliminating empty tokens */
+char **ini_get_string_config_array(struct value_obj *vo,
+                                   const char *sep, int *size, int *error)
+{
+    TRACE_FLOW_ENTRY();
+    return get_str_cfg_array(vo, EXCLUDE_EMPTY, sep, size, error);
+}
+/* Get array of strings from item preserving empty tokens */
+char **ini_get_raw_string_config_array(struct value_obj *vo,
+                                       const char *sep, int *size, int *error)
+{
+    TRACE_FLOW_ENTRY();
+    return get_str_cfg_array(vo, INCLUDE_EMPTY, sep, size, error);
+}
+
+/* Special function to free string config array */
+void ini_free_string_config_array(char **str_config)
+{
+    TRACE_FLOW_ENTRY();
+
+    if (str_config != NULL) {
+        if (*str_config != NULL) free(*str_config);
+        free(str_config);
+    }
+
+    TRACE_FLOW_EXIT();
+}
+
+/* Get an array of long values.
+ * NOTE: For now I leave just one function that returns numeric arrays.
+ * In future if we need other numeric types we can change it to do strtoll
+ * internally and wrap it for backward compatibility.
+ */
+long *ini_get_long_config_array(struct value_obj *vo, int *size, int *error)
+{
+    const char *str;
+    char *endptr;
+    long val = 0;
+    long *array;
+    uint32_t count = 0;
+    int err;
+    uint32_t dlen;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid value object argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Do we have the size ? */
+    if (size == NULL) {
+        TRACE_ERROR_NUMBER("Invalid size argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Get value and length - no error checking as we checked it above
+     * and there is no other reson the function could to fail.
+     */
+    value_get_concatenated(vo, &str);
+    value_get_concatenated_len(vo, &dlen);
+
+    /* Assume that we have maximum number of different numbers */
+    array = (long *)malloc(sizeof(long) * dlen/2);
+    if (array == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if (error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Now parse the string */
+    while (*str) {
+
+        errno = 0;
+        val = strtol(str, &endptr, 10);
+        err = errno;
+
+        if (err) {
+            TRACE_ERROR_NUMBER("Conversion failed", err);
+            free(array);
+            if (error) *error = err;
+            return NULL;
+        }
+
+        if (endptr == str) {
+            TRACE_ERROR_NUMBER("Nothing processed", EIO);
+            free(array);
+            if (error) *error = EIO;
+            return NULL;
+        }
+
+        /* Save value */
+        array[count] = val;
+        count++;
+        /* Are we done? */
+        if (*endptr == 0) break;
+        /* Advance to the next valid number */
+        for (str = endptr; *str; str++) {
+            if (isdigit(*str) || (*str == '-') || (*str == '+')) break;
+        }
+    }
+
+    *size = count;
+    if (error) *error = EOK;
+
+    TRACE_FLOW_EXIT();
+    return array;
+
+}
+
+/* Get an array of double values */
+double *ini_get_double_config_array(struct value_obj *vo, int *size, int *error)
+{
+    const char *str;
+    char *endptr;
+    double val = 0;
+    double *array;
+    int count = 0;
+    struct lconv *loc;
+    uint32_t dlen;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid value object argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Do we have the size ? */
+    if (size == NULL) {
+        TRACE_ERROR_NUMBER("Invalid size argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Get value and length - no error checking as we checked it above
+     * and there is no other reson the function could to fail.
+     */
+    value_get_concatenated(vo, &str);
+    value_get_concatenated_len(vo, &dlen);
+
+
+    /* Assume that we have maximum number of different numbers */
+    array = (double *)malloc(sizeof(double) * dlen/2);
+    if (array == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if (error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Get locale information so that we can check for decimal point character.
+     * Based on the man pages it is unclear if this is an allocated memory or not.
+     * Seems like it is a static thread or process local structure so
+     * I will not try to free it after use.
+     */
+    loc = localeconv();
+
+    /* Now parse the string */
+    while (*str) {
+        TRACE_INFO_STRING("String to convert",str);
+        errno = 0;
+        val = strtod(str, &endptr);
+        if ((errno == ERANGE) ||
+            ((errno != 0) && (val == 0)) ||
+            (endptr == str)) {
+            TRACE_ERROR_NUMBER("Conversion failed", EIO);
+            free(array);
+            if (error) *error = EIO;
+            return NULL;
+        }
+        /* Save value */
+        array[count] = val;
+        count++;
+        /* Are we done? */
+        if (*endptr == 0) break;
+        TRACE_INFO_STRING("End pointer after conversion",endptr);
+        /* Advance to the next valid number */
+        for (str = endptr; *str; str++) {
+            if (isdigit(*str) || (*str == '-') || (*str == '+') ||
+               /* It is ok to do this since the string is null terminated */
+               ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break;
+        }
+    }
+
+    *size = count;
+    if (error) *error = EOK;
+
+    TRACE_FLOW_EXIT();
+    return array;
+
+}
+
+/* Special function to free long config array */
+void ini_free_long_config_array(long *array)
+{
+    free(array);
+}
+
+/* Special function to free double config array */
+void ini_free_double_config_array(double *array)
+{
+    free(array);
+}
diff --git a/ini/ini_get_valueobj.c b/ini/ini_get_valueobj.c
new file mode 100644
index 0000000..3b7785e
--- /dev/null
+++ b/ini/ini_get_valueobj.c
@@ -0,0 +1,794 @@
+/*
+    INI LIBRARY
+
+    Value interpretation functions for single values
+    and corresponding memory cleanup functions.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2012
+
+    INI 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 3 of the License, or
+    (at your option) any later version.
+
+    INI 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 INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "config.h"
+#include "trace.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "ini_defines.h"
+#include "ini_config_priv.h"
+#include "ini_configobj.h"
+#include "ini_valueobj.h"
+
+/* Macro co convert to HEX value */
+#define HEXVAL(c) (isdigit(c) ? (c - '0') : (tolower(c) - 'a') + 10)
+
+static int is_same_section(struct ini_cfgobj *ini_config,
+                           const char *section)
+{
+    int len = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* If section is not defined it is different */
+    if (ini_config->section == NULL) {
+        TRACE_FLOW_RETURN(0);
+        return 0;
+    }
+
+    len = strlen(section);
+
+    /* If values are same this is the same section */
+    if ((strncasecmp(ini_config->section, section, ini_config->section_len) == 0) &&
+        (ini_config->section_len == len)) {
+        TRACE_FLOW_RETURN(1);
+        return 1;
+    }
+
+    /* Otherwise the values are different */
+    TRACE_FLOW_RETURN(0);
+    return 0;
+}
+
+static int is_same_name(struct ini_cfgobj *ini_config,
+                        const char *name,
+                        int name_len)
+{
+    TRACE_FLOW_ENTRY();
+
+    /* If name is not defined it is different */
+    if (ini_config->name == NULL) {
+        TRACE_FLOW_RETURN(0);
+        return 0;
+    }
+
+    /* If values are same this is the same value */
+    if ((strncasecmp(ini_config->name, name, ini_config->name_len) == 0) &&
+        (ini_config->name_len == name_len)) {
+        TRACE_FLOW_RETURN(1);
+        return 1;
+    }
+
+    /* Otherwise the values are different */
+    TRACE_FLOW_RETURN(0);
+    return 0;
+}
+
+
+/* Function to get value object from the configuration handle */
+int ini_get_config_valueobj(const char *section,
+                            const char *name,
+                            struct ini_cfgobj *ini_config,
+                            int mode,
+                            struct value_obj **vo)
+{
+    int error = EOK;
+    struct collection_item *section_handle = NULL;
+    struct collection_item *item = NULL;
+    const char *to_find;
+    char default_section[] = INI_DEFAULT_SECTION;
+    uint64_t hash = 0;
+    int len = 0, name_len = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the accepting memory ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument vo.", EINVAL);
+        return EINVAL;
+    }
+
+    *vo = NULL;
+
+    if (ini_config == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument ini_config.", EINVAL);
+        return EINVAL;
+    }
+
+    if ((mode < INI_GET_FIRST_VALUE) ||
+        (mode > INI_GET_NEXT_VALUE)) {
+        TRACE_ERROR_NUMBER("Invalid argument mode:", mode);
+        return EINVAL;
+    }
+
+    /* Do we have a name ? */
+    if (name == NULL) {
+        TRACE_ERROR_NUMBER("Name is NULL it will not be found.", EINVAL);
+        return EINVAL;
+    }
+
+    /* Empty section means look for the default one */
+    if (section == NULL) to_find = default_section;
+    else to_find = section;
+
+    TRACE_INFO_STRING("Getting Name:", name);
+    TRACE_INFO_STRING("In Section:", section);
+
+    /* Make sure we start over if this is the first value */
+    if (mode == INI_GET_FIRST_VALUE) ini_config_clean_state(ini_config);
+
+    /* Are we looking in the same section ? */
+    if (!is_same_section(ini_config, section)) {
+
+        /* This is a different section */
+        ini_config_clean_state(ini_config);
+
+        /* Get Subcollection */
+        error = col_get_collection_reference(ini_config->cfg, &section_handle, to_find);
+        /* Check error */
+        if (error && (error != ENOENT)) {
+            TRACE_ERROR_NUMBER("Failed to get section", error);
+            return error;
+        }
+
+        /* Did we find a section */
+        if ((error == ENOENT) || (section_handle == NULL)) {
+            /* We have not found section - return success */
+                TRACE_FLOW_EXIT();
+            return EOK;
+        }
+
+        /* Create an iterator */
+        error = col_bind_iterator(&(ini_config->iterator),
+                                  section_handle,
+                                  COL_TRAVERSE_ONELEVEL);
+        /* Make sure we free the section we found */
+        col_destroy_collection(section_handle);
+        /* Check error */
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to bind to section", error);
+            return error;
+        }
+
+        /* Save section */
+        ini_config->section_len = strlen(to_find);
+        ini_config->section = strndup(to_find, ini_config->section_len);
+        /* Check error */
+        if (ini_config->section == NULL) {
+            TRACE_ERROR_NUMBER("Failed to save section name ", ENOMEM);
+            ini_config_clean_state(ini_config);
+            return ENOMEM;
+        }
+    }
+
+    hash = col_make_hash(name, 0, &name_len);
+
+    /* Check if this is the same name */
+    if (!is_same_name(ini_config, name, name_len)) {
+        TRACE_INFO_STRING("Saved name:", ini_config->name);
+        TRACE_INFO_STRING("Passed name:", name);
+        TRACE_INFO_NUMBER("Length of the saved name", ini_config->name_len);
+        TRACE_INFO_NUMBER("Length of the passed name", name_len);
+        col_rewind_iterator(ini_config->iterator);
+        free(ini_config->name);
+        ini_config->name = NULL;
+        ini_config->name_len = 0;
+    }
+
+    /* Iterate through the section */
+    do {
+
+        /* Loop through a collection */
+        error = col_iterate_collection(ini_config->iterator, &item);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to iterate", error);
+            ini_config_clean_state(ini_config);
+            return error;
+        }
+
+        /* Are we done ? */
+        if (item == NULL) {
+            /* There is nothing left to look for */
+            ini_config_clean_state(ini_config);
+            TRACE_FLOW_EXIT();
+            return EOK;
+        }
+
+        if ((hash == (unsigned long int)col_get_item_hash(item)) &&
+            (strncasecmp(col_get_item_property(item, &len), name, name_len) == 0) &&
+            (len == name_len)) {
+                TRACE_INFO_STRING("Item is found", name);
+                break;
+        }
+    }
+    while(1);
+
+    if (!is_same_name(ini_config, name, name_len)) {
+        /* Save name */
+        ini_config->name_len = name_len;
+        ini_config->name = strndup(name, name_len);
+        /* Check error */
+        if (ini_config->name == NULL) {
+            TRACE_ERROR_NUMBER("Failed to save key name ", ENOMEM);
+            ini_config_clean_state(ini_config);
+            return ENOMEM;
+        }
+    }
+
+    *vo = *((struct value_obj **)(col_get_item_data(item)));
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Get long long value from config value object */
+static long long ini_get_llong_config_value(struct value_obj *vo,
+                                            int strict,
+                                            long long def,
+                                            int *error)
+{
+    int err;
+    const char *str;
+    char *endptr;
+    long long val = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return def;
+    }
+
+    if (error) *error = EOK;
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reson the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+
+    /* Try to parse the value */
+    errno = 0;
+    val = strtoll(str, &endptr, 10);
+    err = errno;
+
+    /* Check for various possible errors */
+    if (err != 0) {
+        TRACE_ERROR_NUMBER("Conversion failed", err);
+        if (error) *error = err;
+        return def;
+    }
+
+    /* Other error cases */
+    if ((endptr == str) || (strict && (*endptr != '\0'))) {
+        TRACE_ERROR_NUMBER("More characters or nothing processed", EIO);
+        if (error) *error = EIO;
+        return def;
+    }
+
+    TRACE_FLOW_NUMBER("ini_get_llong_config_value returning", (long)val);
+    return val;
+}
+
+/* Get unsigned long long value from config value object */
+static unsigned long long ini_get_ullong_config_value(struct value_obj *vo,
+                                                      int strict,
+                                                      unsigned long long def,
+                                                      int *error)
+{
+    int err;
+    const char *str;
+    char *endptr;
+    unsigned long long val = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return def;
+    }
+
+    if (error) *error = EOK;
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reson the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+
+    errno = 0;
+    val = strtoull(str, &endptr, 10);
+    err = errno;
+
+    /* Check for various possible errors */
+    if (err != 0) {
+        TRACE_ERROR_NUMBER("Conversion failed", err);
+        if (error) *error = err;
+        return def;
+    }
+
+    /* Other error cases */
+    if ((endptr == str) || (strict && (*endptr != '\0'))) {
+        TRACE_ERROR_NUMBER("More characters or nothing processed", EIO);
+        if (error) *error = EIO;
+        return def;
+    }
+
+    TRACE_FLOW_NUMBER("ini_get_ullong_config_value returning", val);
+    return val;
+}
+
+
+/* Get integer value from config value */
+int ini_get_int_config_value(struct value_obj *vo,
+                             int strict,
+                             int def,
+                             int *error)
+{
+    long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_llong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if ((val > INT_MAX) || (val < INT_MIN)) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_int_config_value returning", (int)val);
+    return (int)val;
+}
+
+/* Get unsigned integer value from config value object */
+unsigned ini_get_unsigned_config_value(struct value_obj *vo,
+                                       int strict,
+                                       unsigned def,
+                                       int *error)
+{
+    unsigned long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_ullong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if (val > UINT_MAX) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_unsigned_config_value returning",
+                      (unsigned)val);
+    return (unsigned)val;
+}
+
+/* Get long value from config value object */
+long ini_get_long_config_value(struct value_obj *vo,
+                               int strict,
+                               long def,
+                               int *error)
+{
+    long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_llong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if ((val > LONG_MAX) || (val < LONG_MIN)) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_long_config_value returning",
+                      (long)val);
+    return (long)val;
+}
+
+/* Get unsigned long value from config value object */
+unsigned long ini_get_ulong_config_value(struct value_obj *vo,
+                                         int strict,
+                                         unsigned long def,
+                                         int *error)
+{
+    unsigned long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_ullong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if (val > ULONG_MAX) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_ulong_config_value returning",
+                      (unsigned long)val);
+    return (unsigned long)val;
+}
+
+/* Get int32_t value from config value object */
+int32_t ini_get_int32_config_value(struct value_obj *vo,
+                                   int strict,
+                                   int32_t def,
+                                   int *error)
+{
+    long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_llong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if ((val > INT32_MAX) || (val < INT32_MIN)) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_int32_config_value returning",
+                      (int32_t)val);
+    return (int32_t)val;
+}
+
+/* Get uint32_t value from config value object */
+uint32_t ini_get_uint32_config_value(struct value_obj *vo,
+                                     int strict,
+                                     uint32_t def,
+                                     int *error)
+{
+    unsigned long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_ullong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if (val > UINT32_MAX) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_uint32_config_value returning",
+                      (uint32_t)val);
+    return (uint32_t)val;
+}
+
+/* Get int64_t value from config value ovject */
+int64_t ini_get_int64_config_value(struct value_obj *vo,
+                                   int strict,
+                                   int64_t def,
+                                   int *error)
+{
+    long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_llong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if ((val > INT64_MAX) || (val < INT64_MIN)) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_int64_config_value returning",
+                      (int64_t)val);
+    return (int64_t)val;
+}
+
+/* Get uint64_t value from config value object */
+uint64_t ini_get_uint64_config_value(struct value_obj *vo,
+                                     int strict,
+                                     uint64_t def,
+                                     int *error)
+{
+    unsigned long long val = 0;
+    int err = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    val = ini_get_ullong_config_value(vo, strict, def, &err);
+    if (err == 0) {
+        if (val > UINT64_MAX) {
+            TRACE_ERROR_NUMBER("Value is out of range", ERANGE);
+            val = def;
+            err = ERANGE;
+        }
+    }
+
+    if (error) *error = err;
+
+    TRACE_FLOW_NUMBER("ini_get_uint64_config_value returning",
+                      (uint64_t)val);
+    return (uint64_t)val;
+}
+
+/* Get double value */
+double ini_get_double_config_value(struct value_obj *vo,
+                                   int strict, double def, int *error)
+{
+    const char *str;
+    char *endptr;
+    double val = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return def;
+    }
+
+    if (error) *error = EOK;
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reason the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+
+    errno = 0;
+    val = strtod(str, &endptr);
+
+    /* Check for various possible errors */
+    if ((errno == ERANGE) ||
+        ((errno != 0) && (val == 0)) ||
+        (endptr == str)) {
+        TRACE_ERROR_NUMBER("Conversion failed", EIO);
+        if (error) *error = EIO;
+        return def;
+    }
+
+    if (strict && (*endptr != '\0')) {
+        TRACE_ERROR_NUMBER("More characters than expected", EIO);
+        if (error) *error = EIO;
+        val = def;
+    }
+
+    TRACE_FLOW_DOUBLE("ini_get_double_config_value returning", val);
+    return val;
+}
+
+/* Get boolean value */
+unsigned char ini_get_bool_config_value(struct value_obj *vo,
+                                        unsigned char def, int *error)
+{
+    const char *str;
+    uint32_t len = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return def;
+    }
+
+    if (error) *error = EOK;
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reson the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+    value_get_concatenated_len(vo, &len);
+
+    /* Try to parse the value */
+    if ((strncasecmp(str, "true", len) == 0) ||
+        (strncasecmp(str, "yes", len) == 0)) {
+        TRACE_FLOW_STRING("Returning", "true");
+        return '\1';
+    }
+    else if ((strncasecmp(str, "false", len) == 0) ||
+             (strncasecmp(str, "no", len) == 0)) {
+        TRACE_FLOW_STRING("Returning", "false");
+        return '\0';
+    }
+
+    TRACE_ERROR_STRING("Returning", "error");
+    if (error) *error = EIO;
+    return def;
+}
+
+/* Return a string out of the value */
+char *ini_get_string_config_value(struct value_obj *vo,
+                              int *error)
+{
+    const char *str = NULL;
+    char *ret_str = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reson the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+
+    ret_str = strdup(str);
+    if (ret_str == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if (error) *error = ENOMEM;
+        return NULL;
+    }
+
+    if (error) *error = EOK;
+
+    TRACE_FLOW_STRING("ini_get_string_config_value returning", str);
+    return ret_str;
+}
+
+/* Get string from the value object */
+const char *ini_get_const_string_config_value(struct value_obj *vo,
+                                              int *error)
+{
+    const char *str;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reson the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+
+    if (error) *error = EOK;
+
+    TRACE_FLOW_STRING("ini_get_const_string_config_value returning", str);
+    return str;
+}
+
+/* A special hex format is assumed.
+ * The string should be taken in single quotes
+ * and consist of hex encoded value two hex digits per byte.
+ * Example: '0A2BFECC'
+ * Case does not matter.
+ */
+char *ini_get_bin_config_value(struct value_obj *vo,
+                               int *length, int *error)
+{
+    int i;
+    char *value = NULL;
+    const char *buff;
+    int size = 0;
+    uint32_t len = 0;
+    const char *str;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if (vo == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    if (error) *error = EOK;
+
+    /* Get value - no error checking as we checked it above
+     * and there is no other reson the function could fail.
+     */
+    value_get_concatenated(vo, &str);
+
+    /* Check the length */
+    value_get_concatenated_len(vo, &len);
+    if ((len%2) != 0) {
+        TRACE_ERROR_STRING("Invalid length for binary data", "");
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Is the format correct ? */
+    if ((*str != '\'') ||
+        (str[len -1] != '\'')) {
+        TRACE_ERROR_STRING("String is not escaped","");
+        if (error) *error = EIO;
+        return NULL;
+    }
+
+    /* Check that all the symbols are ok */
+    buff = str + 1;
+    len -= 2;
+    for (i = 0; i < len; i++) {
+        if (!isxdigit(buff[i])) {
+            TRACE_ERROR_STRING("Invalid encoding for binary data", buff[i]);
+            if (error) *error = EIO;
+            return NULL;
+        }
+    }
+
+    /* The value is good so we can allocate memory for it */
+    value = malloc(len / 2);
+    if (value == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        if (error) *error = ENOMEM;
+        return NULL;
+    }
+
+    /* Convert the value */
+    for (i = 0; i < len; i += 2) {
+        value[size] = (char)(16 * HEXVAL(buff[i]) + HEXVAL(buff[i+1]));
+        size++;
+    }
+
+    if (error) *error = EOK;
+    if (length) *length = size;
+    TRACE_FLOW_STRING("ini_get_bin_config_value", "Exit");
+    return value;
+}
+
+/* Function to free binary configuration value */
+void ini_free_bin_config_value(char *value)
+{
+    free(value);
+}
diff --git a/ini/ini_list_valueobj.c b/ini/ini_list_valueobj.c
new file mode 100644
index 0000000..8df6a18
--- /dev/null
+++ b/ini/ini_list_valueobj.c
@@ -0,0 +1,136 @@
+/*
+    INI LIBRARY
+
+    Value interpretation functions for single values
+    and corresponding memory cleanup functions.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2012
+
+    INI 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 3 of the License, or
+    (at your option) any later version.
+
+    INI 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 INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include "config.h"
+#include "trace.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "ini_configobj.h"
+#include "ini_config_priv.h"
+
+
+/* The section array should be freed using this function */
+void ini_free_section_list(char **section_list)
+{
+    TRACE_FLOW_ENTRY();
+
+    col_free_property_list(section_list);
+
+    TRACE_FLOW_EXIT();
+}
+
+/* The section array should be freed using this function */
+void ini_free_attribute_list(char **section_list)
+{
+    TRACE_FLOW_ENTRY();
+
+    col_free_property_list(section_list);
+
+    TRACE_FLOW_EXIT();
+}
+
+
+/* Get list of sections as an array of strings.
+ * Function allocates memory for the array of the sections.
+ */
+char **ini_get_section_list(struct ini_cfgobj *ini_config, int *size, int *error)
+{
+    char **list;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the configuration object ? */
+    if (ini_config == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Pass it to the function from collection API */
+    list = col_collection_to_list(ini_config->cfg, size, error);
+
+    TRACE_FLOW_STRING("ini_get_section_list returning",
+                      ((list == NULL) ? "NULL" : list[0]));
+    return list;
+}
+
+/* Get list of attributes in a section as an array of strings.
+ * Function allocates memory for the array of the strings.
+ */
+char **ini_get_attribute_list(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              int *size,
+                              int *error)
+{
+    struct collection_item *subcollection = NULL;
+    char **list;
+    int err;
+    int i = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the configuration object ? */
+    if (ini_config == NULL) {
+        TRACE_ERROR_NUMBER("Invalid configuration object argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Do we have the section ? */
+    if (section == NULL) {
+        TRACE_ERROR_NUMBER("Invalid section argument.", EINVAL);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Fetch section */
+    err = col_get_collection_reference(ini_config->cfg, &subcollection, section);
+    /* Check error */
+    if (err && (subcollection == NULL)) {
+        TRACE_ERROR_NUMBER("Failed to get section", err);
+        if (error) *error = EINVAL;
+        return NULL;
+    }
+
+    /* Pass it to the function from collection API */
+    list = col_collection_to_list(subcollection, size, error);
+
+    col_destroy_collection(subcollection);
+
+    /* Our list of attributes has a special extra attribute - remove it */
+    if ((list != NULL) && (list[0] != NULL)) {
+        free(list[0]);
+        while(list[i + 1] != NULL) {
+            list[i] = list[i + 1];
+            i++;
+        }
+        list[i] = NULL;
+    }
+
+    if (size) (*size)--;
+
+    TRACE_FLOW_STRING("ini_get_attribute_list returning", ((list == NULL) ? "NULL" : list[0]));
+    return list;
+}
-- 
1.7.1

