summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitri Pal <dpal@redhat.com>2010-11-27 22:03:24 -0500
committerStephen Gallagher <sgallagh@redhat.com>2010-12-21 11:16:09 -0500
commitec246f748f8dcbeefd46125546364ed2aa3100ea (patch)
tree299ef0b30b5e286d129e991eaac6491010dd702d
parent5dadfb4371b8ba694b8d9431cb6789bf6de485c4 (diff)
Allow merging values
This patch is the first pass at merging functionality. It implements merging of values that belong to the same section. Patch includes: * Definition of merge flags in doxy format * Definition of the masks in internal header * Changes to parser to handle the merging of values. * Also swithed parser to not use ini_config.h as I want to switch implementation of the current interface to new interface at some point. * New unit test was created. * New config file for this unit test was added to ini.d Main code changes are in ini_parse.c
-rw-r--r--ini/ini.d/foo.conf12
-rw-r--r--ini/ini_configobj.h87
-rw-r--r--ini/ini_defines.h5
-rw-r--r--ini/ini_parse.c114
-rw-r--r--ini/ini_parse_ut.c203
5 files changed, 410 insertions, 11 deletions
diff --git a/ini/ini.d/foo.conf b/ini/ini.d/foo.conf
new file mode 100644
index 0000000..44d56a1
--- /dev/null
+++ b/ini/ini.d/foo.conf
@@ -0,0 +1,12 @@
+#Hoho section
+[hoho]
+#Hoho value
+val= hoho
+#End of hoho
+#Start of section
+[foo]
+#First value
+bar = first value
+#Second value
+bar = second value
+#End of section
diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
index 21198db..88a8704 100644
--- a/ini/ini_configobj.h
+++ b/ini/ini_configobj.h
@@ -29,6 +29,7 @@
#include <stdio.h>
#include "simplebuffer.h"
+
/**
* @defgroup errorlevel Error tolerance constants
*
@@ -79,6 +80,92 @@
* @}
*/
+
+/**
+ * @defgroup collisionflags Flags that define collision resolution logic.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup onesecvalue Colliding values come from one section
+ *
+ * Flags that define collision resolution logic for values in
+ * the same section.
+ * These flags should be used during parsing to handle duplicate
+ * keys in the same section of the ini file.
+ *
+ * @{
+ */
+
+/** @brief Value with same key is ovewritten */
+#define INI_MV1S_OVERWRITE 0x0000
+/** @brief Collision causes error */
+#define INI_MV1S_ERROR 0x0001
+/** @brief Second value is discarded */
+#define INI_MV1S_PRESERVE 0x0002
+/** @brief Duplicates are allowed */
+#define INI_MV1S_ALLOW 0x0003
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup twosecvalue Colliding values come from two sections
+ *
+ * Flags that define collision resolution logic between two values
+ * that come from two sections with the same name.
+ * These flags should be used during parsing to handle duplicate
+ * keys coming from the same section scattered across the ini file.
+ * These flags also can be used to specify the rules of merging
+ * values that come from two files separate configuration files.
+ *
+ * @{
+ */
+/** @brief Value with same key is ovewritten */
+#define INI_MV2S_OVERWRITE 0x0000
+/** @brief Collision causes error */
+#define INI_MV2S_ERROR 0x0010
+/** @brief Second value is discarded */
+#define INI_MV2S_PRESERVE 0x0020
+/** @brief Duplicates are allowed */
+#define INI_MV2S_ALLOW 0x0030
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup mergesec Collision in two sections
+ *
+ * Flags that define collision resolution logic between two sections.
+ * These flags should be used during parsing to handle duplicate
+ * sections scattered across the ini file.
+ * These flags also can be used to specify the rules of merging
+ * sections that come from two separate configuration files.
+ *
+ * @{
+ */
+/** @brief Sections are merged */
+#define INI_MS_MERGE 0x0000
+/** @brief Collision causes error */
+#define INI_MS_ERROR 0x0100
+/** @brief First section is discarded */
+#define INI_MS_OVERWRITE 0x0200
+/** @brief Second section is discarded */
+#define INI_MS_PRESERVE 0x0300
+/** @brief Duplicates are allowed */
+#define INI_MS_ALLOW 0x0400
+
+/**
+ * @}
+ */
+/**
+ * @}
+ */
+
+
/**
* @defgroup structures Structures
* @{
diff --git a/ini/ini_defines.h b/ini/ini_defines.h
index 1d03835..b861f0e 100644
--- a/ini/ini_defines.h
+++ b/ini/ini_defines.h
@@ -100,6 +100,11 @@
#define INI_FAMILY_VALIDATION 1
#define INI_FAMILY_GRAMMAR 2
+#define INI_MV1S_MASK 0x000F /* Merge values options mask
+ * for one section */
+#define INI_MV2S_MASK 0x00F0 /* Merge values options mask
+ * for two sections. */
+#define INI_MS_MASK 0x0F00 /* Merge section options mask */
/* Different error string functions can be passed as callbacks */
diff --git a/ini/ini_parse.c b/ini/ini_parse.c
index c92a163..acdb608 100644
--- a/ini/ini_parse.c
+++ b/ini/ini_parse.c
@@ -29,21 +29,36 @@
#include "config.h"
#include "trace.h"
#include "ini_defines.h"
-#include "ini_config.h"
#include "ini_valueobj.h"
#include "ini_config_priv.h"
+#include "ini_configobj.h"
#include "collection.h"
#include "collection_queue.h"
#define INI_WARNING 0xA0000000 /* Warning bit */
+/* This constant belongs to ini_defines.h. Move from ini_config - TBD */
+#define COL_CLASS_INI_BASE 20000
+#define COL_CLASS_INI_SECTION COL_CLASS_INI_BASE + 1
+/**
+ * @brief Name of the default section.
+ *
+ * This is the name of the implied section where orphan key-value
+ * pairs will be put.
+ */
+#define INI_DEFAULT_SECTION "default"
+
+
struct parser_obj {
/* Externally passed and saved data */
FILE *file;
struct collection_item *top;
struct collection_item *el;
const char *filename;
+ /* Level of error reporting */
int error_level;
+ /* Collistion flags */
+ uint32_t collision_flags;
/* Wrapping boundary */
uint32_t boundary;
/* Action queue */
@@ -122,6 +137,7 @@ int parser_create(FILE *file,
const char *config_filename,
struct collection_item *ini_config,
int error_level,
+ uint32_t collision_flags,
struct collection_item *error_list,
uint32_t boundary,
struct parser_obj **po)
@@ -160,6 +176,7 @@ int parser_create(FILE *file,
new_po->el = error_list;
new_po->filename = config_filename;
new_po->error_level = error_level;
+ new_po->collision_flags = collision_flags;
new_po->boundary = boundary;
/* Initialize internal varibles */
@@ -287,6 +304,12 @@ static int complete_value_processing(struct parser_obj *po)
{
int error = EOK;
struct value_obj *vo = NULL;
+ struct value_obj *vo_old = NULL;
+ unsigned insertmode;
+ uint32_t mergemode;
+ int suppress = 0;
+ int doinsert = 0;
+ struct collection_item *item = NULL;
TRACE_FLOW_ENTRY();
@@ -322,17 +345,85 @@ static int complete_value_processing(struct parser_obj *po)
po->raw_lines = NULL;
po->raw_lengths = NULL;
+ mergemode = po->collision_flags & INI_MV1S_MASK;
+
+ switch (mergemode) {
+ case INI_MV1S_ERROR: insertmode = COL_INSERT_DUPERROR;
+ doinsert = 1;
+ break;
+ case INI_MV1S_PRESERVE: insertmode = COL_INSERT_DUPERROR;
+ doinsert = 1;
+ suppress = 1;
+ break;
+ case INI_MV1S_ALLOW: insertmode = COL_INSERT_NOCHECK;
+ doinsert = 1;
+ break;
+ case INI_MV1S_OVERWRITE: /* Special handling */
+ default:
+ break;
+ }
- /* Add value to collection */
- error = col_add_binary_property(po->sec,
- NULL,
- po->key,
- &vo,
- sizeof(struct value_obj *));
- if (error) {
- TRACE_ERROR_NUMBER("Failed to add value object to the section", error);
- value_destroy(vo);
- return error;
+ /* Do not insert but search for dups first */
+ if (!doinsert) {
+ TRACE_INFO_STRING("Ovewrite mode. Lokking for:", po->key);
+
+ error = col_get_item(po->sec,
+ po->key,
+ COL_TYPE_BINARY,
+ COL_TRAVERSE_DEFAULT,
+ &item);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed searching for dup", error);
+ return error;
+ }
+
+ /* Check if there is a dup */
+ if (item) {
+ /* Dup exists - update it */
+ vo_old = *((struct value_obj **)(col_get_item_data(item)));
+ error = col_modify_binary_item(item,
+ NULL,
+ &vo,
+ sizeof(struct value_obj *));
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed updating the value", error);
+ return error;
+ }
+ /* If we failed to update it is better to leak then crash,
+ * so desctroy original value only on the successful update.
+ */
+ value_destroy(vo_old);
+ }
+ else {
+ /* No dup found so we can insert with no check */
+ doinsert = 1;
+ insertmode = COL_INSERT_NOCHECK;
+ }
+ }
+
+ if (doinsert) {
+ /* Add value to collection */
+ error = col_insert_binary_property(po->sec,
+ NULL,
+ COL_DSP_END,
+ NULL,
+ 0,
+ insertmode,
+ po->key,
+ &vo,
+ sizeof(struct value_obj *));
+ if (error) {
+ if ((suppress) && (error == EEXIST)) {
+ TRACE_INFO_STRING("Preseved exisitng value", po->key);
+ value_destroy(vo);
+ }
+ else {
+ TRACE_ERROR_NUMBER("Failed to add value object to the section", error);
+ value_destroy(vo);
+ return error;
+ }
+ }
}
free(po->key);
@@ -970,6 +1061,7 @@ int ini_config_parse(struct ini_cfgfile *file_ctx,
file_ctx->filename,
ini_config->cfg,
file_ctx->error_level,
+ file_ctx->collision_flags,
file_ctx->error_list,
ini_config->boundary,
&po);
diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
index cc37c21..eed4649 100644
--- a/ini/ini_parse_ut.c
+++ b/ini/ini_parse_ut.c
@@ -231,6 +231,208 @@ int read_again_test(void)
return error;
}
+int create_expect(char *checkname)
+{
+ FILE *ff;
+ int error = EOK;
+
+ errno = 0;
+ ff = fopen(checkname, "w");
+ if(!ff) {
+ error = errno;
+ printf("Failed to open file for writing. Error %d.\n", error);
+ return error;
+ }
+
+ /* Ovewrite */
+ fprintf(ff,"#Hoho section\n");
+ fprintf(ff,"[hoho]\n");
+ fprintf(ff,"#Hoho value\n");
+ fprintf(ff,"val = hoho\n");
+ fprintf(ff,"#End of hoho\n");
+ fprintf(ff,"#Start of section\n");
+ fprintf(ff,"[foo]\n");
+ fprintf(ff,"#Second value\n");
+ fprintf(ff,"bar = second value\n");
+ fprintf(ff,"#End of section\n");
+ /* Error */
+ fprintf(ff,"#Hoho section\n");
+ fprintf(ff,"[hoho]\n");
+ fprintf(ff,"#Hoho value\n");
+ fprintf(ff,"val = hoho\n");
+ /* No "#End of hoho" line is expected due to error */
+ /* Preserve */
+ fprintf(ff,"#Hoho section\n");
+ fprintf(ff,"[hoho]\n");
+ fprintf(ff,"#Hoho value\n");
+ fprintf(ff,"val = hoho\n");
+ fprintf(ff,"#End of hoho\n");
+ fprintf(ff,"#Start of section\n");
+ fprintf(ff,"[foo]\n");
+ fprintf(ff,"#First value\n");
+ fprintf(ff,"bar = first value\n");
+ fprintf(ff,"#End of section\n");
+ /* Allow */
+ fprintf(ff,"#Hoho section\n");
+ fprintf(ff,"[hoho]\n");
+ fprintf(ff,"#Hoho value\n");
+ fprintf(ff,"val = hoho\n");
+ fprintf(ff,"#End of hoho\n");
+ fprintf(ff,"#Start of section\n");
+ fprintf(ff,"[foo]\n");
+ fprintf(ff,"#First value\n");
+ fprintf(ff,"bar = first value\n");
+ fprintf(ff,"#Second value\n");
+ fprintf(ff,"bar = second value\n");
+ fprintf(ff,"#End of section\n");
+
+ fclose(ff);
+
+ return EOK;
+}
+
+
+
+/* Check merge modes */
+int merge_values_test(void)
+{
+ int error = EOK;
+ int i;
+ struct ini_cfgfile *file_ctx = NULL;
+ FILE *ff = NULL;
+ struct ini_cfgobj *ini_config = NULL;
+ char **error_list = NULL;
+ struct simplebuffer *sbobj = NULL;
+ uint32_t left = 0;
+ uint32_t mflags[] = { INI_MV1S_OVERWRITE,
+ INI_MV1S_ERROR,
+ INI_MV1S_PRESERVE,
+ INI_MV1S_ALLOW };
+
+ const char *mstr[] = { "OVERWRITE",
+ "ERROR",
+ "PRESERVE",
+ "ALLOW" };
+
+ char filename[PATH_MAX];
+ char resname[PATH_MAX];
+ char checkname[PATH_MAX];
+ char command[PATH_MAX * 3];
+ char *srcdir;
+
+ srcdir = getenv("srcdir");
+ sprintf(filename, "%s/ini/ini.d/foo.conf", (srcdir == NULL) ? "." : srcdir);
+ sprintf(resname, "%s/merge.conf", (srcdir == NULL) ? "." : srcdir);
+ sprintf(checkname, "%s/expect.conf", (srcdir == NULL) ? "." : srcdir);
+
+ error = simplebuffer_alloc(&sbobj);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
+ return error;
+ }
+
+ for (i = 0; i < 4; i++) {
+
+ INIOUT(printf("<==== Testing mode %s ====>\n", mstr[i]));
+
+ /* Create config collection */
+ error = ini_config_create(&ini_config);
+ if (error) {
+ printf("Failed to create collection. Error %d.\n", error);
+ simplebuffer_free(sbobj);
+ return error;
+ }
+
+ error = ini_config_file_open(filename,
+ INI_STOP_ON_ANY,
+ mflags[i],
+ 0, /* TBD */
+ &file_ctx);
+ if (error) {
+ printf("Failed to open file for reading. Error %d.\n", error);
+ ini_config_destroy(ini_config);
+ simplebuffer_free(sbobj);
+ return error;
+ }
+
+ error = ini_config_parse(file_ctx,
+ ini_config);
+ if (error) {
+ INIOUT(printf("Failed to parse configuration. Error %d.\n", error));
+
+ if (ini_config_error_count(file_ctx)) {
+ INIOUT(printf("Errors detected while parsing: %s\n",
+ ini_config_get_filename(file_ctx)));
+ ini_config_get_errors(file_ctx, &error_list);
+ INIOUT(ini_print_errors(stdout, error_list));
+ ini_config_free_errors(error_list);
+ }
+
+ if ((mflags[i] != INI_MV1S_ERROR) ||
+ ((mflags[i] = INI_MV1S_ERROR) && (error != EEXIST))) {
+ printf("This is unexpected error %d in mode %d\n", error, mflags[i]);
+ ini_config_destroy(ini_config);
+ simplebuffer_free(sbobj);
+ ini_config_file_close(file_ctx);
+ return error;
+ }
+ /* We do not return here intentionally */
+ }
+
+ ini_config_file_close(file_ctx);
+
+ INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT));
+
+ error = ini_config_serialize(ini_config, sbobj);
+ if (error != EOK) {
+ printf("Failed to serialize configuration. Error %d.\n", error);
+ ini_config_destroy(ini_config);
+ simplebuffer_free(sbobj);
+ return error;
+ }
+
+ ini_config_destroy(ini_config);
+ }
+
+ errno = 0;
+ ff = fopen(resname, "w");
+ if(!ff) {
+ error = errno;
+ printf("Failed to open file for writing. Error %d.\n", error);
+ simplebuffer_free(sbobj);
+ return error;
+ }
+
+ /* Save */
+ left = simplebuffer_get_len(sbobj);
+ while (left > 0) {
+ error = simplebuffer_write(fileno(ff), sbobj, &left);
+ if (error) {
+ printf("Failed to write back the configuration %d.\n", error);
+ simplebuffer_free(sbobj);
+ fclose(ff);
+ return error;
+ }
+ }
+
+ simplebuffer_free(sbobj);
+ fclose(ff);
+
+ error = create_expect(checkname);
+ if (error) {
+ printf("Failed to create file with expected contents %d.\n", error);
+ return error;
+ }
+
+ sprintf(command,"diff -q %s %s", resname, checkname);
+ error = system(command);
+ INIOUT(printf("Comparison of %s %s returned: %d\n",
+ resname, checkname, error));
+
+ return error;
+
+
+}
/* Main function of the unit test */
int main(int argc, char *argv[])
@@ -238,6 +440,7 @@ int main(int argc, char *argv[])
int error = 0;
test_fn tests[] = { read_save_test,
read_again_test,
+ merge_values_test,
NULL };
test_fn t;
int i = 0;