From 3ca698fdc498859a6b3a45156749dc0449d31614 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Tue, 4 Dec 2012 19:38:58 -0500
Subject: [PATCH 07/10] [INI] Improve parser

1. Move the last comment into the config object
Last comment in the file was previously treated as a special
value in the last section in the configuration object.
That created difficulties while merging. Storing an orphan
comment as part of the config object is a better approach
2. Since the structure of the config object changed
the internal header and serialization required update.
3. Parser code was updated to:
- deal with the last comment on the top level
- make sure the right error codes are returned in
different modes
- pass right information through the layers which caused
some refactoring of the internal calls
- prevent using an already used configuration object
as an input to parser.
---
 ini/ini_config_priv.h |    3 +
 ini/ini_defines.h     |    3 +-
 ini/ini_parse.c       |  110 +++++++++++++++++++++++++++++++++++++------------
 ini/ini_serialize.c   |   12 +++++-
 ini/ini_valueobj.c    |    6 ---
 5 files changed, 99 insertions(+), 35 deletions(-)

diff --git a/ini/ini_config_priv.h b/ini/ini_config_priv.h
index 5d994f95a3e666d7606859f4414fecd0ea5c27ab..8b19d733afe9fb4b7ff38a5c0c2632e56c381225 100644
--- a/ini/ini_config_priv.h
+++ b/ini/ini_config_priv.h
@@ -26,6 +26,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include "collection.h"
+#include "ini_comment.h"
 
 /* Configuration object */
 struct ini_cfgobj {
@@ -33,6 +34,8 @@ struct ini_cfgobj {
     struct collection_item *cfg;
     /* Boundary */
     uint32_t boundary;
+    /* Last comment */
+    struct ini_comment *last_comment;
     /* Last search state */
     char *section;
     char *name;
diff --git a/ini/ini_defines.h b/ini/ini_defines.h
index b861f0e7e282fa8b3e20d4d3b60cb909bb1ef0d1..db898c3ea8cedf5f0aab2d8cf12d67be89ec858f 100644
--- a/ini/ini_defines.h
+++ b/ini/ini_defines.h
@@ -63,7 +63,6 @@
 #define INI_ERROR_NAME  "errname"
 #define INI_CONFIG_NAME "INI"
 
-#define INI_SPECIAL_KEY "="
 #define INI_SECTION_KEY "["
 
 /* Internal sizes. MAX_KEY is defined in config.h */
@@ -81,6 +80,8 @@
 
 /* This constant belongs here. */
 #define COL_CLASS_INI_BASE        20000
+#define COL_CLASS_INI_CONFIG      COL_CLASS_INI_BASE + 0
+
 /**
  * @brief A one level collection of parse errors.
  *
diff --git a/ini/ini_parse.c b/ini/ini_parse.c
index c61536d745fcb4035c5c4a198c3badf77ba071bc..5d393759a4407ce62d632261b1957b6ad61e10e5 100644
--- a/ini/ini_parse.c
+++ b/ini/ini_parse.c
@@ -55,6 +55,7 @@ struct parser_obj {
     struct collection_item *top;
     struct collection_item *el;
     const char *filename;
+    struct ini_cfgobj *co;
     /* Level of error reporting */
     int error_level;
     /* Collistion flags */
@@ -135,6 +136,7 @@ static void parser_destroy(struct parser_obj *po)
                              po->raw_lengths);
         if (po->last_read) free(po->last_read);
         if (po->key) free(po->key);
+        col_destroy_collection_with_cb(po->top, ini_cleanup_cb, NULL);
         free(po);
     }
 
@@ -146,25 +148,26 @@ static void parser_destroy(struct parser_obj *po)
  * It assumes that the ini collection
  * has been precreated.
  */
-static int parser_create(FILE *file,
+static int parser_create(struct ini_cfgobj *co,
+                         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)
 {
     int error = EOK;
     struct parser_obj *new_po = NULL;
+    unsigned count = 0;
 
     TRACE_FLOW_ENTRY();
 
     /* Make sure that all the parts are initialized */
     if ((!po) ||
+        (!co) ||
+        (!(co->cfg)) ||
         (!file) ||
         (!config_filename) ||
-        (!ini_config) ||
         (!error_list)) {
         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
         return EINVAL;
@@ -177,6 +180,17 @@ static int parser_create(FILE *file,
         return EINVAL;
     }
 
+    error = col_get_collection_count(co->cfg, &count);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to check object size", error);
+        return error;
+    }
+
+    if (count != 1) {
+        TRACE_ERROR_NUMBER("Configuration is not empty", EINVAL);
+        return EINVAL;
+    }
+
     new_po = malloc(sizeof(struct parser_obj));
     if (!new_po) {
         TRACE_ERROR_NUMBER("No memory", ENOMEM);
@@ -185,12 +199,12 @@ static int parser_create(FILE *file,
 
     /* Save external data */
     new_po->file = file;
-    new_po->top = ini_config;
     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;
+    new_po->boundary = co->boundary;
+    new_po->co = co;
 
     /* Initialize internal varibles */
     new_po->sec = NULL;
@@ -210,9 +224,20 @@ static int parser_create(FILE *file,
     new_po->merge_key = NULL;
     new_po->merge_vo = NULL;
     new_po->merge_error = 0;
-
-    /* Create a queue */
+    new_po->top = NULL;
     new_po->queue = NULL;
+
+    /* Create top collection */
+    error = col_create_collection(&(new_po->top),
+                                  INI_CONFIG_NAME,
+                                  COL_CLASS_INI_CONFIG);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to create top collection", error);
+        parser_destroy(new_po);
+        return error;
+    }
+
+    /* Create a queue */
     error = col_create_queue(&(new_po->queue));
     if (error) {
         TRACE_ERROR_NUMBER("Failed to create queue", error);
@@ -1260,18 +1285,27 @@ static int parser_post(struct parser_obj *po)
 
     TRACE_FLOW_ENTRY();
 
-    /* If there was just a comment at the bottom add special key */
+    /* If there was just a comment at the bottom
+     * put it directly into the config object
+     */
     if((po->ic) && (!(po->key))) {
-        po->key_len = sizeof(INI_SPECIAL_KEY) - 1;
-        po->key = strndup(INI_SPECIAL_KEY, sizeof(INI_SPECIAL_KEY));
-        /* Create new arrays */
-        error = value_create_arrays(&(po->raw_lines),
-                                    &(po->raw_lengths));
-        if (error) {
-            TRACE_ERROR_NUMBER("Failed to create arrays", error);
-            return error;
+        if (po->co->last_comment) {
+            error = ini_comment_add(po->ic, po->co->last_comment);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to merge comment", error);
+                return error;
+            }
+        }
+        else {
+            error = ini_comment_copy(po->ic, &(po->co->last_comment));
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to copy comment", error);
+                return error;
+            }
         }
 
+        ini_comment_destroy(po->ic);
+        po->ic = NULL;
     }
 
     /* If there is a key being processed add it */
@@ -1482,6 +1516,7 @@ int ini_config_parse(struct ini_cfgfile *file_ctx,
 {
     int error = EOK;
     struct parser_obj *po;
+    uint32_t fl1, fl2, fl3;
 
     TRACE_FLOW_ENTRY();
 
@@ -1490,13 +1525,12 @@ int ini_config_parse(struct ini_cfgfile *file_ctx,
         return EINVAL;
     }
 
-    error = parser_create(file_ctx->file,
+    error = parser_create(ini_config,
+                          file_ctx->file,
                           file_ctx->filename,
-                          ini_config->cfg,
                           file_ctx->error_level,
                           file_ctx->collision_flags,
                           file_ctx->error_list,
-                          ini_config->boundary,
                           &po);
     if (error) {
         TRACE_ERROR_NUMBER("Failed to perform an action", error);
@@ -1505,17 +1539,39 @@ int ini_config_parse(struct ini_cfgfile *file_ctx,
 
     error = parser_run(po);
     if (error) {
-        TRACE_ERROR_NUMBER("Failed to parse file", error);
-        col_get_collection_count(file_ctx->error_list, &(file_ctx->count));
-        if(file_ctx->count) (file_ctx->count)--;
-        parser_destroy(po);
-        return error;
+        fl1 = file_ctx->collision_flags & INI_MS_MASK;
+        fl2 = file_ctx->collision_flags & INI_MV1S_MASK;
+        fl3 = file_ctx->collision_flags & INI_MV2S_MASK;
+        if ((error == EEXIST) &&
+            (((fl1 == INI_MS_DETECT) &&
+              (fl2 != INI_MV1S_ERROR) &&
+              (fl3 != INI_MV2S_ERROR)) ||
+             ((fl2 == INI_MV1S_DETECT) &&
+              (fl1 != INI_MS_ERROR) &&
+              (fl3 != INI_MV2S_ERROR)) ||
+             ((fl3 == INI_MV2S_DETECT) &&
+              (fl1 != INI_MS_ERROR) &&
+              (fl2 != INI_MV1S_ERROR)))) {
+            TRACE_ERROR_NUMBER("No error in detect mode", error);
+            /* Fall through */
+        }
+        else {
+            TRACE_ERROR_NUMBER("Failed to parse file", error);
+            TRACE_ERROR_NUMBER("Mode", file_ctx->collision_flags);
+            col_get_collection_count(file_ctx->error_list, &(file_ctx->count));
+            if(file_ctx->count) (file_ctx->count)--;
+            parser_destroy(po);
+            return error;
+        }
     }
 
+    /* If should be empty anyways */
+    col_destroy_collection_with_cb(ini_config->cfg, ini_cleanup_cb, NULL);
+    ini_config->cfg = po->top;
+    po->top = NULL;
+
     parser_destroy(po);
 
-
-    TRACE_INFO_NUMBER("Count returned:", error);
     TRACE_FLOW_EXIT();
     return error;
 }
diff --git a/ini/ini_serialize.c b/ini/ini_serialize.c
index 177979a04cde607a554113ca74027f3197f4fd9c..7a53f29e4d12fd49c872fd321daf0e0ea65e2b94 100644
--- a/ini/ini_serialize.c
+++ b/ini/ini_serialize.c
@@ -79,9 +79,19 @@ int ini_config_serialize(struct ini_cfgobj *ini_config,
                                         COL_TRAVERSE_DEFAULT,
                                         ini_serialize_cb,
                                         (void *)sbobj);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to serialize collection", error);
+            return error;
+        }
     }
 
-    TRACE_INFO_NUMBER("Serialization returned:", error);
+    if (ini_config->last_comment) {
+        error = ini_comment_serialize(ini_config->last_comment, sbobj);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed serialize comment", error);
+            return error;
+        }
+    }
 
     TRACE_FLOW_EXIT();
     return error;
diff --git a/ini/ini_valueobj.c b/ini/ini_valueobj.c
index e0c8d9861ec71ccd6b9889dbda37bda67aa9220d..6d4dc1fcad029f92ad065e58b140f8e9940f2a0b 100644
--- a/ini/ini_valueobj.c
+++ b/ini/ini_valueobj.c
@@ -967,12 +967,6 @@ int value_serialize(struct value_obj *vo,
         }
     }
 
-    if (strncmp(key, INI_SPECIAL_KEY, sizeof(INI_SPECIAL_KEY)) == 0) {
-        /* Special key carries only a comment */
-        TRACE_FLOW_EXIT();
-        return EOK;
-    }
-
     /* Handle the case it is a section key */
     if (strncmp(key,
                 INI_SECTION_KEY,
-- 
1.7.1

