From 43ecc8fdb9d04920f91c68505f4c64f9501a2674 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Fri, 26 Nov 2010 23:11:07 -0500
Subject: [PATCH] [INI] New copy and folding functionality

* Added method to copy configuration.
* Added unit test for it.
* Added method to reset boandary for the
  whole configuration.
* Added unit test for it.
* Unit test now can read a file
  save it read again and save.
  Both saves produce same files!
* Reworked the way the parser was dealing
  with empty lines. It was sutting
  off the value in the middle
  if the folded line consisted of
  just spaces.
---
 ini/ini_configobj.c |  141 +++++++++++++++++++++++++++++++++++++++++++++++++++
 ini/ini_configobj.h |    4 ++
 ini/ini_parse.c     |   74 ++++++++++++++-------------
 ini/ini_parse_ut.c  |  129 +++++++++++++++++++++++++++++++++--------------
 4 files changed, 274 insertions(+), 74 deletions(-)

diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c
index 4da9a7cf7c45552ee3322ad8644709a7fd3ef3da..3e5dec4733a49d03e44185637592f0efdbe7db3c 100644
--- a/ini/ini_configobj.c
+++ b/ini/ini_configobj.c
@@ -113,3 +113,144 @@ int ini_config_create(struct ini_cfgobj **ini_config)
     TRACE_FLOW_EXIT();
     return error;
 }
+
+/* Callback to set the boundary */
+int ini_boundary_cb(const char *property,
+                     int property_len,
+                     int type,
+                     void *data,
+                     int length,
+                     void *custom_data,
+                     int *dummy)
+{
+    int error = EOK;
+    struct value_obj *vo = NULL;
+    uint32_t boundary;
+
+    TRACE_FLOW_ENTRY();
+
+    boundary = *((uint32_t *)(custom_data));
+    /* Banary items are the values */
+    if(type == COL_TYPE_BINARY) {
+        vo = *((struct value_obj **)(data));
+        error = value_set_boundary(vo, boundary);
+    }
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Set the folding boundary for multiline values.
+ * Use before serializing and saving to a file if the
+ * default boundary of 80 characters does not work for you.
+ */
+int ini_config_set_wrap(struct ini_cfgobj *ini_config,
+                        uint32_t boundary)
+{
+    int error = EOK;
+
+    TRACE_FLOW_ENTRY();
+
+    if (!ini_config) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+    ini_config->boundary = boundary;
+    error = col_traverse_collection(ini_config->cfg,
+                                    COL_TRAVERSE_DEFAULT,
+                                    ini_boundary_cb,
+                                    (void *)(&(ini_config->boundary)));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to set wrapping boundary", error);
+        return error;
+    }
+
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Configuration copy callback */
+static int ini_copy_cb(struct collection_item *item,
+                       void *ext_data,
+                       int *skip)
+{
+    int error = EOK;
+    struct value_obj *vo = NULL;
+    struct value_obj *new_vo = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ext_data = NULL;
+    *skip = 0;
+
+    /* Banary items are the values */
+    if(col_get_item_type(item) == COL_TYPE_BINARY) {
+        vo = *((struct value_obj **)(col_get_item_data(item)));
+
+        error = value_copy(vo, &new_vo);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to copy value", error);
+            return error;
+        }
+
+        error = col_modify_binary_item(item,
+                                       NULL,
+                                       &new_vo,
+                                       sizeof(struct value_obj *));
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to copy value", error);
+            value_destroy(new_vo);
+            return error;
+        }
+    }
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Copy configuration */
+int ini_config_copy(struct ini_cfgobj *ini_config,
+                    struct ini_cfgobj **ini_new)
+{
+    int error = EOK;
+    struct ini_cfgobj *new_co;
+
+    TRACE_FLOW_ENTRY();
+
+    if ((!ini_config) ||
+        (!ini_new)) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+    /* Create a new configuration object */
+    errno = 0;
+    new_co = malloc(sizeof(struct ini_cfgobj));
+    if (!new_co) {
+        error = errno;
+        TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
+        return ENOMEM;
+    }
+
+    new_co->cfg = NULL;
+    new_co->boundary = ini_config->boundary;
+
+    error = col_copy_collection_with_cb(&(new_co->cfg),
+                                        ini_config->cfg,
+                                        INI_CONFIG_NAME,
+                                        COL_COPY_NORMAL,
+                                        ini_copy_cb,
+                                        NULL);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to copy collection", error);
+        ini_config_destroy(new_co);
+        return error;
+    }
+
+    *ini_new = new_co;
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
index 81578c0c442896f69d62c4ba2c56cbd9523228f3..21198db064c44acedd7fd83664d01b112ddac344 100644
--- a/ini/ini_configobj.h
+++ b/ini/ini_configobj.h
@@ -138,6 +138,10 @@ void ini_config_free_errors(char **errors);
 int ini_config_parse(struct ini_cfgfile *file_ctx,
                      struct ini_cfgobj *ini_config);
 
+/* Copy configuration */
+int ini_config_copy(struct ini_cfgobj *ini_config,
+                    struct ini_cfgobj **ini_new);
+
 /* Function to print errors from the list */
 void ini_print_errors(FILE *file, char **error_list);
 
diff --git a/ini/ini_parse.c b/ini/ini_parse.c
index 253b78bc6830f1622ed1c240a7ae8bb091c4f7a8..c92a1633e3aee20227f2c0596bd9b27ae27fe809 100644
--- a/ini/ini_parse.c
+++ b/ini/ini_parse.c
@@ -79,6 +79,20 @@ typedef int (*action_fn)(struct parser_obj *);
 #define PARSE_DONE      4 /* We are done */
 
 
+int is_just_spaces(const char *str, uint32_t len)
+{
+    uint32_t i;
+
+    TRACE_FLOW_ENTRY();
+
+    for (i = 0; i < len; i++) {
+        if (!isspace(str[i])) return 0;
+    }
+
+    TRACE_FLOW_EXIT();
+    return 1;
+}
+
 
 /* Destroy parser object */
 void parser_destroy(struct parser_obj *po)
@@ -401,14 +415,24 @@ static int handle_space(struct parser_obj *po, uint32_t *action)
         *action = PARSE_READ;
     }
     else {
-        /* We do not have an active value
-         * but have a line is starting with a space.
-         * For now it is error.
-         * We can change it in future if
-         * people find it being too restrictive
-         */
-        *action = PARSE_ERROR;
-        po->last_error = ERR_SPACE;
+        /* Check if this is a completely empty line */
+        if (is_just_spaces(po->last_read, po->last_read_len)) {
+            error = handle_comment(po, action);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to process comment", error);
+                return error;
+            }
+        }
+        else {
+            /* We do not have an active value
+             * but have a line is starting with a space.
+             * For now it is error.
+             * We can change it in future if
+             * people find it being too restrictive
+             */
+            *action = PARSE_ERROR;
+            po->last_error = ERR_SPACE;
+        }
     }
 
     TRACE_FLOW_EXIT();
@@ -425,6 +449,8 @@ static int handle_kvp(struct parser_obj *po, uint32_t *action)
 
     TRACE_FLOW_ENTRY();
 
+    TRACE_INFO_STRING("Last read:", po->last_read);
+
     /* We got a line with KVP */
     if (*(po->last_read) == '=') {
         po->last_error = ERR_NOKEY;
@@ -681,20 +707,6 @@ static int handle_section(struct parser_obj *po, uint32_t *action)
 
 }
 
-int is_just_spaces(const char *str, uint32_t len)
-{
-    uint32_t i;
-
-    TRACE_FLOW_ENTRY();
-
-    for (i = 0; i < len; i++) {
-        if (!isspace(str[i])) return 0;
-    }
-
-    TRACE_FLOW_EXIT();
-    return 1;
-}
-
 /* Inspect the line */
 static int parser_inspect(struct parser_obj *po)
 {
@@ -716,20 +728,10 @@ static int parser_inspect(struct parser_obj *po)
     else if ((*(po->last_read) == ' ') ||
              (*(po->last_read) == '\t')) {
 
-        /* Check if this is a completely empty line */
-        if (is_just_spaces(po->last_read, po->last_read_len)) {
-            error = handle_comment(po, &action);
-            if (error) {
-                TRACE_ERROR_NUMBER("Failed to process comment", error);
-                return error;
-            }
-        }
-        else {
-            error = handle_space(po, &action);
-            if (error) {
-                TRACE_ERROR_NUMBER("Failed to process line wrapping", error);
-                return error;
-            }
+        error = handle_space(po, &action);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to process line wrapping", error);
+            return error;
         }
     }
     else if (*(po->last_read) == '[') {
diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
index 01b70776413dd717a3e6085c32f45a4137b6c0ae..cc37c21936db40a1ca16d497af3bd261a241bca5 100644
--- a/ini/ini_parse_ut.c
+++ b/ini/ini_parse_ut.c
@@ -23,13 +23,16 @@
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <limits.h>
 #include "ini_defines.h"
 #include "ini_configobj.h"
+#include "ini_config_priv.h"
 #include "simplebuffer.h"
 #include "path_utils.h"
 #include "config.h"
 #define TRACE_HOME
 #include "trace.h"
+#include "collection_tools.h"
 
 int verbose = 0;
 char *confdir = NULL;
@@ -41,19 +44,19 @@ char *confdir = NULL;
 
 typedef int (*test_fn)(void);
 
-int test_one_file(const char *filename)
+int test_one_file(const char *in_filename,
+                  const char *out_filename)
 {
     int error = EOK;
     struct ini_cfgfile *file_ctx = NULL;
     FILE *ff = NULL;
-    char new_file[100];
     struct ini_cfgobj *ini_config = NULL;
+    struct ini_cfgobj *ini_copy = NULL;
     char **error_list = NULL;
     struct simplebuffer *sbobj = NULL;
     uint32_t left = 0;
-    char filename_base[96];
 
-    INIOUT(printf("<==== Testing file %s ====>\n", filename));
+    INIOUT(printf("<==== Testing file %s ====>\n", in_filename));
 
     /* Create config collection */
     error = ini_config_create(&ini_config);
@@ -62,7 +65,7 @@ int test_one_file(const char *filename)
         return error;
     }
 
-    error = ini_config_file_open(filename,
+    error = ini_config_file_open(in_filename,
                                  INI_STOP_ON_NONE,
                                  0, /* TBD */
                                  0, /* TBD */
@@ -83,12 +86,35 @@ int test_one_file(const char *filename)
                    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);
         }
         /* We do not return here intentionally */
     }
 
     ini_config_file_close(file_ctx);
 
+    INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT));
+
+    /* Copy configuration */
+    error = ini_config_copy(ini_config, &ini_copy);
+    if (error) {
+        printf("Failed to copy configuration. Error %d.\n", error);
+        ini_config_destroy(ini_config);
+        return error;
+    }
+
+    ini_config_destroy(ini_config);
+    ini_config = ini_copy;
+
+    INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT));
+
+    error = ini_config_set_wrap(ini_config, 5);
+    if (error) {
+        printf("Failed to set custom wrapper. Error %d.\n", error);
+        ini_config_destroy(ini_config);
+        return error;
+    }
+
     error = simplebuffer_alloc(&sbobj);
     if (error) {
         TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
@@ -104,11 +130,8 @@ int test_one_file(const char *filename)
         return error;
     }
 
-    error = get_basename(filename_base, 96, filename);
-    sprintf(new_file, "%s.out", filename_base);
-
     errno = 0;
-    ff = fopen(new_file, "w");
+    ff = fopen(out_filename, "w");
     if(!ff) {
         error = errno;
         printf("Failed to open file for writing. Error %d.\n", error);
@@ -143,41 +166,79 @@ int read_save_test(void)
 {
     int error = EOK;
     int i = 0;
-    int lasterr = EOK;
-    char *files[5];
-
-    files[0] = malloc(sizeof(char)*512);
-    sprintf(files[0], "%s/ini.d/real.conf", confdir);
-    files[1] = malloc(sizeof(char)*512);
-    sprintf(files[1], "%s/ini.d/mysssd.conf", confdir);
-    files[2] = malloc(sizeof(char)*512);
-    sprintf(files[2], "%s/ini.d/ipa.conf", confdir);
-    files[3] = malloc(sizeof(char)*512);
-    sprintf(files[3], "%s/ini.d/test.conf", confdir);
-    files[4] = NULL;
+    char infile[PATH_MAX];
+    char outfile[PATH_MAX];
+    char *srcdir;
+    const char *files[] = { "real",
+                            "mysssd",
+                            "ipa",
+                            "test",
+                            NULL };
+
+
+    srcdir = getenv("srcdir");
 
     while(files[i]) {
-        error = test_one_file(files[i]);
-        INIOUT(printf("Test fo file: %s returned %d\n", files[i], error));
-        if (error) lasterr = error;
+
+        sprintf(infile, "%s/ini/ini.d/%s.conf", (srcdir == NULL) ? "." : srcdir,
+                                                files[i]);
+        sprintf(outfile, "%s/%s.conf.out", (srcdir == NULL) ? "." : srcdir,
+                                           files[i]);
+        error = test_one_file(infile, outfile);
+        INIOUT(printf("Test for file: %s returned %d\n", files[i], error));
         i++;
     }
 
-    free(files[3]);
-    free(files[2]);
-    free(files[1]);
-    free(files[0]);
+    return EOK;
+}
+
+/* Run tests for multiple files */
+int read_again_test(void)
+{
+    int error = EOK;
+    int i = 0;
+    char infile[PATH_MAX];
+    char outfile[PATH_MAX];
+    char *srcdir;
+    char command[PATH_MAX * 3];
+    const char *files[] = { "real",
+                            "mysssd",
+                            "ipa",
+                            "test",
+                            NULL };
+
 
-    return lasterr;
+    srcdir = getenv("srcdir");
+
+    while(files[i]) {
+
+        sprintf(infile, "%s/%s.conf.out", (srcdir == NULL) ? "." : srcdir,
+                                          files[i]);
+        sprintf(outfile, "%s/%s.conf.2.out", (srcdir == NULL) ? "." : srcdir,
+                                             files[i]);
+        error = test_one_file(infile, outfile);
+        INIOUT(printf("Test for file: %s returned %d\n", files[i], error));
+        if (error) break;
+        sprintf(command,"diff -q %s %s", infile, outfile);
+        error = system(command);
+        INIOUT(printf("Comparison of %s %s returned: %d\n",
+                      infile, outfile, error));
+        if (error) break;
+
+        i++;
+    }
+
+    return error;
 }
 
+
 /* Main function of the unit test */
 int main(int argc, char *argv[])
 {
     int error = 0;
     test_fn tests[] = { read_save_test,
+                        read_again_test,
                         NULL };
-    char *srcdir;
     test_fn t;
     int i = 0;
     char *var;
@@ -190,14 +251,6 @@ int main(int argc, char *argv[])
 
     INIOUT(printf("Start\n"));
 
-    srcdir = getenv("srcdir");
-    if(!srcdir) {
-        confdir = strdup("./ini");
-    } else {
-        confdir = malloc(strlen(srcdir)+5*sizeof(char));
-        sprintf(confdir, "%s/ini", srcdir);
-    }
-
     while ((t = tests[i++])) {
         error = t();
         if (error) {
-- 
1.5.5.6

