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)
downloadding-libs2-ec246f748f8dcbeefd46125546364ed2aa3100ea.tar.gz
ding-libs2-ec246f748f8dcbeefd46125546364ed2aa3100ea.tar.xz
ding-libs2-ec246f748f8dcbeefd46125546364ed2aa3100ea.zip
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;