summaryrefslogtreecommitdiffstats
path: root/ini
diff options
context:
space:
mode:
authorDmitri Pal <dpal@redhat.com>2010-04-15 17:27:04 -0400
committerStephen Gallagher <sgallagh@redhat.com>2010-04-26 09:54:59 -0400
commite06ef2acf0df9ffa854b0359750bb4800a702bf2 (patch)
tree85823b8c0fb7686a5502e9dbb6b2a6d1ee64a973 /ini
parente26fa48aa42eebd2acc22a3731b17a53911dcd4b (diff)
downloadding-libs-e06ef2acf0df9ffa854b0359750bb4800a702bf2.tar.gz
ding-libs-e06ef2acf0df9ffa854b0359750bb4800a702bf2.tar.xz
ding-libs-e06ef2acf0df9ffa854b0359750bb4800a702bf2.zip
Code restructuring
Time came to split ini_config.c into many much smaller pieces. 1) ini_parse.c - will have parsing functions 2) ini_get_value.c - will have single value interpretation functions 3) ini_get_array.c - will have array interpretation functions. 4) ini_print.c - error printing 5) ini_defines.h - common constants 6) ini_parse.h header for parsing functions 7) ini_list.c - will have list processing functions
Diffstat (limited to 'ini')
-rw-r--r--ini/Makefile.am11
-rw-r--r--ini/ini_config.c1476
-rw-r--r--ini/ini_config.h3
-rw-r--r--ini/ini_defines.h74
-rw-r--r--ini/ini_get_array.c354
-rw-r--r--ini/ini_get_value.c523
-rw-r--r--ini/ini_list.c123
-rw-r--r--ini/ini_parse.c190
-rw-r--r--ini/ini_parse.h36
-rw-r--r--ini/ini_print.c387
10 files changed, 1705 insertions, 1472 deletions
diff --git a/ini/Makefile.am b/ini/Makefile.am
index a72ce3a..1dc55d9 100644
--- a/ini/Makefile.am
+++ b/ini/Makefile.am
@@ -34,8 +34,17 @@ dist_include_HEADERS = \
lib_LTLIBRARIES = libini_config.la
libini_config_la_SOURCES = \
ini_config.c \
+ ini_config.h \
+ ini_get_value.c \
+ ini_get_array.c \
+ ini_list.c \
+ ini_print.c \
+ ini_parse.c \
+ ini_parse.h \
ini_metadata.c \
- ini_metadata.h
+ ini_metadata.h \
+ ini_defines.h
+
libini_config_la_LIBADD = \
-L$(topbuilddir)/collection \
diff --git a/ini/ini_config.c b/ini/ini_config.c
index 1c8e4ae..b9cd6eb 100644
--- a/ini/ini_config.c
+++ b/ini/ini_config.c
@@ -21,220 +21,19 @@
*/
#define _GNU_SOURCE
-#include <stdio.h>
#include <errno.h>
#include <string.h>
-#include <ctype.h>
#include <stdlib.h>
-#include <locale.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
#include "config.h"
-/* For error text */
-#include <libintl.h>
-#define _(String) gettext (String)
-/* INI file is used as a collection */
+#include "trace.h"
#include "collection.h"
#include "collection_tools.h"
-#include "trace.h"
-#include "ini_config.h"
-#include "ini_metadata.h"
#include "path_utils.h"
+#include "ini_defines.h"
+#include "ini_parse.h"
+#include "ini_metadata.h"
+#include "ini_config.h"
-#define NAME_OVERHEAD 10
-
-#define SLASH "/"
-
-#define EXCLUDE_EMPTY 0
-#define INCLUDE_EMPTY 1
-
-/* Name of the special collection used to store parsing errors */
-#define FILE_ERROR_SET "ini_file_error_set"
-
-/* Text error strings used when errors are printed out */
-#define WARNING_TXT _("Warning")
-#define ERROR_TXT _("Error")
-/* For parse errors */
-#define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n")
-#define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n")
-#define ERROR_HEADER _("Parsing errors and warnings in file: %s\n")
-/* For grammar errors */
-#define WRONG_GRAMMAR _("Passed in list is not a list of grammar errors.\n")
-#define FAILED_TO_PROC_G _("Internal Error. Failed to process list of grammar errors.\n")
-#define ERROR_HEADER_G _("Logical errors and warnings in file: %s\n")
-/* For validation errors */
-#define WRONG_VALIDATION _("Passed in list is not a list of validation errors.\n")
-#define FAILED_TO_PROC_V _("Internal Error. Failed to process list of validation errors.\n")
-#define ERROR_HEADER_V _("Validation errors and warnings in file: %s\n")
-
-#define LINE_FORMAT _("%s (%d) on line %d: %s\n")
-
-
-/* Codes that parsing function can return */
-#define RET_PAIR 0
-#define RET_COMMENT 1
-#define RET_SECTION 2
-#define RET_INVALID 3
-#define RET_EMPTY 4
-#define RET_EOF 5
-#define RET_ERROR 6
-
-#define INI_ERROR "errors"
-#define INI_ERROR_NAME "errname"
-
-/* Internal sizes. MAX_KEY is defined in config.h */
-#define MAX_VALUE PATH_MAX
-#define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3
-
-/* Beffer length used for int to string conversions */
-#define CONVERSION_BUFFER 80
-
-/*============================================================*/
-/* The following classes moved here from the public header
- * They are reserved for future use.
- *
- * NOTE: before exposing these constants again in the common header
- * check that the class IDs did not get reused over time by
- * other classes.
- */
-/** @brief Collection of grammar errors.
- *
- * Reserved for future use.
- */
-#define COL_CLASS_INI_GERROR COL_CLASS_INI_BASE + 5
-/** @brief Collection of validation errors.
- *
- * Reserved for future use.
- */
-#define COL_CLASS_INI_VERROR COL_CLASS_INI_BASE + 6
-
-#ifdef HAVE_VALIDATION
-
-/** @brief Collection of lines from the INI file.
- *
- * Reserved for future use
- */
-#define COL_CLASS_INI_LINES COL_CLASS_INI_BASE + 7
-
-#endif /* HAVE_VALIDATION */
-/*============================================================*/
-
-
-/* Different error string functions can be passed as callbacks */
-typedef const char * (*error_fn)(int error);
-
-/* Function to return parsing error */
-const char *parsing_error_str(int parsing_error)
-{
- const char *placeholder= _("Unknown pasing error.");
- const char *str_error[] = { _("Data is too long."),
- _("No closing bracket."),
- _("Section name is missing."),
- _("Section name is too long."),
- _("Equal sign is missing."),
- _("Property name is missing."),
- _("Property name is too long.")
- };
-
- /* Check the range */
- if ((parsing_error < 1) || (parsing_error > ERR_MAXPARSE))
- return placeholder;
- else
- return str_error[parsing_error-1];
-}
-
-/* Function to return grammar error.
- * This function is currently not used.
- * It is planned to be used by the INI
- * file grammar parser.
- *
- * The following doxygen description is moved here.
- * When the function gets exposed move it into
- * the header file.
- */
-/** @brief Function to return a grammar error in template.
- *
- * EXPERIMENTAL. Reserved for future use.
- *
- * This error is returned when the template
- * is translated into the grammar object.
- *
- * @param[in] parsing_error Error code for the grammar error.
- *
- * @return Error string.
- */
-
-const char *grammar_error_str(int grammar_error)
-{
- const char *placeholder= _("Unknown grammar error.");
- /* THIS IS A TEMPORARY PLACEHOLDER !!!! */
- const char *str_error[] = { _(""),
- _(""),
- _(""),
- _(""),
- _(""),
- _(""),
- _("")
- };
-
- /* Check the range */
- if ((grammar_error < 1) || (grammar_error > ERR_MAXGRAMMAR))
- return placeholder;
- else
- return str_error[grammar_error-1];
-}
-
-/* Function to return validation error.
- * This function is currently not used.
- * It is planned to be used by the INI
- * file grammar validator.
- *
- * The following doxygen description is moved here.
- * When the function gets exposed move it into
- * the header file.
- */
-/** @brief Function to return a validation error.
- *
- * EXPERIMENTAL. Reserved for future use.
- *
- * This is the error that it is returned when
- * the INI file is validated against the
- * grammar object.
- *
- * @param[in] parsing_error Error code for the validation error.
- *
- * @return Error string.
- */
-const char *validation_error_str(int validation_error)
-{
- const char *placeholder= _("Unknown validation error.");
- /* THIS IS A TEMPORARY PLACEHOLDER !!!! */
- const char *str_error[] = { _(""),
- _(""),
- _(""),
- _(""),
- _(""),
- _(""),
- _("")
- };
-
- /* Check the range */
- if ((validation_error < 1) || (validation_error > ERR_MAXVALID))
- return placeholder;
- else
- return str_error[validation_error-1];
-}
-
-
-/* Internal function to read line from INI file */
-int read_line(FILE *file,
- char *buf,
- int read_size,
- char **key,
- char **value,
- int *length,
- int *ext_error);
/***************************************************************************/
/* Function to read single ini file and pupulate
@@ -1035,1268 +834,3 @@ int config_for_app(const char *application,
TRACE_FLOW_NUMBER("config_for_app. Returning", error);
return error;
}
-
-
-
-/* Reads a line from the file */
-int read_line(FILE *file,
- char *buf,
- int read_size,
- char **key, char **value,
- int *length,
- int *ext_error)
-{
-
- char *res;
- int len;
- char *buffer;
- int i;
- char *eq;
-
- TRACE_FLOW_STRING("read_line", "Entry");
-
- *ext_error = 0;
-
- buffer = buf;
-
- /* Get data from file */
- res = fgets(buffer, read_size - 1, file);
- if (res == NULL) {
- TRACE_ERROR_STRING("Read nothing", "");
- return RET_EOF;
- }
-
- /* Make sure the buffer is NULL terminated */
- buffer[read_size - 1] = '\0';
-
- len = strlen(buffer);
- if (len == 0) {
- TRACE_ERROR_STRING("Nothing was read.", "");
- return RET_EMPTY;
- }
-
- /* Added \r just in case we deal with Windows in future */
- if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) {
- TRACE_ERROR_STRING("String it too big!", "");
- *ext_error = ERR_LONGDATA;
- return RET_ERROR;
- }
-
- /* Ingnore comments */
- if ((*buffer == ';') || (*buffer == '#')) {
- TRACE_FLOW_STRING("Comment", buf);
- return RET_COMMENT;
- }
-
- TRACE_INFO_STRING("BUFFER before trimming:", buffer);
-
- /* Trucate trailing spaces and CRs */
- /* Make sure not to step before the beginning */
- while (len && isspace(buffer[len - 1])) {
- buffer[len - 1] = '\0';
- len--;
- }
-
- TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer);
-
- /* Trucate leading spaces */
- while (isspace(*buffer)) {
- buffer++;
- len--;
- }
-
- TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer);
- TRACE_INFO_NUMBER("BUFFER length:", len);
-
- /* Empty line */
- if (len == 0) {
- TRACE_FLOW_STRING("Empty line", buf);
- return RET_EMPTY;
- }
-
- /* Section */
- if (*buffer == '[') {
- if (buffer[len-1] != ']') {
- TRACE_ERROR_STRING("Invalid format for section", buf);
- *ext_error = ERR_NOCLOSESEC;
- return RET_ERROR;
- }
- buffer++;
- len--;
- while (isspace(*buffer)) {
- buffer++;
- len--;
- }
- if (len == 0) {
- TRACE_ERROR_STRING("Invalid format for section", buf);
- *ext_error = ERR_NOSECTION;
- return RET_ERROR;
- }
-
- buffer[len - 1] = '\0';
- len--;
- while (isspace(buffer[len - 1])) {
- buffer[len - 1] = '\0';
- len--;
- }
- if (len >= MAX_KEY) {
- TRACE_ERROR_STRING("Section name is too long", buf);
- *ext_error = ERR_SECTIONLONG;
- return RET_ERROR;
- }
-
- *key = buffer;
- return RET_SECTION;
- }
-
- /* Assume we are dealing with the K-V here */
- /* Find "=" */
- eq = strchr(buffer, '=');
- if (eq == NULL) {
- TRACE_ERROR_STRING("No equal sign", buf);
- *ext_error = ERR_NOEQUAL;
- return RET_INVALID;
- }
-
- len -= eq-buffer;
-
- /* Strip spaces around "=" */
- i = eq - buffer - 1;
- while ((i >= 0) && isspace(buffer[i])) i--;
- if (i < 0) {
- TRACE_ERROR_STRING("No key", buf);
- *ext_error = ERR_NOKEY;
- return RET_INVALID;
- }
-
- /* Copy key into provided buffer */
- if(i >= MAX_KEY) {
- TRACE_ERROR_STRING("Section name is too long", buf);
- *ext_error = ERR_LONGKEY;
- return RET_INVALID;
- }
- *key = buffer;
- buffer[i + 1] = '\0';
- TRACE_INFO_STRING("KEY:", *key);
-
- eq++;
- len--;
- while (isspace(*eq)) {
- eq++;
- len--;
- }
-
- *value = eq;
- /* Make sure we include trailing 0 into data */
- *length = len + 1;
-
- TRACE_INFO_STRING("VALUE:", *value);
- TRACE_INFO_NUMBER("LENGTH:", *length);
-
- TRACE_FLOW_STRING("read_line", "Exit");
- return RET_PAIR;
-}
-
-
-
-/* Internal function that prints errors */
-static void print_error_list(FILE *file,
- struct collection_item *error_list,
- int cclass,
- char *wrong_col_error,
- char *failed_to_process,
- char *error_header,
- char *line_format,
- error_fn error_function)
-{
- struct collection_iterator *iterator;
- int error;
- struct collection_item *item = NULL;
- struct parse_error *pe;
- unsigned int count;
-
- TRACE_FLOW_STRING("print_error_list", "Entry");
-
- /* If we have something to print print it */
- if (error_list == NULL) {
- TRACE_ERROR_STRING("No error list","");
- return;
- }
-
- /* Make sure we go the right collection */
- if (!col_is_of_class(error_list, cclass)) {
- TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error);
- fprintf(file,"%s\n", wrong_col_error);
- return;
- }
-
- /* Bind iterator */
- error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT);
- if (error) {
- TRACE_ERROR_STRING("Error (bind):", failed_to_process);
- fprintf(file, "%s\n", failed_to_process);
- return;
- }
-
- while(1) {
- /* Loop through a collection */
- error = col_iterate_collection(iterator, &item);
- if (error) {
- TRACE_ERROR_STRING("Error (iterate):", failed_to_process);
- fprintf(file, "%s\n", failed_to_process);
- col_unbind_iterator(iterator);
- return;
- }
-
- /* Are we done ? */
- if (item == NULL) break;
-
- /* Process collection header */
- if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
- col_get_collection_count(item, &count);
- if (count <= 2) break;
- } else if (col_get_item_type(item) == COL_TYPE_STRING) {
- fprintf(file, error_header, (char *)col_get_item_data(item));
- }
- else {
- /* Put error into provided format */
- pe = (struct parse_error *)(col_get_item_data(item));
- fprintf(file, line_format,
- col_get_item_property(item, NULL), /* Error or warning */
- pe->error, /* Error */
- pe->line, /* Line */
- error_function(pe->error)); /* Error str */
- }
-
- }
-
- /* Do not forget to unbind iterator - otherwise there will be a leak */
- col_unbind_iterator(iterator);
-
- TRACE_FLOW_STRING("print_error_list", "Exit");
-}
-
-/* Print errors and warnings that were detected while parsing one file */
-void print_file_parsing_errors(FILE *file,
- struct collection_item *error_list)
-{
- print_error_list(file,
- error_list,
- COL_CLASS_INI_PERROR,
- WRONG_COLLECTION,
- FAILED_TO_PROCCESS,
- ERROR_HEADER,
- LINE_FORMAT,
- parsing_error_str);
-}
-
-
-/* Print errors and warnings that were detected while processing grammar.
- *
- * The following doxygen description is moved here.
- * When the function gets exposed move it into
- * the header file.
- */
-/**
- * @brief Print errors and warnings that were detected while
- * checking grammar of the template.
- *
- * EXPERIMENTAL. Reserved for future use.
- *
- * @param[in] file File descriptor.
- * @param[in] error_list List of the parsing errors.
- *
- */
-void print_grammar_errors(FILE *file,
- struct collection_item *error_list)
-{
- print_error_list(file,
- error_list,
- COL_CLASS_INI_GERROR,
- WRONG_GRAMMAR,
- FAILED_TO_PROC_G,
- ERROR_HEADER_G,
- LINE_FORMAT,
- grammar_error_str);
-}
-
-/* Print errors and warnings that were detected while validating INI file.
- *
- * The following doxygen description is moved here.
- * When the function gets exposed move it into
- * the header file.
- */
-/**
- * @brief Print errors and warnings that were detected while
- * checking INI file against the grammar object.
- *
- * EXPERIMENTAL. Reserved for future use.
- *
- * @param[in] file File descriptor.
- * @param[in] error_list List of the parsing errors.
- *
- */
-void print_validation_errors(FILE *file,
- struct collection_item *error_list)
-{
- print_error_list(file,
- error_list,
- COL_CLASS_INI_VERROR,
- WRONG_VALIDATION,
- FAILED_TO_PROC_V,
- ERROR_HEADER_V,
- LINE_FORMAT,
- validation_error_str);
-}
-
-/* Print errors and warnings that were detected while parsing
- * the whole configuration */
-void print_config_parsing_errors(FILE *file,
- struct collection_item *error_list)
-{
- struct collection_iterator *iterator;
- int error;
- struct collection_item *item = NULL;
- struct collection_item *file_errors = NULL;
-
- TRACE_FLOW_STRING("print_config_parsing_errors", "Entry");
-
- /* If we have something to print print it */
- if (error_list == NULL) {
- TRACE_ERROR_STRING("No error list", "");
- return;
- }
-
- /* Make sure we go the right collection */
- if (!col_is_of_class(error_list, COL_CLASS_INI_PESET)) {
- TRACE_ERROR_STRING("Wrong collection class:", WRONG_COLLECTION);
- fprintf(file, "%s\n", WRONG_COLLECTION);
- return;
- }
-
- /* Bind iterator */
- error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT);
- if (error) {
- TRACE_ERROR_STRING("Error (bind):", FAILED_TO_PROCCESS);
- fprintf(file,"%s\n", FAILED_TO_PROCCESS);
- return;
- }
-
- while(1) {
- /* Loop through a collection */
- error = col_iterate_collection(iterator, &item);
- if (error) {
- TRACE_ERROR_STRING("Error (iterate):", FAILED_TO_PROCCESS);
- fprintf(file, "%s\n", FAILED_TO_PROCCESS);
- col_unbind_iterator(iterator);
- return;
- }
-
- /* Are we done ? */
- if (item == NULL) break;
-
- /* Print per file sets of errors */
- if (col_get_item_type(item) == COL_TYPE_COLLECTIONREF) {
- /* Extract a sub collection */
- error = col_get_reference_from_item(item, &file_errors);
- if (error) {
- TRACE_ERROR_STRING("Error (extract):", FAILED_TO_PROCCESS);
- fprintf(file, "%s\n", FAILED_TO_PROCCESS);
- col_unbind_iterator(iterator);
- return;
- }
- print_file_parsing_errors(file, file_errors);
- col_destroy_collection(file_errors);
- }
- }
-
- /* Do not forget to unbind iterator - otherwise there will be a leak */
- col_unbind_iterator(iterator);
-
- TRACE_FLOW_STRING("print_config_parsing_errors", "Exit");
-}
-
-
-/* Function to get value from the configration handle */
-int get_config_item(const char *section,
- const char *name,
- struct collection_item *ini_config,
- struct collection_item **item)
-{
- int error = EOK;
- struct collection_item *section_handle = NULL;
- const char *to_find;
- char default_section[] = INI_DEFAULT_SECTION;
-
- TRACE_FLOW_STRING("get_config_item", "Entry");
-
- /* Do we have the accepting memory ? */
- if (item == NULL) {
- TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL);
- return EINVAL;
- }
-
- /* Is the collection of a right type */
- if ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) &&
- (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) {
- TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
- return EINVAL;
- }
-
- *item = NULL;
-
- if (section == NULL) to_find = default_section;
- else to_find = section;
-
- TRACE_INFO_STRING("Getting Name:", name);
- TRACE_INFO_STRING("In Section:", section);
-
- /* Get Subcollection */
- error = col_get_collection_reference(ini_config, &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_STRING("get_value_from_config", "No such section");
- return EOK;
- }
-
- /* Get item */
- error = col_get_item(section_handle, name,
- COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item);
-
- /* Make sure we free the section we found */
- col_destroy_collection(section_handle);
-
- TRACE_FLOW_NUMBER("get_config_item returning", error);
- return error;
-}
-
-/* Get long long value from config item */
-static long long get_llong_config_value(struct collection_item *item,
- int strict,
- long long def,
- int *error)
-{
- int err;
- const char *str;
- char *endptr;
- long long val = 0;
-
- TRACE_FLOW_STRING("get_long_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return def;
- }
-
- if (error) *error = EOK;
-
- /* Try to parse the value */
- str = (const char *)col_get_item_data(item);
- 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("get_long_config_value returning", (long)val);
- return val;
-}
-
-/* Get unsigned long long value from config item */
-static unsigned long long get_ullong_config_value(struct collection_item *item,
- int strict,
- unsigned long long def,
- int *error)
-{
- int err;
- const char *str;
- char *endptr;
- unsigned long long val = 0;
-
- TRACE_FLOW_STRING("get_ullong_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return def;
- }
-
- if (error) *error = EOK;
-
- /* Try to parse the value */
- str = (const char *)col_get_item_data(item);
- 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("get_ullong_config_value returning", (long)val);
- return val;
-}
-
-
-/* Get integer value from config item */
-int get_int_config_value(struct collection_item *item,
- int strict,
- int def,
- int *error)
-{
- long long val = 0;
- int err = 0;
-
- TRACE_FLOW_STRING("get_int_config_value", "Entry");
-
- val = get_llong_config_value(item, strict, def, &err);
- if (err == 0) {
- if ((val > INT_MAX) || (val < INT_MIN)) {
- val = def;
- err = ERANGE;
- }
- }
-
- if (error) *error = err;
-
- TRACE_FLOW_NUMBER("get_int_config_value returning", (int)val);
- return (int)val;
-}
-
-/* Get unsigned integer value from config item */
-unsigned get_unsigned_config_value(struct collection_item *item,
- int strict,
- unsigned def,
- int *error)
-{
- unsigned long long val = 0;
- int err = 0;
-
- TRACE_FLOW_STRING("get_unsigned_config_value", "Entry");
-
- val = get_ullong_config_value(item, strict, def, &err);
- if (err == 0) {
- if (val > UINT_MAX) {
- val = def;
- err = ERANGE;
- }
- }
-
- if (error) *error = err;
-
- TRACE_FLOW_NUMBER("get_unsigned_config_value returning",
- (unsigned)val);
- return (unsigned)val;
-}
-
-/* Get long value from config item */
-long get_long_config_value(struct collection_item *item,
- int strict,
- long def,
- int *error)
-{
- long long val = 0;
- int err = 0;
-
- TRACE_FLOW_STRING("get_long_config_value", "Entry");
-
- val = get_llong_config_value(item, strict, def, &err);
- if (err == 0) {
- if ((val > LONG_MAX) || (val < LONG_MIN)) {
- val = def;
- err = ERANGE;
- }
- }
-
- if (error) *error = err;
-
- TRACE_FLOW_NUMBER("get_long_config_value returning",
- (long)val);
- return (long)val;
-}
-
-/* Get unsigned long value from config item */
-unsigned long get_ulong_config_value(struct collection_item *item,
- int strict,
- unsigned long def,
- int *error)
-{
- unsigned long long val = 0;
- int err = 0;
-
- TRACE_FLOW_STRING("get_ulong_config_value", "Entry");
-
- val = get_ullong_config_value(item, strict, def, &err);
- if (err == 0) {
- if (val > ULONG_MAX) {
- val = def;
- err = ERANGE;
- }
- }
-
- if (error) *error = err;
-
- TRACE_FLOW_NUMBER("get_ulong_config_value returning",
- (unsigned long)val);
- return (unsigned long)val;
-}
-
-
-/* Get double value */
-double get_double_config_value(struct collection_item *item,
- int strict, double def, int *error)
-{
- const char *str;
- char *endptr;
- double val = 0;
-
- TRACE_FLOW_STRING("get_double_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return def;
- }
-
- if (error) *error = EOK;
-
- /* Try to parse the value */
- str = (const char *)col_get_item_data(item);
- 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_NUMBER("get_double_config_value returning", val);
- return val;
-}
-
-/* Get boolean value */
-unsigned char get_bool_config_value(struct collection_item *item,
- unsigned char def, int *error)
-{
- const char *str;
- int len;
-
- TRACE_FLOW_STRING("get_bool_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return def;
- }
-
- if (error) *error = EOK;
-
- str = (const char *)col_get_item_data(item);
- len = col_get_item_length(item);
-
- /* 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 *get_string_config_value(struct collection_item *item,
- int *error)
-{
- char *str = NULL;
-
- TRACE_FLOW_STRING("get_string_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- str = strdup((const char *)col_get_item_data(item));
- if (str == NULL) {
- TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
- if (error) *error = ENOMEM;
- return NULL;
- }
-
- if (error) *error = EOK;
-
- TRACE_FLOW_STRING("get_string_config_value returning", str);
- return str;
-}
-
-/* Get string from item */
-const char *get_const_string_config_value(struct collection_item *item, int *error)
-{
- const char *str;
-
- TRACE_FLOW_STRING("get_const_string_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- str = (const char *)col_get_item_data(item);
-
- if (error) *error = EOK;
-
- TRACE_FLOW_STRING("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 *get_bin_config_value(struct collection_item *item,
- int *length, int *error)
-{
- int i;
- char *value = NULL;
- const char *buff;
- int size = 0;
- unsigned char hex;
- int len;
- const char *str;
-
- TRACE_FLOW_STRING("get_bin_config_value", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- /* Check the length */
- len = col_get_item_length(item)-1;
- if ((len%2) != 0) {
- TRACE_ERROR_STRING("Invalid length for binary data", "");
- if (error) *error = EINVAL;
- return NULL;
- }
-
- str = (const char *)col_get_item_data(item);
-
- /* 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 += 2) {
- if (!isxdigit(buff[i]) || !isxdigit(buff[i + 1])) {
- 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) {
- if (isdigit(buff[i])) {
- if (isdigit(buff[i+1]))
- hex = 16 * (buff[i] - '0') + (buff[i+1] - '0');
- else
- hex = 16 * (buff[i] - '0') + (tolower(buff[i+1]) - 'a' + 10);
- }
- else {
- if (isdigit(buff[i+1]))
- hex = 16 * (tolower(buff[i]) - 'a') + (buff[i+1] - '0');
- else
- hex = 16 * (tolower(buff[i]) - 'a' + 10) + (tolower(buff[i+1]) - 'a' + 10);
- }
-
- value[size] = (char)(hex);
- size++;
- }
-
- if (error) *error = EOK;
- if (length) *length = size;
- TRACE_FLOW_STRING("get_bin_config_value", "Exit");
- return value;
-}
-
-/* Function to free binary configuration value */
-void free_bin_config_value(char *value)
-{
- if (value) free(value);
-}
-
-/* Arrays of stings */
-static char **get_str_cfg_array(struct collection_item *item,
- int include,
- const char *sep,
- int *size,
- int *error)
-{
- char *copy = NULL;
- char *dest = NULL;
- char locsep[4];
- int lensep;
- char *buff;
- int count = 0;
- int len = 0;
- int resume_len;
- char **array;
- char *start;
- int i, j;
- int dlen;
-
- TRACE_FLOW_STRING("get_str_cfg_array", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- /* Handle the separators */
- if (sep == NULL) {
- locsep[0] = ',';
- locsep[1] = '\0';
- lensep = 2;
- }
- else {
- strncpy(locsep, sep, 3);
- locsep[3] = '\0';
- lensep = strlen(locsep) + 1;
- }
-
- /* Allocate memory for the copy of the string */
- copy = malloc(col_get_item_length(item));
- if (copy == NULL) {
- TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
- if (error) *error = ENOMEM;
- return NULL;
- }
-
- /* Loop through the string */
- dest = copy;
- buff = col_get_item_data(item);
- start = buff;
- dlen = col_get_item_length(item);
- 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 increas 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 = copy;
- for (i = 0; i < count; i++) {
- TRACE_INFO_STRING("Token :", start);
- TRACE_INFO_NUMBER("Item :", i);
- array[i] = start;
- /* Move to next item */
- while(*start) start++;
- start++;
- }
- array[count] = NULL;
-
- if (error) *error = EOK;
- if (size) *size = count;
- TRACE_FLOW_STRING("get_str_cfg_array", "Exit");
- return array;
-}
-
-/* Get array of strings from item eliminating empty tokens */
-char **get_string_config_array(struct collection_item *item,
- const char *sep, int *size, int *error)
-{
- TRACE_FLOW_STRING("get_string_config_array", "Called.");
- return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error);
-}
-/* Get array of strings from item preserving empty tokens */
-char **get_raw_string_config_array(struct collection_item *item,
- const char *sep, int *size, int *error)
-{
- TRACE_FLOW_STRING("get_raw_string_config_array", "Called.");
- return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error);
-}
-
-/* Special function to free string config array */
-void free_string_config_array(char **str_config)
-{
- TRACE_FLOW_STRING("free_string_config_array", "Entry");
-
- if (str_config != NULL) {
- if (*str_config != NULL) free(*str_config);
- free(str_config);
- }
-
- TRACE_FLOW_STRING("free_string_config_array", "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 *get_long_config_array(struct collection_item *item, int *size, int *error)
-{
- const char *str;
- char *endptr;
- long val = 0;
- long *array;
- int count = 0;
- int err;
-
- TRACE_FLOW_STRING("get_long_config_array", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING) ||
- (size == NULL)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- /* Assume that we have maximum number of different numbers */
- array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2);
- if (array == NULL) {
- TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
- if (error) *error = ENOMEM;
- return NULL;
- }
-
- /* Now parse the string */
- str = (const char *)col_get_item_data(item);
- 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_NUMBER("get_long_config_value returning", val);
- return array;
-
-}
-
-/* Get an array of double values */
-double *get_double_config_array(struct collection_item *item, int *size, int *error)
-{
- const char *str;
- char *endptr;
- double val = 0;
- double *array;
- int count = 0;
- struct lconv *loc;
-
- TRACE_FLOW_STRING("get_double_config_array", "Entry");
-
- /* Do we have the item ? */
- if ((item == NULL) ||
- (col_get_item_type(item) != COL_TYPE_STRING) ||
- (size == NULL)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- /* Assume that we have maximum number of different numbers */
- array = (double *)malloc(sizeof(double) * col_get_item_length(item)/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 */
- str = (const char *)col_get_item_data(item);
- 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_NUMBER("get_double_config_value returning", val);
- return array;
-
-}
-
-
-/* Special function to free long config array */
-void free_long_config_array(long *array)
-{
- if (array != NULL) free(array);
-}
-
-/* Special function to free double config array */
-void free_double_config_array(double *array)
-{
- if (array != NULL) free(array);
-}
-
-/* The section array should be freed using this function */
-void free_section_list(char **section_list)
-{
- TRACE_FLOW_STRING("free_section_list","Entry");
-
- col_free_property_list(section_list);
-
- TRACE_FLOW_STRING("free_section_list","Exit");
-}
-
-/* The section array should be freed using this function */
-void free_attribute_list(char **section_list)
-{
- TRACE_FLOW_STRING("free_section_list","Entry");
-
- col_free_property_list(section_list);
-
- TRACE_FLOW_STRING("free_section_list","Exit");
-}
-
-
-/* Get list of sections as an array of strings.
- * Function allocates memory for the array of the sections.
- */
-char **get_section_list(struct collection_item *ini_config, int *size, int *error)
-{
- char **list;
-
- TRACE_FLOW_STRING("get_section_list","Entry");
- /* Do we have the item ? */
- if ((ini_config == NULL) ||
- ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) &&
- (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0))) {
- 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, size, error);
-
- TRACE_FLOW_STRING("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 **get_attribute_list(struct collection_item *ini_config, const char *section, int *size, int *error)
-{
- struct collection_item *subcollection = NULL;
- char **list;
- int err;
-
- TRACE_FLOW_STRING("get_attribute_list","Entry");
- /* Do we have the item ? */
- if ((ini_config == NULL) ||
- ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) &&
- (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) ||
- (section == NULL)) {
- TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
- if (error) *error = EINVAL;
- return NULL;
- }
-
- /* Fetch section */
- err = col_get_collection_reference(ini_config, &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);
-
- TRACE_FLOW_STRING("get_attribute_list returning", ((list == NULL) ? "NULL" : list[0]));
- return list;
-}
diff --git a/ini/ini_config.h b/ini/ini_config.h
index a5fd4a9..3b58a11 100644
--- a/ini/ini_config.h
+++ b/ini/ini_config.h
@@ -23,6 +23,9 @@
#ifndef INI_CONFIG_H
#define INI_CONFIG_H
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include "collection.h"
diff --git a/ini/ini_defines.h b/ini/ini_defines.h
new file mode 100644
index 0000000..46ed00f
--- /dev/null
+++ b/ini/ini_defines.h
@@ -0,0 +1,74 @@
+/*
+ INI LIBRARY
+
+ Header file for the internal constants for the INI interface.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+ 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/>.
+*/
+
+#ifndef INI_DEFINES_H
+#define INI_DEFINES_H
+
+#define NAME_OVERHEAD 10
+
+#define SLASH "/"
+
+
+/* Name of the special collection used to store parsing errors */
+#define FILE_ERROR_SET "ini_file_error_set"
+
+/* Text error strings used when errors are printed out */
+#define WARNING_TXT _("Warning")
+#define ERROR_TXT _("Error")
+/* For parse errors */
+#define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n")
+#define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n")
+#define ERROR_HEADER _("Parsing errors and warnings in file: %s\n")
+/* For grammar errors */
+#define WRONG_GRAMMAR _("Passed in list is not a list of grammar errors.\n")
+#define FAILED_TO_PROC_G _("Internal Error. Failed to process list of grammar errors.\n")
+#define ERROR_HEADER_G _("Logical errors and warnings in file: %s\n")
+/* For validation errors */
+#define WRONG_VALIDATION _("Passed in list is not a list of validation errors.\n")
+#define FAILED_TO_PROC_V _("Internal Error. Failed to process list of validation errors.\n")
+#define ERROR_HEADER_V _("Validation errors and warnings in file: %s\n")
+
+#define LINE_FORMAT _("%s (%d) on line %d: %s\n")
+
+
+/* Codes that parsing function can return */
+#define RET_PAIR 0
+#define RET_COMMENT 1
+#define RET_SECTION 2
+#define RET_INVALID 3
+#define RET_EMPTY 4
+#define RET_EOF 5
+#define RET_ERROR 6
+
+#define INI_ERROR "errors"
+#define INI_ERROR_NAME "errname"
+
+/* Internal sizes. MAX_KEY is defined in config.h */
+#define MAX_VALUE PATH_MAX
+#define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3
+
+/* Beffer length used for int to string conversions */
+#define CONVERSION_BUFFER 80
+
+/* Different error string functions can be passed as callbacks */
+typedef const char * (*error_fn)(int error);
+
+#endif
diff --git a/ini/ini_get_array.c b/ini/ini_get_array.c
new file mode 100644
index 0000000..c2c6362
--- /dev/null
+++ b/ini/ini_get_array.c
@@ -0,0 +1,354 @@
+/*
+ INI LIBRARY
+
+ Value interpretation functions for arrays of values
+ and corresponding memory cleanup functions.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+ 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_config.h"
+
+/*
+ * Internal contants to indicate how
+ * to process the lists of strings.
+ */
+#define EXCLUDE_EMPTY 0
+#define INCLUDE_EMPTY 1
+
+/* Arrays of stings */
+static char **get_str_cfg_array(struct collection_item *item,
+ int include,
+ const char *sep,
+ int *size,
+ int *error)
+{
+ char *copy = NULL;
+ char *dest = NULL;
+ char locsep[4];
+ int lensep;
+ char *buff;
+ int count = 0;
+ int len = 0;
+ int resume_len;
+ char **array;
+ char *start;
+ int i, j;
+ int dlen;
+
+ TRACE_FLOW_STRING("get_str_cfg_array", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Handle the separators */
+ if (sep == NULL) {
+ locsep[0] = ',';
+ locsep[1] = '\0';
+ lensep = 2;
+ }
+ else {
+ strncpy(locsep, sep, 3);
+ locsep[3] = '\0';
+ lensep = strlen(locsep) + 1;
+ }
+
+ /* Allocate memory for the copy of the string */
+ copy = malloc(col_get_item_length(item));
+ if (copy == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if (error) *error = ENOMEM;
+ return NULL;
+ }
+
+ /* Loop through the string */
+ dest = copy;
+ buff = col_get_item_data(item);
+ start = buff;
+ dlen = col_get_item_length(item);
+ 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 increas 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 = copy;
+ for (i = 0; i < count; i++) {
+ TRACE_INFO_STRING("Token :", start);
+ TRACE_INFO_NUMBER("Item :", i);
+ array[i] = start;
+ /* Move to next item */
+ while(*start) start++;
+ start++;
+ }
+ array[count] = NULL;
+
+ if (error) *error = EOK;
+ if (size) *size = count;
+ TRACE_FLOW_STRING("get_str_cfg_array", "Exit");
+ return array;
+}
+
+/* Get array of strings from item eliminating empty tokens */
+char **get_string_config_array(struct collection_item *item,
+ const char *sep, int *size, int *error)
+{
+ TRACE_FLOW_STRING("get_string_config_array", "Called.");
+ return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error);
+}
+/* Get array of strings from item preserving empty tokens */
+char **get_raw_string_config_array(struct collection_item *item,
+ const char *sep, int *size, int *error)
+{
+ TRACE_FLOW_STRING("get_raw_string_config_array", "Called.");
+ return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error);
+}
+
+/* Special function to free string config array */
+void free_string_config_array(char **str_config)
+{
+ TRACE_FLOW_STRING("free_string_config_array", "Entry");
+
+ if (str_config != NULL) {
+ if (*str_config != NULL) free(*str_config);
+ free(str_config);
+ }
+
+ TRACE_FLOW_STRING("free_string_config_array", "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 *get_long_config_array(struct collection_item *item, int *size, int *error)
+{
+ const char *str;
+ char *endptr;
+ long val = 0;
+ long *array;
+ int count = 0;
+ int err;
+
+ TRACE_FLOW_STRING("get_long_config_array", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING) ||
+ (size == NULL)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Assume that we have maximum number of different numbers */
+ array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2);
+ if (array == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if (error) *error = ENOMEM;
+ return NULL;
+ }
+
+ /* Now parse the string */
+ str = (const char *)col_get_item_data(item);
+ 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_NUMBER("get_long_config_value returning", val);
+ return array;
+
+}
+
+/* Get an array of double values */
+double *get_double_config_array(struct collection_item *item, int *size, int *error)
+{
+ const char *str;
+ char *endptr;
+ double val = 0;
+ double *array;
+ int count = 0;
+ struct lconv *loc;
+
+ TRACE_FLOW_STRING("get_double_config_array", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING) ||
+ (size == NULL)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Assume that we have maximum number of different numbers */
+ array = (double *)malloc(sizeof(double) * col_get_item_length(item)/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 */
+ str = (const char *)col_get_item_data(item);
+ 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_NUMBER("get_double_config_value returning", val);
+ return array;
+
+}
+
+
+/* Special function to free long config array */
+void free_long_config_array(long *array)
+{
+ if (array != NULL) free(array);
+}
+
+/* Special function to free double config array */
+void free_double_config_array(double *array)
+{
+ if (array != NULL) free(array);
+}
diff --git a/ini/ini_get_value.c b/ini/ini_get_value.c
new file mode 100644
index 0000000..70d940f
--- /dev/null
+++ b/ini/ini_get_value.c
@@ -0,0 +1,523 @@
+/*
+ INI LIBRARY
+
+ Value interpretation functions for single values
+ and corresponding memory cleanup functions.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+ 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.h"
+
+/* Function to get value from the configuration handle */
+int get_config_item(const char *section,
+ const char *name,
+ struct collection_item *ini_config,
+ struct collection_item **item)
+{
+ int error = EOK;
+ struct collection_item *section_handle = NULL;
+ const char *to_find;
+ char default_section[] = INI_DEFAULT_SECTION;
+
+ TRACE_FLOW_STRING("get_config_item", "Entry");
+
+ /* Do we have the accepting memory ? */
+ if (item == NULL) {
+ TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL);
+ return EINVAL;
+ }
+
+ /* Is the collection of a right type */
+ if ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) &&
+ (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) {
+ TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+ return EINVAL;
+ }
+
+ *item = NULL;
+
+ if (section == NULL) to_find = default_section;
+ else to_find = section;
+
+ TRACE_INFO_STRING("Getting Name:", name);
+ TRACE_INFO_STRING("In Section:", section);
+
+ /* Get Subcollection */
+ error = col_get_collection_reference(ini_config, &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_STRING("get_value_from_config", "No such section");
+ return EOK;
+ }
+
+ /* Get item */
+ error = col_get_item(section_handle, name,
+ COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item);
+
+ /* Make sure we free the section we found */
+ col_destroy_collection(section_handle);
+
+ TRACE_FLOW_NUMBER("get_config_item returning", error);
+ return error;
+}
+
+/* Get long long value from config item */
+static long long get_llong_config_value(struct collection_item *item,
+ int strict,
+ long long def,
+ int *error)
+{
+ int err;
+ const char *str;
+ char *endptr;
+ long long val = 0;
+
+ TRACE_FLOW_STRING("get_long_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return def;
+ }
+
+ if (error) *error = EOK;
+
+ /* Try to parse the value */
+ str = (const char *)col_get_item_data(item);
+ 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("get_long_config_value returning", (long)val);
+ return val;
+}
+
+/* Get unsigned long long value from config item */
+static unsigned long long get_ullong_config_value(struct collection_item *item,
+ int strict,
+ unsigned long long def,
+ int *error)
+{
+ int err;
+ const char *str;
+ char *endptr;
+ unsigned long long val = 0;
+
+ TRACE_FLOW_STRING("get_ullong_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return def;
+ }
+
+ if (error) *error = EOK;
+
+ /* Try to parse the value */
+ str = (const char *)col_get_item_data(item);
+ 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("get_ullong_config_value returning", (long)val);
+ return val;
+}
+
+
+/* Get integer value from config item */
+int get_int_config_value(struct collection_item *item,
+ int strict,
+ int def,
+ int *error)
+{
+ long long val = 0;
+ int err = 0;
+
+ TRACE_FLOW_STRING("get_int_config_value", "Entry");
+
+ val = get_llong_config_value(item, strict, def, &err);
+ if (err == 0) {
+ if ((val > INT_MAX) || (val < INT_MIN)) {
+ val = def;
+ err = ERANGE;
+ }
+ }
+
+ if (error) *error = err;
+
+ TRACE_FLOW_NUMBER("get_int_config_value returning", (int)val);
+ return (int)val;
+}
+
+/* Get unsigned integer value from config item */
+unsigned get_unsigned_config_value(struct collection_item *item,
+ int strict,
+ unsigned def,
+ int *error)
+{
+ unsigned long long val = 0;
+ int err = 0;
+
+ TRACE_FLOW_STRING("get_unsigned_config_value", "Entry");
+
+ val = get_ullong_config_value(item, strict, def, &err);
+ if (err == 0) {
+ if (val > UINT_MAX) {
+ val = def;
+ err = ERANGE;
+ }
+ }
+
+ if (error) *error = err;
+
+ TRACE_FLOW_NUMBER("get_unsigned_config_value returning",
+ (unsigned)val);
+ return (unsigned)val;
+}
+
+/* Get long value from config item */
+long get_long_config_value(struct collection_item *item,
+ int strict,
+ long def,
+ int *error)
+{
+ long long val = 0;
+ int err = 0;
+
+ TRACE_FLOW_STRING("get_long_config_value", "Entry");
+
+ val = get_llong_config_value(item, strict, def, &err);
+ if (err == 0) {
+ if ((val > LONG_MAX) || (val < LONG_MIN)) {
+ val = def;
+ err = ERANGE;
+ }
+ }
+
+ if (error) *error = err;
+
+ TRACE_FLOW_NUMBER("get_long_config_value returning",
+ (long)val);
+ return (long)val;
+}
+
+/* Get unsigned long value from config item */
+unsigned long get_ulong_config_value(struct collection_item *item,
+ int strict,
+ unsigned long def,
+ int *error)
+{
+ unsigned long long val = 0;
+ int err = 0;
+
+ TRACE_FLOW_STRING("get_ulong_config_value", "Entry");
+
+ val = get_ullong_config_value(item, strict, def, &err);
+ if (err == 0) {
+ if (val > ULONG_MAX) {
+ val = def;
+ err = ERANGE;
+ }
+ }
+
+ if (error) *error = err;
+
+ TRACE_FLOW_NUMBER("get_ulong_config_value returning",
+ (unsigned long)val);
+ return (unsigned long)val;
+}
+
+
+/* Get double value */
+double get_double_config_value(struct collection_item *item,
+ int strict, double def, int *error)
+{
+ const char *str;
+ char *endptr;
+ double val = 0;
+
+ TRACE_FLOW_STRING("get_double_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return def;
+ }
+
+ if (error) *error = EOK;
+
+ /* Try to parse the value */
+ str = (const char *)col_get_item_data(item);
+ 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_NUMBER("get_double_config_value returning", val);
+ return val;
+}
+
+/* Get boolean value */
+unsigned char get_bool_config_value(struct collection_item *item,
+ unsigned char def, int *error)
+{
+ const char *str;
+ int len;
+
+ TRACE_FLOW_STRING("get_bool_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return def;
+ }
+
+ if (error) *error = EOK;
+
+ str = (const char *)col_get_item_data(item);
+ len = col_get_item_length(item);
+
+ /* 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 *get_string_config_value(struct collection_item *item,
+ int *error)
+{
+ char *str = NULL;
+
+ TRACE_FLOW_STRING("get_string_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ str = strdup((const char *)col_get_item_data(item));
+ if (str == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if (error) *error = ENOMEM;
+ return NULL;
+ }
+
+ if (error) *error = EOK;
+
+ TRACE_FLOW_STRING("get_string_config_value returning", str);
+ return str;
+}
+
+/* Get string from item */
+const char *get_const_string_config_value(struct collection_item *item, int *error)
+{
+ const char *str;
+
+ TRACE_FLOW_STRING("get_const_string_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ str = (const char *)col_get_item_data(item);
+
+ if (error) *error = EOK;
+
+ TRACE_FLOW_STRING("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 *get_bin_config_value(struct collection_item *item,
+ int *length, int *error)
+{
+ int i;
+ char *value = NULL;
+ const char *buff;
+ int size = 0;
+ unsigned char hex;
+ int len;
+ const char *str;
+
+ TRACE_FLOW_STRING("get_bin_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if ((item == NULL) ||
+ (col_get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Check the length */
+ len = col_get_item_length(item)-1;
+ if ((len%2) != 0) {
+ TRACE_ERROR_STRING("Invalid length for binary data", "");
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ str = (const char *)col_get_item_data(item);
+
+ /* 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 += 2) {
+ if (!isxdigit(buff[i]) || !isxdigit(buff[i + 1])) {
+ 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) {
+ if (isdigit(buff[i])) {
+ if (isdigit(buff[i+1]))
+ hex = 16 * (buff[i] - '0') + (buff[i+1] - '0');
+ else
+ hex = 16 * (buff[i] - '0') + (tolower(buff[i+1]) - 'a' + 10);
+ }
+ else {
+ if (isdigit(buff[i+1]))
+ hex = 16 * (tolower(buff[i]) - 'a') + (buff[i+1] - '0');
+ else
+ hex = 16 * (tolower(buff[i]) - 'a' + 10) + (tolower(buff[i+1]) - 'a' + 10);
+ }
+
+ value[size] = (char)(hex);
+ size++;
+ }
+
+ if (error) *error = EOK;
+ if (length) *length = size;
+ TRACE_FLOW_STRING("get_bin_config_value", "Exit");
+ return value;
+}
+
+/* Function to free binary configuration value */
+void free_bin_config_value(char *value)
+{
+ if (value) free(value);
+}
diff --git a/ini/ini_list.c b/ini/ini_list.c
new file mode 100644
index 0000000..95894a1
--- /dev/null
+++ b/ini/ini_list.c
@@ -0,0 +1,123 @@
+/*
+ INI LIBRARY
+
+ Value interpretation functions for single values
+ and corresponding memory cleanup functions.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+ 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 <ctype.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+*/
+#include "config.h"
+#include "trace.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "ini_config.h"
+
+
+/* The section array should be freed using this function */
+void free_section_list(char **section_list)
+{
+ TRACE_FLOW_STRING("free_section_list","Entry");
+
+ col_free_property_list(section_list);
+
+ TRACE_FLOW_STRING("free_section_list","Exit");
+}
+
+/* The section array should be freed using this function */
+void free_attribute_list(char **section_list)
+{
+ TRACE_FLOW_STRING("free_attribute_list","Entry");
+
+ col_free_property_list(section_list);
+
+ TRACE_FLOW_STRING("free_attribute_list","Exit");
+}
+
+
+/* Get list of sections as an array of strings.
+ * Function allocates memory for the array of the sections.
+ */
+char **get_section_list(struct collection_item *ini_config, int *size, int *error)
+{
+ char **list;
+
+ TRACE_FLOW_STRING("get_section_list","Entry");
+ /* Do we have the item ? */
+ if ((ini_config == NULL) ||
+ ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) &&
+ (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0))) {
+ 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, size, error);
+
+ TRACE_FLOW_STRING("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 **get_attribute_list(struct collection_item *ini_config, const char *section, int *size, int *error)
+{
+ struct collection_item *subcollection = NULL;
+ char **list;
+ int err;
+
+ TRACE_FLOW_STRING("get_attribute_list","Entry");
+ /* Do we have the item ? */
+ if ((ini_config == NULL) ||
+ ((col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) == 0) &&
+ (col_is_of_class(ini_config, COL_CLASS_INI_META) == 0)) ||
+ (section == NULL)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if (error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Fetch section */
+ err = col_get_collection_reference(ini_config, &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);
+
+ TRACE_FLOW_STRING("get_attribute_list returning", ((list == NULL) ? "NULL" : list[0]));
+ return list;
+}
diff --git a/ini/ini_parse.c b/ini/ini_parse.c
new file mode 100644
index 0000000..be733d6
--- /dev/null
+++ b/ini/ini_parse.c
@@ -0,0 +1,190 @@
+/*
+ INI LIBRARY
+
+ Low level parsing functions
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+ 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 <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include "config.h"
+#include "trace.h"
+#include "ini_parse.h"
+#include "ini_defines.h"
+#include "ini_config.h"
+
+
+/* Reads a line from the file */
+int read_line(FILE *file,
+ char *buf,
+ int read_size,
+ char **key, char **value,
+ int *length,
+ int *ext_error)
+{
+
+ char *res;
+ int len;
+ char *buffer;
+ int i;
+ char *eq;
+
+ TRACE_FLOW_STRING("read_line", "Entry");
+
+ *ext_error = 0;
+
+ buffer = buf;
+
+ /* Get data from file */
+ res = fgets(buffer, read_size - 1, file);
+ if (res == NULL) {
+ TRACE_ERROR_STRING("Read nothing", "");
+ return RET_EOF;
+ }
+
+ /* Make sure the buffer is NULL terminated */
+ buffer[read_size - 1] = '\0';
+
+ len = strlen(buffer);
+ if (len == 0) {
+ TRACE_ERROR_STRING("Nothing was read.", "");
+ return RET_EMPTY;
+ }
+
+ /* Added \r just in case we deal with Windows in future */
+ if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) {
+ TRACE_ERROR_STRING("String it too big!", "");
+ *ext_error = ERR_LONGDATA;
+ return RET_ERROR;
+ }
+
+ /* Ingnore comments */
+ if ((*buffer == ';') || (*buffer == '#')) {
+ TRACE_FLOW_STRING("Comment", buf);
+ return RET_COMMENT;
+ }
+
+ TRACE_INFO_STRING("BUFFER before trimming:", buffer);
+
+ /* Trucate trailing spaces and CRs */
+ /* Make sure not to step before the beginning */
+ while (len && isspace(buffer[len - 1])) {
+ buffer[len - 1] = '\0';
+ len--;
+ }
+
+ TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer);
+
+ /* Trucate leading spaces */
+ while (isspace(*buffer)) {
+ buffer++;
+ len--;
+ }
+
+ TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer);
+ TRACE_INFO_NUMBER("BUFFER length:", len);
+
+ /* Empty line */
+ if (len == 0) {
+ TRACE_FLOW_STRING("Empty line", buf);
+ return RET_EMPTY;
+ }
+
+ /* Section */
+ if (*buffer == '[') {
+ if (buffer[len-1] != ']') {
+ TRACE_ERROR_STRING("Invalid format for section", buf);
+ *ext_error = ERR_NOCLOSESEC;
+ return RET_ERROR;
+ }
+ buffer++;
+ len--;
+ while (isspace(*buffer)) {
+ buffer++;
+ len--;
+ }
+ if (len == 0) {
+ TRACE_ERROR_STRING("Invalid format for section", buf);
+ *ext_error = ERR_NOSECTION;
+ return RET_ERROR;
+ }
+
+ buffer[len - 1] = '\0';
+ len--;
+ while (isspace(buffer[len - 1])) {
+ buffer[len - 1] = '\0';
+ len--;
+ }
+ if (len >= MAX_KEY) {
+ TRACE_ERROR_STRING("Section name is too long", buf);
+ *ext_error = ERR_SECTIONLONG;
+ return RET_ERROR;
+ }
+
+ *key = buffer;
+ return RET_SECTION;
+ }
+
+ /* Assume we are dealing with the K-V here */
+ /* Find "=" */
+ eq = strchr(buffer, '=');
+ if (eq == NULL) {
+ TRACE_ERROR_STRING("No equal sign", buf);
+ *ext_error = ERR_NOEQUAL;
+ return RET_INVALID;
+ }
+
+ len -= eq-buffer;
+
+ /* Strip spaces around "=" */
+ i = eq - buffer - 1;
+ while ((i >= 0) && isspace(buffer[i])) i--;
+ if (i < 0) {
+ TRACE_ERROR_STRING("No key", buf);
+ *ext_error = ERR_NOKEY;
+ return RET_INVALID;
+ }
+
+ /* Copy key into provided buffer */
+ if(i >= MAX_KEY) {
+ TRACE_ERROR_STRING("Section name is too long", buf);
+ *ext_error = ERR_LONGKEY;
+ return RET_INVALID;
+ }
+ *key = buffer;
+ buffer[i + 1] = '\0';
+ TRACE_INFO_STRING("KEY:", *key);
+
+ eq++;
+ len--;
+ while (isspace(*eq)) {
+ eq++;
+ len--;
+ }
+
+ *value = eq;
+ /* Make sure we include trailing 0 into data */
+ *length = len + 1;
+
+ TRACE_INFO_STRING("VALUE:", *value);
+ TRACE_INFO_NUMBER("LENGTH:", *length);
+
+ TRACE_FLOW_STRING("read_line", "Exit");
+ return RET_PAIR;
+}
diff --git a/ini/ini_parse.h b/ini/ini_parse.h
new file mode 100644
index 0000000..56a0db9
--- /dev/null
+++ b/ini/ini_parse.h
@@ -0,0 +1,36 @@
+/*
+ INI LIBRARY
+
+ Header file for the internal parsing functions.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+ 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/>.
+*/
+
+#ifndef INI_PARSE_H
+#define INI_PARSE_H
+
+#include <stdio.h>
+
+/* Internal function to read line from INI file */
+int read_line(FILE *file,
+ char *buf,
+ int read_size,
+ char **key,
+ char **value,
+ int *length,
+ int *ext_error);
+
+#endif
diff --git a/ini/ini_print.c b/ini/ini_print.c
new file mode 100644
index 0000000..e8f9186
--- /dev/null
+++ b/ini/ini_print.c
@@ -0,0 +1,387 @@
+/*
+ INI LIBRARY
+
+ Parsing functions of the INI interface
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+ 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"
+/* For error text */
+#include <libintl.h>
+#define _(String) gettext (String)
+/* INI file is used as a collection */
+#include "trace.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "ini_defines.h"
+#include "ini_config.h"
+
+
+/*============================================================*/
+/* The following classes moved here from the public header
+ * They are reserved for future use.
+ *
+ * NOTE: before exposing these constants again in the common header
+ * check that the class IDs did not get reused over time by
+ * other classes.
+ */
+/** @brief Collection of grammar errors.
+ *
+ * Reserved for future use.
+ */
+#define COL_CLASS_INI_GERROR COL_CLASS_INI_BASE + 5
+/** @brief Collection of validation errors.
+ *
+ * Reserved for future use.
+ */
+#define COL_CLASS_INI_VERROR COL_CLASS_INI_BASE + 6
+
+#ifdef HAVE_VALIDATION
+
+/** @brief Collection of lines from the INI file.
+ *
+ * Reserved for future use
+ */
+#define COL_CLASS_INI_LINES COL_CLASS_INI_BASE + 7
+
+#endif /* HAVE_VALIDATION */
+/*============================================================*/
+
+
+/* Function to return parsing error */
+const char *parsing_error_str(int parsing_error)
+{
+ const char *placeholder= _("Unknown pasing error.");
+ const char *str_error[] = { _("Data is too long."),
+ _("No closing bracket."),
+ _("Section name is missing."),
+ _("Section name is too long."),
+ _("Equal sign is missing."),
+ _("Property name is missing."),
+ _("Property name is too long.")
+ };
+
+ /* Check the range */
+ if ((parsing_error < 1) || (parsing_error > ERR_MAXPARSE))
+ return placeholder;
+ else
+ return str_error[parsing_error-1];
+}
+
+/* Function to return grammar error.
+ * This function is currently not used.
+ * It is planned to be used by the INI
+ * file grammar parser.
+ *
+ * The following doxygen description is moved here.
+ * When the function gets exposed move it into
+ * the header file.
+ */
+/** @brief Function to return a grammar error in template.
+ *
+ * EXPERIMENTAL. Reserved for future use.
+ *
+ * This error is returned when the template
+ * is translated into the grammar object.
+ *
+ * @param[in] parsing_error Error code for the grammar error.
+ *
+ * @return Error string.
+ */
+
+const char *grammar_error_str(int grammar_error)
+{
+ const char *placeholder= _("Unknown grammar error.");
+ /* THIS IS A TEMPORARY PLACEHOLDER !!!! */
+ const char *str_error[] = { _(""),
+ _(""),
+ _(""),
+ _(""),
+ _(""),
+ _(""),
+ _("")
+ };
+
+ /* Check the range */
+ if ((grammar_error < 1) || (grammar_error > ERR_MAXGRAMMAR))
+ return placeholder;
+ else
+ return str_error[grammar_error-1];
+}
+
+/* Function to return validation error.
+ * This function is currently not used.
+ * It is planned to be used by the INI
+ * file grammar validator.
+ *
+ * The following doxygen description is moved here.
+ * When the function gets exposed move it into
+ * the header file.
+ */
+/** @brief Function to return a validation error.
+ *
+ * EXPERIMENTAL. Reserved for future use.
+ *
+ * This is the error that it is returned when
+ * the INI file is validated against the
+ * grammar object.
+ *
+ * @param[in] parsing_error Error code for the validation error.
+ *
+ * @return Error string.
+ */
+const char *validation_error_str(int validation_error)
+{
+ const char *placeholder= _("Unknown validation error.");
+ /* THIS IS A TEMPORARY PLACEHOLDER !!!! */
+ const char *str_error[] = { _(""),
+ _(""),
+ _(""),
+ _(""),
+ _(""),
+ _(""),
+ _("")
+ };
+
+ /* Check the range */
+ if ((validation_error < 1) || (validation_error > ERR_MAXVALID))
+ return placeholder;
+ else
+ return str_error[validation_error-1];
+}
+
+
+
+/* Internal function that prints errors */
+static void print_error_list(FILE *file,
+ struct collection_item *error_list,
+ int cclass,
+ char *wrong_col_error,
+ char *failed_to_process,
+ char *error_header,
+ char *line_format,
+ error_fn error_function)
+{
+ struct collection_iterator *iterator;
+ int error;
+ struct collection_item *item = NULL;
+ struct parse_error *pe;
+ unsigned int count;
+
+ TRACE_FLOW_STRING("print_error_list", "Entry");
+
+ /* If we have something to print print it */
+ if (error_list == NULL) {
+ TRACE_ERROR_STRING("No error list","");
+ return;
+ }
+
+ /* Make sure we go the right collection */
+ if (!col_is_of_class(error_list, cclass)) {
+ TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error);
+ fprintf(file,"%s\n", wrong_col_error);
+ return;
+ }
+
+ /* Bind iterator */
+ error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT);
+ if (error) {
+ TRACE_ERROR_STRING("Error (bind):", failed_to_process);
+ fprintf(file, "%s\n", failed_to_process);
+ return;
+ }
+
+ while(1) {
+ /* Loop through a collection */
+ error = col_iterate_collection(iterator, &item);
+ if (error) {
+ TRACE_ERROR_STRING("Error (iterate):", failed_to_process);
+ fprintf(file, "%s\n", failed_to_process);
+ col_unbind_iterator(iterator);
+ return;
+ }
+
+ /* Are we done ? */
+ if (item == NULL) break;
+
+ /* Process collection header */
+ if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
+ col_get_collection_count(item, &count);
+ if (count <= 2) break;
+ } else if (col_get_item_type(item) == COL_TYPE_STRING) {
+ fprintf(file, error_header, (char *)col_get_item_data(item));
+ }
+ else {
+ /* Put error into provided format */
+ pe = (struct parse_error *)(col_get_item_data(item));
+ fprintf(file, line_format,
+ col_get_item_property(item, NULL), /* Error or warning */
+ pe->error, /* Error */
+ pe->line, /* Line */
+ error_function(pe->error)); /* Error str */
+ }
+
+ }
+
+ /* Do not forget to unbind iterator - otherwise there will be a leak */
+ col_unbind_iterator(iterator);
+
+ TRACE_FLOW_STRING("print_error_list", "Exit");
+}
+
+/* Print errors and warnings that were detected while parsing one file */
+void print_file_parsing_errors(FILE *file,
+ struct collection_item *error_list)
+{
+ print_error_list(file,
+ error_list,
+ COL_CLASS_INI_PERROR,
+ WRONG_COLLECTION,
+ FAILED_TO_PROCCESS,
+ ERROR_HEADER,
+ LINE_FORMAT,
+ parsing_error_str);
+}
+
+
+/* Print errors and warnings that were detected while processing grammar.
+ *
+ * The following doxygen description is moved here.
+ * When the function gets exposed move it into
+ * the header file.
+ */
+/**
+ * @brief Print errors and warnings that were detected while
+ * checking grammar of the template.
+ *
+ * EXPERIMENTAL. Reserved for future use.
+ *
+ * @param[in] file File descriptor.
+ * @param[in] error_list List of the parsing errors.
+ *
+ */
+void print_grammar_errors(FILE *file,
+ struct collection_item *error_list)
+{
+ print_error_list(file,
+ error_list,
+ COL_CLASS_INI_GERROR,
+ WRONG_GRAMMAR,
+ FAILED_TO_PROC_G,
+ ERROR_HEADER_G,
+ LINE_FORMAT,
+ grammar_error_str);
+}
+
+/* Print errors and warnings that were detected while validating INI file.
+ *
+ * The following doxygen description is moved here.
+ * When the function gets exposed move it into
+ * the header file.
+ */
+/**
+ * @brief Print errors and warnings that were detected while
+ * checking INI file against the grammar object.
+ *
+ * EXPERIMENTAL. Reserved for future use.
+ *
+ * @param[in] file File descriptor.
+ * @param[in] error_list List of the parsing errors.
+ *
+ */
+void print_validation_errors(FILE *file,
+ struct collection_item *error_list)
+{
+ print_error_list(file,
+ error_list,
+ COL_CLASS_INI_VERROR,
+ WRONG_VALIDATION,
+ FAILED_TO_PROC_V,
+ ERROR_HEADER_V,
+ LINE_FORMAT,
+ validation_error_str);
+}
+
+/* Print errors and warnings that were detected while parsing
+ * the whole configuration */
+void print_config_parsing_errors(FILE *file,
+ struct collection_item *error_list)
+{
+ struct collection_iterator *iterator;
+ int error;
+ struct collection_item *item = NULL;
+ struct collection_item *file_errors = NULL;
+
+ TRACE_FLOW_STRING("print_config_parsing_errors", "Entry");
+
+ /* If we have something to print print it */
+ if (error_list == NULL) {
+ TRACE_ERROR_STRING("No error list", "");
+ return;
+ }
+
+ /* Make sure we go the right collection */
+ if (!col_is_of_class(error_list, COL_CLASS_INI_PESET)) {
+ TRACE_ERROR_STRING("Wrong collection class:", WRONG_COLLECTION);
+ fprintf(file, "%s\n", WRONG_COLLECTION);
+ return;
+ }
+
+ /* Bind iterator */
+ error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT);
+ if (error) {
+ TRACE_ERROR_STRING("Error (bind):", FAILED_TO_PROCCESS);
+ fprintf(file,"%s\n", FAILED_TO_PROCCESS);
+ return;
+ }
+
+ while(1) {
+ /* Loop through a collection */
+ error = col_iterate_collection(iterator, &item);
+ if (error) {
+ TRACE_ERROR_STRING("Error (iterate):", FAILED_TO_PROCCESS);
+ fprintf(file, "%s\n", FAILED_TO_PROCCESS);
+ col_unbind_iterator(iterator);
+ return;
+ }
+
+ /* Are we done ? */
+ if (item == NULL) break;
+
+ /* Print per file sets of errors */
+ if (col_get_item_type(item) == COL_TYPE_COLLECTIONREF) {
+ /* Extract a sub collection */
+ error = col_get_reference_from_item(item, &file_errors);
+ if (error) {
+ TRACE_ERROR_STRING("Error (extract):", FAILED_TO_PROCCESS);
+ fprintf(file, "%s\n", FAILED_TO_PROCCESS);
+ col_unbind_iterator(iterator);
+ return;
+ }
+ print_file_parsing_errors(file, file_errors);
+ col_destroy_collection(file_errors);
+ }
+ }
+
+ /* Do not forget to unbind iterator - otherwise there will be a leak */
+ col_unbind_iterator(iterator);
+
+ TRACE_FLOW_STRING("print_config_parsing_errors", "Exit");
+}