[SSSD] [INI] Patches for ding-libs: Merging config sections, handling metadata, remaining Coverity issues...

Dmitri Pal dpal at redhat.com
Sun Mar 18 21:38:19 UTC 2012


On 01/24/2011 12:27 PM, Sumit Bose wrote:


The numeration changed. But the ste is the same. The patches in this
mail are dependent on the patches sent in the previous email on Friday.
>> Patches related porting of the meta data from old way of doing things to
>> the new way of doing things:
>> 0017--INI-Separate-close-and-destroy.patch
> You should set file_ctx->file to NULL after fclose(file_ctx->file) to
> make the if(file_ctx->file) checks work in ini_config_file_close() and
> ini_config_file_destroy().
>

Yes it was fixed in a different patch. Now merged together.

> There are tab indents in merge_values_test() and merge_section_test().

Fixed.
>> 0018--INI-Function-to-reopen-file.patch

Unchanged

>> 0019--INI-Metadata-collection-is-gone.patch
> You remove metadata from struct ini_cfgfile without removing all
> references to metadata in the same patch. You should make clear that
> more patches are needed to create a buildable version of libini or
> remove all references in this patch.

This is not true. The buildable version is still preserved.
All the code that I am building is not executed yet.
It is a parallel interface. There are currently two interfaces:
ini_config.h and ini_configobj.h
The external code is still suing the ini_config.h.
The new interface that the UT are executing is pointing to the interface
growing in ini_configobj.h.
This metadata functions of the existing ini_config interface are not
affected.
After some thinking I realized that there is too much overhead for doing
the metadata handling the way I did it in the first implementation. The
new interface corrects this but does not affect the old code yet. So the
metadata collection is gone from the new interface not from the old one.
The code compiles fine after this patch and the three patches right
after it take advantage of this change. Patches 18-21 should be
considered together as a block of related patches.


> I wonder is the following is a change of defaults. With the patch the
> new file_ctx->file_stats are only set if INI_META_STATS is set while
> previously file-ctx>metadata was set unconditionally.

Fixed by memset.
>> 0020--INI-Check-access-function.patch
This is now 19.
> I wonder if it is necessary to return EINVAL if flags == 0. I would say
> in this case no checks are requested and EOK could be returned?
I disagree with this one. With flags 0 I think it is a noop and might
lead to the wrong assumptions while no operation was actually performed.
IMO passing flags=0 is a codding error in this case and thus should be
reported as such.

> I would prefer to copy file_ctx->file_stats.st_mode instead of modifying
> it, e.g. you might want to know the file type later on.

Good catch. Agree. Fixed.
>> 0021--INI-Avoid-double-free.patch <- patch related to 17 (missed check)
> oops, so you can ignore my comment to 00017, let's see if there is also
> a patch for ini_config_file_destroy(). "I might squash this patch into
> one of the previous ones." Yes, please.
>

This one is merged now.
>> 0022--INI-Function-to-check-for-changes.patch

This is now 20 and unchanged.

>> 0023--INI-Tests-for-access-and-changes.patch
This is now 21.

> Why do you need sleep(1) ?

Removed.
> The man page of system() does not mention that system() sets errno,
> please check the return code instead.

Changed to use execlp().
>> 0024--INI-Rename-error-print-function.patch <- rename error printing
>> function for consistency with new interface
>>
This is now 22.

> Maybe ini_print_errors() should print a deprecated warning to make
> migrations easier.

As I mentioned above there is no impact on the existing interface.
As you notice I made a clone of the existing function with a different
name and made a comment to clean the old function when the interface is
deprecated.
The changes will be made later when we switch to new interface, so it is
premature to print a warning as this function is still used.

>> 0025--INI-Initialize-variables-in-loops.patch <- Coverity issue
>> addressed. Related to patch 0001.

This is 23 and merged with another initialization one liner patch.

>> 0026--INI-Exposing-functions.patch <- Make some internal functions reusable

This is 24 and unchanged.

>>  
>> There is also patch 27. It is a piece of new functionality. It is a
>> preview. Please see the comment before reviewing it.
>> Do I need to split it into multiple patches or it is Ok as is? It is
>> pretty big but all changes are in one file and logically related.
>> The UNIT test is missing so I am not claiming it actually works as
>> expected.
> I didn't had a look at 0027 so far.


I am going to hold to patch 27 yet. I have other higher priority issues
to address.


Please review and nack/push this ASAP.
I need to clean ding libs and elapi for the audit effort ASAP.
There are too many patches piling to be able to move forward
effectively. I am the bottleneck now and i need to bring all this into a
usable state within a week or so. Please help!


One last thing. Should I switch to the format used in freeipa?
https://fedorahosted.org/freeipa/wiki/PatchFormat
It seems that SSSD/ding-libs does not follow this format. Should it? Not
insisting, just asking.

 
> bye,
> Sumit
>
>> -- 
>> Thank you,
>> Dmitri Pal
>>
>> Sr. Engineering Manager IPA project,
>> Red Hat Inc.
>>
>>
>> -------------------------------
>> Looking to carve out IT costs?
>> www.redhat.com/carveoutcosts/
>>
>> From 2cb4d6ad0bfad2170e09152379f29de2b9c29196 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Fri, 24 Dec 2010 00:38:48 -0500
>> Subject: [PATCH] [INI] Making Coverity happy
>>
>> Coverity found issues 10078 & 10079. This patch
>> should make it happy, however I think that Coverity
>> is wrong unless I am missing something. If I am
>> I hope that this patch would be able to reveal the
>> real issue if any.
>> For now just renaming a variable.
>>
>> [INI] Added missing initialization
>>
>> One liner to initialize a variable.
>> ---
>>  ini/ini_configobj.c   |    2 +-
>>  ini/ini_valueobj_ut.c |    9 +++++----
>>  2 files changed, 6 insertions(+), 5 deletions(-)
>>
>> diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c
>> index 3e5dec4733a49d03e44185637592f0efdbe7db3c..5e5e40b566abdf3ce2cbc225babe4b1e20c2dd47 100644
>> --- a/ini/ini_configobj.c
>> +++ b/ini/ini_configobj.c
>> @@ -215,7 +215,7 @@ int ini_config_copy(struct ini_cfgobj *ini_config,
>>                      struct ini_cfgobj **ini_new)
>>  {
>>      int error = EOK;
>> -    struct ini_cfgobj *new_co;
>> +    struct ini_cfgobj *new_co = NULL;
>>  
>>      TRACE_FLOW_ENTRY();
>>  
>> diff --git a/ini/ini_valueobj_ut.c b/ini/ini_valueobj_ut.c
>> index 767a64cc2fe624800bed7e9dc4b53a90fa2d59c2..af62c140d215f9298dd5671d8aa544a84ef254b1 100644
>> --- a/ini/ini_valueobj_ut.c
>> +++ b/ini/ini_valueobj_ut.c
>> @@ -378,6 +378,7 @@ int vo_basic_test(void)
>>       */
>>  
>>      struct value_obj *vo = NULL;
>> +    struct value_obj *other_vo = NULL;
>>      uint32_t wrap = 0;
>>      struct ini_comment *ic = NULL;
>>      FILE *ff = NULL;
>> @@ -429,7 +430,7 @@ int vo_basic_test(void)
>>      }
>>  
>>      /* Run other create test here */
>> -    error = other_create_test(ff, &vo);
>> +    error = other_create_test(ff, &other_vo);
>>      if (error) {
>>          printf("Create test failed %d.\n", error);
>>          fclose(ff);
>> @@ -437,15 +438,15 @@ int vo_basic_test(void)
>>      }
>>  
>>      /* Run modify test here */
>> -    error = modify_test(ff, vo);
>> +    error = modify_test(ff, other_vo);
>>      if (error) {
>>          printf("Modify test failed %d.\n", error);
>>          fclose(ff);
>> -        value_destroy(vo);
>> +        value_destroy(other_vo);
>>          return error;
>>      }
>>  
>> -    value_destroy(vo);
>> +    value_destroy(other_vo);
>>  
>>  
>>      ic = NULL;
>> -- 
>> 1.5.5.6
>>
>> From 68ecd60fac51706c743a5c4a4cfe87ec6ca9d304 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Fri, 24 Dec 2010 21:27:24 -0500
>> Subject: [PATCH] [INI] Adding missing function declararion.
>>
>> One-liner that eliminates compilation warning.
>>
>> [INI] Include proper header
>>
>> Value object needs its own header file.
>> ---
>>  ini/ini_valueobj.c |    1 +
>>  1 files changed, 1 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_valueobj.c b/ini/ini_valueobj.c
>> index a90fa451468aecc6c04f4411675ab67613a59cbe..f196c1ab3195db7cbe616e6e1275c656919fe156 100644
>> --- a/ini/ini_valueobj.c
>> +++ b/ini/ini_valueobj.c
>> @@ -29,6 +29,7 @@
>>  #include "ini_comment.h"
>>  #include "ini_defines.h"
>>  #include "trace.h"
>> +#include "ini_valueobj.h"
>>  
>>  struct value_obj {
>>      struct ref_array *raw_lines;
>> -- 
>> 1.5.5.6
>>
>> From 574be6d8c0896c8bee0c16224f52c2dbea48a44e Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Fri, 24 Dec 2010 22:38:42 -0500
>> Subject: [PATCH] [BUILD] Allow trace per component
>>
>> This patch add ability to build tracing
>> for each component independently.
>> ---
>>  Makefile.am  |   50 ++++++++++++++++++++++++++++++++++++++++++++++----
>>  configure.ac |   15 ++++++++++++++-
>>  2 files changed, 60 insertions(+), 5 deletions(-)
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index 0946169308bab6585c93cca36d45b360b565f6b5..1780e9b234e68006af910413b3e8312c9db72c7b 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>> @@ -1,4 +1,7 @@
>> -TRACE_LEVEL=@TRACE_VAR@
>> +TRACE_INI=@TRACE_INI@
>> +TRACE_COLLECTION=@TRACE_COLLECTION@
>> +TRACE_REFARRAY=@TRACE_REFARRAY@
>> +TRACE_BASICOBJECTS=@TRACE_BASICOBJECTS@
>>  
>>  RPMBUILD ?= $(PWD)/rpmbuild
>>  
>> @@ -28,8 +31,7 @@ AM_CPPFLAGS = \
>>      -I$(srcdir)/ini \
>>      -I$(srcdir)/basicobjects \
>>      -I$(srcdir) \
>> -    -I$(srcdir)/trace \
>> -    $(TRACE_LEVEL)
>> +    -I$(srcdir)/trace
>>  
>>  ACLOCAL_AMFLAGS = -I m4
>>  
>> @@ -132,6 +134,10 @@ libcollection_la_SOURCES = \
>>  libcollection_la_LDFLAGS = \
>>      -version-info 3:0:0
>>  
>> +libcollection_la_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_COLLECTION)
>> +
>>  check_PROGRAMS += \
>>      collection_ut \
>>      collection_stack_ut \
>> @@ -142,11 +148,21 @@ TESTS += \
>>      collection_queue_ut
>>  
>>  collection_ut_SOURCES = collection/collection_ut.c
>> +collection_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_COLLECTION)
>> +
>>  collection_ut_LDADD = libcollection.la
>>  collection_stack_ut_SOURCES = collection/collection_stack_ut.c
>>  collection_stack_ut_LDADD = libcollection.la
>> +collection_stack_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_COLLECTION)
>>  collection_queue_ut_SOURCES = collection/collection_queue_ut.c
>>  collection_queue_ut_LDADD = libcollection.la
>> +collection_queue_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_COLLECTION)
>>  
>>  collection-docs:
>>  if HAVE_DOXYGEN
>> @@ -167,10 +183,15 @@ libref_array_la_SOURCES = \
>>      trace/trace.h
>>  libref_array_la_LDFLAGS = \
>>      -version-info 1:0:0
>> -
>> +libref_array_la_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_REFARRAY)
>>  check_PROGRAMS += ref_array_ut
>>  TESTS += ref_array_ut
>>  ref_array_ut_SOURCES = refarray/ref_array_ut.c
>> +ref_array_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_REFARRAY)
>>  ref_array_ut_LDADD = libref_array.la
>>  
>>  dist_doc_DATA += refarray/README.ref_array
>> @@ -194,11 +215,17 @@ libbasicobjects_la_SOURCES = \
>>      trace/trace.h
>>  libbasicobjects_la_LDFLAGS = \
>>      -version-info 1:0:0
>> +libbasicobjects_la_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_BASICOBJECTS)
>>  
>>  check_PROGRAMS += simplebuffer_ut
>>  TESTS += simplebuffer_ut
>>  simplebuffer_ut_SOURCES = basicobjects/simplebuffer_ut.c
>>  simplebuffer_ut_LDADD = libbasicobjects.la
>> +simplebuffer_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_BASICOBJECTS)
>>  
>>  basicobjects-docs:
>>  if HAVE_DOXYGEN
>> @@ -243,6 +270,9 @@ libini_config_la_LIBADD = \
>>      libbasicobjects.la
>>  libini_config_la_LDFLAGS = \
>>      -version-info 3:0:0
>> +libini_config_la_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_INI)
>>  
>>  dist_noinst_DATA += \
>>      ini/ini.conf \
>> @@ -268,15 +298,27 @@ ini_config_ut_SOURCES = ini/ini_config_ut.c
>>  ini_config_ut_LDADD = \
>>      libini_config.la \
>>      libcollection.la
>> +ini_config_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_INI)
>>  
>>  ini_comment_ut_SOURCES = ini/ini_comment_ut.c
>>  ini_comment_ut_LDADD = libini_config.la
>> +ini_comment_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_INI)
>>  
>>  ini_valueobj_ut_SOURCES = ini/ini_valueobj_ut.c
>>  ini_valueobj_ut_LDADD = libini_config.la
>> +ini_valueobj_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_INI)
>>  
>>  ini_parse_ut_SOURCES = ini/ini_parse_ut.c
>>  ini_parse_ut_LDADD = libini_config.la
>> +ini_parse_ut_CFLAGS = \
>> +    $(AM_CFLAGS) \
>> +    $(TRACE_INI)
>>  
>>  ini_config-docs:
>>  if HAVE_DOXYGEN
>> diff --git a/configure.ac b/configure.ac
>> index 2e22e8a6aaac271dfa4841c17f98411967e09fd9..d1b68f5fd76654e899738f6c8a29b516a23592c5 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -39,11 +39,24 @@ AC_PATH_PROG([DOXYGEN], [doxygen], [false])
>>  AM_CONDITIONAL([HAVE_DOXYGEN], [test x$DOXYGEN != xfalse ])
>>  
>>  # Enable trace build
>> +# To turn on the tracing of one of the componenets one has not only
>> +# to enable it with --enable-trace but to drop a file named "trace"
>> +# into a corresponding directory.
>> +# Tracing can be built for collection, ini, refarray or basicobjects.
>> +# The approach allows building different libraries with trace
>> +# independent of each other.
>>  AC_ARG_ENABLE([trace],
>>                [AS_HELP_STRING([--enable-trace[=LEVEL]],[build with low level tracing enabled])],
>>                [trace_level="$enableval"],
>>                [trace_level="0"])
>> -AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TRACE_VAR],["-DTRACE_LEVEL=$trace_level"])])
>> +AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] -a [ -e $srcdir/ini/trace] ],
>> +      [AC_SUBST([TRACE_INI],["-DTRACE_LEVEL=$trace_level"])])
>> +AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] -a [ -e $srcdir/collection/trace] ],
>> +      [AC_SUBST([TRACE_COLLECTION],["-DTRACE_LEVEL=$trace_level"])])
>> +AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] -a [ -e $srcdir/refarray/trace] ],
>> +      [AC_SUBST([TRACE_REFARRAY],["-DTRACE_LEVEL=$trace_level"])])
>> +AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] -a [ -e $srcdir/basicobjects/trace] ],
>> +      [AC_SUBST([TRACE_BASICOBJECTS],["-DTRACE_LEVEL=$trace_level"])])
>>  
>>  AC_CHECK_SIZEOF([long])
>>  AC_CHECK_SIZEOF([long long])
>> -- 
>> 1.5.5.6
>>
>> From 6a66a986e962554b161ddb46f8b1e85a7c791e27 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sat, 25 Dec 2010 23:24:35 -0500
>> Subject: [PATCH] [INI] New error codes and messages
>>
>> Patch consists of two parts:
>> * Adding new constants for error messages to ini_configobj.h file
>> * Making ini_print.c use this header file instead of the old one.
>> ---
>>  ini/ini_configobj.h |   12 ++++++----
>>  ini/ini_print.c     |   54 +++++++++++++++++++++++++++++++++++++++++++++++---
>>  2 files changed, 57 insertions(+), 9 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index 88a8704277facb0f8883b2df28a709b26ae311f7..36558a701a86f6f656f2d0e3b155b9d901177fa7 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -72,13 +72,15 @@
>>  #define ERR_READ            8
>>  /** @brief Line starts with space when it should not (Error). */
>>  #define ERR_SPACE           9
>> +/** @brief Duplicate key is not allowed (Error). */
>> +#define ERR_DUPKEY          10
>> +/** @brief Duplicate key is detected while merging sections (Error). */
>> +#define ERR_DUPKEYSEC       11
>> +/** @brief Duplicate section is not allowed (Error). */
>> +#define ERR_DUPSECTION      12
>>  
>>  /** @brief Size of the error array. */
>> -#define ERR_MAXPARSE        ERR_SPACE
>> -
>> -/**
>> - * @}
>> - */
>> +#define ERR_MAXPARSE        ERR_DUPSECTION
>>  
>>  
>>  /**
>> diff --git a/ini/ini_print.c b/ini/ini_print.c
>> index 0430976cd56e89313cb386b99ad387263498c639..1dcfa54bfd72daf2c790d04c37ff2b3107eb7295 100644
>> --- a/ini/ini_print.c
>> +++ b/ini/ini_print.c
>> @@ -31,7 +31,8 @@
>>  #include "collection.h"
>>  #include "collection_tools.h"
>>  #include "ini_defines.h"
>> -#include "ini_config.h"
>> +#include "ini_config_priv.h"
>> +#include "ini_configobj.h"
>>  
>>  
>>  /*============================================================*/
>> @@ -42,6 +43,16 @@
>>   * check that the class IDs did not get reused over time by
>>   * other classes.
>>   */
>> +/**
>> + * @brief Collection of error collections.
>> + *
>> + * When multiple files are read during one call
>> + * each file has its own set of parsing errors
>> + * and warnings. This is the collection
>> + * of such sets.
>> + */
>> +#define COL_CLASS_INI_PESET       COL_CLASS_INI_BASE + 3
>> +
>>  /** @brief Collection of grammar errors.
>>   *
>>   * Reserved for future use.
>> @@ -53,6 +64,37 @@
>>   */
>>  #define COL_CLASS_INI_VERROR      COL_CLASS_INI_BASE + 6
>>  
>> +/**
>> + * @}
>> + */
>> +
>> +/**
>> + * @defgroup gramerr Grammar errors and warnings
>> + *
>> + * Placeholder for now. Reserved for future use.
>> + *
>> + * @{
>> + */
>> +#define ERR_MAXGRAMMAR      0
>> +/**
>> + * @}
>> + */
>> +
>> +/**
>> + * @defgroup valerr Validation errors and warnings
>> + *
>> + * Placeholder for now. Reserved for future use.
>> + *
>> + * @{
>> + */
>> +#define ERR_MAXVALID        0
>> +
>> +
>> +/**
>> + * @}
>> + */
>> +
>> +
>>  #ifdef HAVE_VALIDATION
>>  
>>  /** @brief Collection of lines from the INI file.
>> @@ -78,7 +120,11 @@ static const char *parsing_error_str(int parsing_error)
>>                                  _("Property name is too long."),
>>                                  _("Failed to read line."),
>>                                  _("Invalid space character at the "
>> -                                  "beginning of the line.")
>> +                                  "beginning of the line."),
>> +                                _("Duplicate key is not allowed."),
>> +                                _("Duplicate key is detected while "
>> +                                  "merging sections."),
>> +                                _("Duplicate section is not allowed.")
>>      };
>>  
>>      /* Check the range */
>> @@ -208,7 +254,7 @@ static void print_error_list(FILE *file,
>>      struct collection_iterator *iterator;
>>      int error;
>>      struct collection_item *item = NULL;
>> -    struct parse_error *pe;
>> +    struct ini_parse_error *pe;
>>      unsigned int count;
>>  
>>      TRACE_FLOW_STRING("print_error_list", "Entry");
>> @@ -256,7 +302,7 @@ static void print_error_list(FILE *file,
>>          }
>>          else {
>>              /* Put error into provided format */
>> -            pe = (struct parse_error *)(col_get_item_data(item));
>> +            pe = (struct ini_parse_error *)(col_get_item_data(item));
>>              fprintf(file, line_format,
>>                      col_get_item_property(item, NULL), /* Error or warning */
>>                      pe->error,                         /* Error */
>> -- 
>> 1.5.5.6
>>
>> From 2323da5507a13b5267152482a212442685cfcf05 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sat, 25 Dec 2010 23:29:05 -0500
>> Subject: [PATCH] [INI] New merge flags
>>
>> Adding "DETECT" merge modes.
>> These modes would be useable for dry run cases to detect
>> if there are any potential merge conflicts.
>> Patch just defines new constans and adds input checks.
>> ---
>>  ini/ini_configobj.h |    6 ++++++
>>  ini/ini_fileobj.c   |    9 ++++++---
>>  2 files changed, 12 insertions(+), 3 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index 36558a701a86f6f656f2d0e3b155b9d901177fa7..f0c6882dbb1e066972f98309bf78a99670b985c5 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -108,6 +108,8 @@
>>  #define INI_MV1S_PRESERVE  0x0002
>>  /** @brief Duplicates are allowed */
>>  #define INI_MV1S_ALLOW     0x0003
>> +/** @brief Duplicates are allowed but errors are logged */
>> +#define INI_MV1S_DETECT    0x0004
>>  
>>  /**
>>   * @}
>> @@ -133,6 +135,8 @@
>>  #define INI_MV2S_PRESERVE  0x0020
>>  /** @brief Duplicates are allowed */
>>  #define INI_MV2S_ALLOW     0x0030
>> +/** @brief Duplicates are allowed but errors are logged */
>> +#define INI_MV2S_DETECT    0x0040
>>  
>>  /**
>>   * @}
>> @@ -159,6 +163,8 @@
>>  #define INI_MS_PRESERVE  0x0300
>>  /** @brief Duplicates are allowed */
>>  #define INI_MS_ALLOW     0x0400
>> +/** @brief Duplicates are allowed but errors are logged */
>> +#define INI_MS_DETECT    0x0500
>>  
>>  /**
>>   * @}
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index 94494f78a41b6af5e44686134ab1e48290b9ab38..7bc599b948563964830f9ade75966cf4f9aa3018 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -44,7 +44,8 @@ static int valid_collision_flags(uint32_t collision_flags)
>>      if ((flag != INI_MV1S_OVERWRITE) &&
>>          (flag != INI_MV1S_ERROR) &&
>>          (flag != INI_MV1S_PRESERVE) &&
>> -        (flag != INI_MV1S_ALLOW)) {
>> +        (flag != INI_MV1S_ALLOW) &&
>> +        (flag != INI_MV1S_DETECT)) {
>>          TRACE_ERROR_STRING("Invalid value collision flag","");
>>          return 0;
>>      }
>> @@ -53,7 +54,8 @@ static int valid_collision_flags(uint32_t collision_flags)
>>      if ((flag != INI_MV2S_OVERWRITE) &&
>>          (flag != INI_MV2S_ERROR) &&
>>          (flag != INI_MV2S_PRESERVE) &&
>> -        (flag != INI_MV2S_ALLOW)) {
>> +        (flag != INI_MV2S_ALLOW) &&
>> +        (flag != INI_MV2S_DETECT)) {
>>          TRACE_ERROR_STRING("Invalid value cross-section collision flag","");
>>          return 0;
>>      }
>> @@ -63,7 +65,8 @@ static int valid_collision_flags(uint32_t collision_flags)
>>          (flag != INI_MS_OVERWRITE) &&
>>          (flag != INI_MS_ERROR) &&
>>          (flag != INI_MS_PRESERVE) &&
>> -        (flag != INI_MS_ALLOW)) {
>> +        (flag != INI_MS_ALLOW) &&
>> +        (flag != INI_MS_DETECT)) {
>>          TRACE_ERROR_STRING("Invalid section collision flag","");
>>          return 0;
>>      }
>> -- 
>> 1.5.5.6
>>
>> From 45efaa8dc8de649d4714066ccd3b8441dd55b0e6 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sat, 25 Dec 2010 23:36:06 -0500
>> Subject: [PATCH] [INI] Add new vars to parse structure
>>
>> Adding new varibles to the internal parsing structure
>> and initializing them.
>> ---
>>  ini/ini_parse.c |   14 +++++++++++++-
>>  1 files changed, 13 insertions(+), 1 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index 18edc629e90fe47597357b7953f06990ab5a48f0..9667a02926ef7d984886896b206997a841f04619 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -69,8 +69,11 @@ struct parser_obj {
>>      uint32_t linenum;
>>      /* Line number of the last found key */
>>      uint32_t keylinenum;
>> +    /* Line number of the last found section */
>> +    uint32_t seclinenum;
>>      /* Internal variables */
>>      struct collection_item *sec;
>> +    struct collection_item *merge_sec;
>>      struct ini_comment *ic;
>>      char *last_read;
>>      uint32_t last_read_len;
>> @@ -78,12 +81,15 @@ struct parser_obj {
>>      uint32_t key_len;
>>      struct ref_array *raw_lines;
>>      struct ref_array *raw_lengths;
>> +    char *merge_key;
>> +    struct value_obj *merge_vo;
>> +    /* Merge error */
>> +    uint32_t merge_error;
>>      int ret;
>>  };
>>  
>>  typedef int (*action_fn)(struct parser_obj *);
>>  
>> -
>>  #define PARSE_ACTION       "action"
>>  
>>  /* Actions */
>> @@ -181,9 +187,12 @@ static int parser_create(FILE *file,
>>  
>>      /* Initialize internal varibles */
>>      new_po->sec = NULL;
>> +    new_po->merge_sec = NULL;
>>      new_po->ic = NULL;
>>      new_po->last_error = 0;
>>      new_po->linenum = 0;
>> +    new_po->keylinenum = 0;
>> +    new_po->seclinenum = 0;
>>      new_po->last_read = NULL;
>>      new_po->last_read_len = 0;
>>      new_po->key = NULL;
>> @@ -191,6 +200,9 @@ static int parser_create(FILE *file,
>>      new_po->raw_lines = NULL;
>>      new_po->raw_lengths = NULL;
>>      new_po->ret = EOK;
>> +    new_po->merge_key = NULL;
>> +    new_po->merge_vo = NULL;
>> +    new_po->merge_error = 0;
>>  
>>      /* Create a queue */
>>      new_po->queue = NULL;
>> -- 
>> 1.5.5.6
>>
>> From 27cf7bc5c1089380cc7bc893deee8eb88db2d3d4 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sat, 25 Dec 2010 23:44:52 -0500
>> Subject: [PATCH] [INI] Add save_error function
>>
>> Create a function to add error into error list.
>> ---
>>  ini/ini_parse.c |   29 +++++++++++++++++++++++++++++
>>  1 files changed, 29 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index 9667a02926ef7d984886896b206997a841f04619..a2e05f9311d38ff5904b016c5c8335d56b26a170 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -99,6 +99,13 @@ typedef int (*action_fn)(struct parser_obj *);
>>  #define PARSE_ERROR     3 /* Handle error */
>>  #define PARSE_DONE      4 /* We are done */
>>  
>> +/* Declarations of the reusble functions: */
>> +static int complete_value_processing(struct parser_obj *po);
>> +static int save_error(struct collection_item *el,
>> +                      unsigned line,
>> +                      int error,
>> +                      int idx);
>> +
>>  
>>  int is_just_spaces(const char *str, uint32_t len)
>>  {
>> @@ -946,6 +953,28 @@ static int parser_post(struct parser_obj *po)
>>      return EOK;
>>  }
>>  
>> +
>> +static int save_error(struct collection_item *el,
>> +                      unsigned line,
>> +                      int inerr,
>> +                      int idx)
>> +{
>> +    int error = EOK;
>> +    const char *errtxt[] = { ERROR_TXT, WARNING_TXT };
>> +    struct ini_parse_error pe;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    /* Clear the warning bit */
>> +    pe.error = inerr;
>> +    pe.line = line;
>> +    error = col_add_binary_property(el, NULL,
>> +                                    errtxt[idx], &pe, sizeof(pe));
>> +    TRACE_FLOW_RETURN(error);
>> +    return error;
>> +}
>> +
>> +
>>  /* Error and warning processing */
>>  static int parser_error(struct parser_obj *po)
>>  {
>> -- 
>> 1.5.5.6
>>
>> From e5a6bb7e8fab4b7e23f31ee1ab5ce10bf7dd33d5 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sat, 25 Dec 2010 23:48:35 -0500
>> Subject: [PATCH] [INI] Change parse_error to use save_error
>>
>> * Changed parse_error to use save_error function
>>   created in previous commit.
>> * Fixed comment to be more clear.
>> ---
>>  ini/ini_parse.c |   18 +++++++++---------
>>  1 files changed, 9 insertions(+), 9 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index a2e05f9311d38ff5904b016c5c8335d56b26a170..e7eab28211655f7a225202552fd96b68c1e09450 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -981,19 +981,17 @@ static int parser_error(struct parser_obj *po)
>>      int error = EOK;
>>      uint32_t action;
>>      int idx = 0;
>> -    const char *errtxt[] = { ERROR_TXT, WARNING_TXT };
>> -    struct ini_parse_error pe;
>>  
>>      TRACE_FLOW_ENTRY();
>>  
>> -    pe.line = po->linenum;
>> -    /* Clear the warning bit */
>> -    pe.error = po->last_error & ~INI_WARNING;
>>      if (po->last_error & INI_WARNING) idx = 1;
>> -    error = col_add_binary_property(po->el, NULL,
>> -                                    errtxt[idx], &pe, sizeof(pe));
>> +
>> +    error = save_error(po->el,
>> +                       po->linenum,
>> +                       po->last_error & ~INI_WARNING,
>> +                       idx);
>>      if (error) {
>> -        TRACE_ERROR_NUMBER("Failed to add error to collection",
>> +        TRACE_ERROR_NUMBER("Failed to add error to error list",
>>                              error);
>>          return error;
>>      }
>> @@ -1043,7 +1041,9 @@ static int parser_error(struct parser_obj *po)
>>              /* If merging sections should produce error and we got error
>>               * or if we merge sections but dup values produce error and
>>               * we got error then it is not a fatal error so we need to handle
>> -             * it nicely. We check for reverse condition and return error,
>> +             * it nicely and suppress it here. We already in the procees
>> +             * of handling another error and merge error does not matter here.
>> +             * We check for reverse condition and return error,
>>               * otherwise fall through.
>>               */
>>              if (!((((po->collision_flags & INI_MS_MASK) == INI_MS_ERROR) &&
>> -- 
>> 1.5.5.6
>>
>> From 10610699c6db3d181011c85447073f5ee4a04153 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sat, 25 Dec 2010 23:56:51 -0500
>> Subject: [PATCH] [INI] Preparing for merging sections
>>
>> Patch implements three functions:
>> * Function to detect a collision between two section names.
>> * Function to empty section from all its keys
>> * Function to add values one by one from one section to
>>   another.
>> ---
>>  ini/ini_parse.c |  177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 177 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index e7eab28211655f7a225202552fd96b68c1e09450..fc43cb06daca64309e47f43047263c5f066fc43e 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -318,6 +318,183 @@ static int parser_read(struct parser_obj *po)
>>      return EOK;
>>  }
>>  
>> +/* Find if there is a collistion */
>> +static int check_section_collision(struct parser_obj *po)
>> +{
>> +    int error = EOK;
>> +    struct collection_item *item = NULL;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    TRACE_INFO_STRING("Searching for:", col_get_item_property(po->sec, NULL));
>> +
>> +    error = col_get_item(po->top,
>> +                         col_get_item_property(po->sec, NULL),
>> +                         COL_TYPE_ANY,
>> +                         COL_TRAVERSE_DEFAULT,
>> +                         &item);
>> +
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed searching for dup", error);
>> +        return error;
>> +    }
>> +
>> +    /* Check if there is a dup */
>> +    if (item) {
>> +        TRACE_INFO_STRING("Collision found:",
>> +                          col_get_item_property(item, NULL));
>> +        /* Get the actual section collection instead of reference */
>> +        po->merge_sec = *((struct collection_item **)
>> +                          (col_get_item_data(item)));
>> +    }
>> +    else {
>> +        TRACE_INFO_STRING("Collision not found.", "");
>> +        po->merge_sec = NULL;
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return EOK;
>> +}
>> +
>> +/* Clean all items in the section */
>> +static int empty_section(struct collection_item *sec)
>> +{
>> +    int error = EOK;
>> +    struct collection_item *item = NULL;
>> +    struct collection_item *save_item = NULL;
>> +    struct value_obj *vo = NULL;
>> +    int work_to_do = 1;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    do {
>> +        item = NULL;
>> +        error = col_extract_item_from_current(sec,
>> +                                              COL_DSP_FRONT,
>> +                                              NULL,
>> +                                              0,
>> +                                              COL_TYPE_ANY,
>> +                                              &item);
>> +        if ((error) && (error != ENOENT)) {
>> +            TRACE_ERROR_NUMBER("Failed to extract item.", error);
>> +            return error;
>> +        }
>> +
>> +        if (item) {
>> +            TRACE_INFO_STRING("Item found:",
>> +                              col_get_item_property(item, NULL));
>> +
>> +            if (strncmp(col_get_item_property(item, NULL),
>> +                        INI_SECTION_KEY, 1) == 0) {
>> +                /* Just ignore the first item */
>> +                save_item = item;
>> +                continue;
>> +            }
>> +
>> +            vo = *((struct value_obj **)(col_get_item_data(item)));
>> +            value_destroy(vo);
>> +            col_delete_item(item);
>> +        }
>> +        else {
>> +            TRACE_INFO_STRING("No more items:", "");
>> +            /* Restore saved item */
>> +            error = col_insert_item(sec,
>> +                                    NULL,
>> +                                    save_item,
>> +                                    COL_DSP_END,
>> +                                    NULL,
>> +                                    0,
>> +                                    COL_INSERT_NOCHECK);
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Failed to restore item.", error);
>> +                return error;
>> +            }
>> +
>> +            work_to_do = 0;
>> +        }
>> +    }
>> +    while (work_to_do);
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return EOK;
>> +}
>> +
>> +/* Merge contents of the section */
>> +static int merge_section(struct parser_obj *po)
>> +{
>> +    int error = EOK;
>> +    struct collection_item *item = NULL;
>> +    struct value_obj *vo = NULL;
>> +    int work_to_do = 1;
>> +    const char *key;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    do {
>> +        TRACE_INFO_STRING("Top of the merge loop", "");
>> +
>> +        item = NULL;
>> +        error = col_extract_item_from_current(po->sec,
>> +                                              COL_DSP_FRONT,
>> +                                              NULL,
>> +                                              0,
>> +                                              COL_TYPE_ANY,
>> +                                              &item);
>> +        if ((error) && (error != ENOENT)) {
>> +            TRACE_ERROR_NUMBER("Failed to extract item.", error);
>> +            return error;
>> +        }
>> +
>> +        if (item) {
>> +
>> +            TRACE_INFO_STRING("Item found:", col_get_item_property(item, NULL));
>> +
>> +            if (strncmp(col_get_item_property(item, NULL),
>> +                        INI_SECTION_KEY, 1) == 0) {
>> +                /* Just ignore the first item */
>> +                vo = *((struct value_obj **)(col_get_item_data(item)));
>> +                value_destroy(vo);
>> +                col_delete_item(item);
>> +                continue;
>> +            }
>> +
>> +            po->merge_vo = *((struct value_obj **)(col_get_item_data(item)));
>> +            key = col_get_item_property(item, NULL);
>> +            /* To be able to use po->merge_key in the loop
>> +             * we have to overcome constraints imposed by
>> +             * the "const" declaration.
>> +             */
>> +            memcpy(&(po->merge_key), &key, sizeof(char *));
>> +
>> +            /* Use the value processing function to inser the value */
>> +            error = complete_value_processing(po);
>> +
>> +            /* In case of error value is already cleaned */
>> +            po->merge_vo = NULL;
>> +            po->merge_key = NULL;
>> +            col_delete_item(item);
>> +            /* Now we can check the error */
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Failed to merge item.", error);
>> +                return error;
>> +            }
>> +        }
>> +        else {
>> +            TRACE_INFO_STRING("No more items:", "");
>> +            work_to_do = 0;
>> +        }
>> +    }
>> +    while (work_to_do);
>> +
>> +    /* If we reached this place the incoming section is empty.
>> +     * but just to be safe clean with callback. */
>> +    col_destroy_collection_with_cb(po->sec, ini_cleanup_cb, NULL);
>> +    po->sec = NULL;
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return EOK;
>> +}
>> +
>>  /* Function to read next line from the file */
>>  static int parser_save_section(struct parser_obj *po)
>>  {
>> -- 
>> 1.5.5.6
>>
>> From aa35a75674482febe3f5f67b3e7c7c7faf507f05 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:03:33 -0500
>> Subject: [PATCH] [INI] Enhance value processing
>>
>> This patch refactores the value processing
>> function so that it can be used both in
>> normal mode when velues need to be constrcuted and
>> saved into the current section (po->sec) and in the
>> merge mode when values are already constructed
>> and need to be saved into a po->merge_sec.
>> ---
>>  ini/ini_parse.c |  165 ++++++++++++++++++++++++++++++++++++++----------------
>>  1 files changed, 116 insertions(+), 49 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index fc43cb06daca64309e47f43047263c5f066fc43e..5dd873a339e5fce36bc13ea8bde65dbefc5d617f 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -529,6 +529,7 @@ static int parser_save_section(struct parser_obj *po)
>>  static int complete_value_processing(struct parser_obj *po)
>>  {
>>      int error = EOK;
>> +    int error2 = EOK;
>>      struct value_obj *vo = NULL;
>>      struct value_obj *vo_old = NULL;
>>      unsigned insertmode;
>> @@ -536,12 +537,19 @@ static int complete_value_processing(struct parser_obj *po)
>>      int suppress = 0;
>>      int doinsert = 0;
>>      struct collection_item *item = NULL;
>> +    struct collection_item *section = NULL;
>> +    int merging = 0;
>>  
>>      TRACE_FLOW_ENTRY();
>>  
>> -    /* If there is not open section create a default one */
>> -    if(!(po->sec)) {
>> -        /* Create a new section */
>> +    if (po->merge_sec) {
>> +        TRACE_INFO_STRING("Processing value in merge mode", "");
>> +        section = po->merge_sec;
>> +        merging = 1;
>> +    }
>> +    else if(!(po->sec)) {
>> +        TRACE_INFO_STRING("Creating default section", "");
>> +        /* If there is not open section create a default one */
>>          error = col_create_collection(&po->sec,
>>                                        INI_DEFAULT_SECTION,
>>                                        COL_CLASS_INI_SECTION);
>> @@ -549,29 +557,44 @@ static int complete_value_processing(struct parser_obj *po)
>>              TRACE_ERROR_NUMBER("Failed to create default section", error);
>>              return error;
>>          }
>> +        section = po->sec;
>>      }
>> -
>> -    /* Construct value object from what we have */
>> -    error = value_create_from_refarray(po->raw_lines,
>> -                                       po->raw_lengths,
>> -                                       po->keylinenum,
>> -                                       INI_VALUE_READ,
>> -                                       po->key_len,
>> -                                       po->boundary,
>> -                                       po->ic,
>> -                                       &vo);
>> -
>> -    if (error) {
>> -        TRACE_ERROR_NUMBER("Failed to create value object", error);
>> -        return error;
>> +    else {
>> +        TRACE_INFO_STRING("Processing value in normal mode", "");
>> +        section = po->sec;
>>      }
>>  
>> -    /* Forget about the arrays. They are now owned by the value object */
>> -    po->ic = NULL;
>> -    po->raw_lines = NULL;
>> -    po->raw_lengths = NULL;
>> +    if (merging) {
>> +        TRACE_INFO_STRING("Using merge key:", po->merge_key);
>> +        vo = po->merge_vo;
>> +        /* We are adding to the merge section so use MV2S flags.
>> +         * But flags are done in such a way that deviding MV2S by MV1S mask
>> +         * will translate MV2S flags into MV1S so we can use
>> +         * MV1S constants. */
>> +        TRACE_INFO_NUMBER("Collisions flags:", po->collision_flags);
>> +        mergemode = (po->collision_flags & INI_MV2S_MASK) / INI_MV1S_MASK;
>> +    }
>> +    else {
>> +        /* Construct value object from what we have */
>> +        error = value_create_from_refarray(po->raw_lines,
>> +                                           po->raw_lengths,
>> +                                           po->keylinenum,
>> +                                           INI_VALUE_READ,
>> +                                           po->key_len,
>> +                                           po->boundary,
>> +                                           po->ic,
>> +                                           &vo);
>>  
>> -    mergemode = po->collision_flags & INI_MV1S_MASK;
>> +        if (error) {
>> +            TRACE_ERROR_NUMBER("Failed to create value object", error);
>> +            return error;
>> +        }
>> +        /* Forget about the arrays. They are now owned by the value object */
>> +        po->ic = NULL;
>> +        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;
>> @@ -585,16 +608,18 @@ static int complete_value_processing(struct parser_obj *po)
>>                               doinsert = 1;
>>                               break;
>>      case INI_MV1S_OVERWRITE: /* Special handling */
>> +    case INI_MV1S_DETECT:
>>      default:
>>                               break;
>>      }
>>  
>>      /* Do not insert but search for dups first */
>>      if (!doinsert) {
>> -        TRACE_INFO_STRING("Ovewrite mode. Lokking for:", po->key);
>> +        TRACE_INFO_STRING("Overwrite mode. Looking for:",
>> +                          (char *)(merging ? po->merge_key : po->key));
>>  
>> -        error = col_get_item(po->sec,
>> -                             po->key,
>> +        error = col_get_item(section,
>> +                             merging ? po->merge_key : po->key,
>>                               COL_TYPE_BINARY,
>>                               COL_TRAVERSE_DEFAULT,
>>                               &item);
>> @@ -607,21 +632,42 @@ static int complete_value_processing(struct parser_obj *po)
>>  
>>          /* 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);
>> -                value_destroy(vo);
>> -                return error;
>> +            /* Check if we are in the detect mode */
>> +            if (mergemode == INI_MV1S_DETECT) {
>> +                po->merge_error = EEXIST;
>> +                /* There is a dup - inform user about it and continue */
>> +                error = save_error(po->el,
>> +                                   merging ? po->seclinenum : po->keylinenum,
>> +                                   merging ? ERR_DUPKEYSEC : ERR_DUPKEY,
>> +                                   0);
>> +                if (error) {
>> +                    TRACE_ERROR_NUMBER("Failed to save error", error);
>> +                    value_destroy(vo);
>> +                    return error;
>> +                }
>> +                doinsert = 1;
>> +                insertmode = COL_INSERT_NOCHECK;
>> +
>> +            }
>> +            else {
>> +
>> +                /* 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);
>> +                    value_destroy(vo);
>> +                    return error;
>> +                }
>> +
>> +                /* If we failed to update it is better to leak then crash,
>> +                 * so destroy original value only on the successful update.
>> +                 */
>> +                value_destroy(vo_old);
>>              }
>> -            /* 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 */
>> @@ -632,31 +678,52 @@ static int complete_value_processing(struct parser_obj *po)
>>  
>>      if (doinsert) {
>>          /* Add value to collection */
>> -        error = col_insert_binary_property(po->sec,
>> +        error = col_insert_binary_property(section,
>>                                             NULL,
>>                                             COL_DSP_END,
>>                                             NULL,
>>                                             0,
>>                                             insertmode,
>> -                                           po->key,
>> +                                           merging ? po->merge_key : po->key,
>>                                             &vo,
>>                                             sizeof(struct value_obj *));
>>          if (error) {
>> +            value_destroy(vo);
>> +
>>              if ((suppress) && (error == EEXIST)) {
>> -                TRACE_INFO_STRING("Preseved exisitng value", po->key);
>> -                value_destroy(vo);
>> +                TRACE_INFO_STRING("Preseved exisitng value",
>> +                                  (char *)(merging ? po->merge_key : po->key));
>>              }
>>              else {
>> -                TRACE_ERROR_NUMBER("Failed to add value object to the section", error);
>> -                value_destroy(vo);
>> -                return error;
>> +                /* Check if this is a critical error or not */
>> +                if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) {
>> +                    TRACE_ERROR_NUMBER("Failed to add value object "
>> +                                       "to the section", error);
>> +                    error2 = save_error(po->el,
>> +                                       merging ? po->seclinenum : po->keylinenum,
>> +                                       merging ? ERR_DUPKEYSEC : ERR_DUPKEY,
>> +                                       0);
>> +                    if (error2) {
>> +                        TRACE_ERROR_NUMBER("Failed to save error", error2);
>> +                        return error2;
>> +                    }
>> +                    return error;
>> +                }
>> +                else {
>> +                    TRACE_ERROR_NUMBER("Failed to add value object"
>> +                                       " to the section", error);
>> +                    return error;
>> +                }
>>              }
>>          }
>>      }
>>  
>> -    free(po->key);
>> -    po->key = NULL;
>> -    po->key_len = 0;
>> +    if (!merging) {
>> +        free(po->key);
>> +        po->key = NULL;
>> +        po->key_len = 0;
>> +    }
>> +
>>      TRACE_FLOW_EXIT();
>>      return EOK;
>>  }
>> -- 
>> 1.5.5.6
>>
>> From f5e1f6494f6350d41fce3e063a8aebf72aa7c4ea Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:05:28 -0500
>> Subject: [PATCH] [INI] Use section line number
>>
>> Use section line number for error reporting
>> about the section collisions.
>> ---
>>  ini/ini_parse.c |    7 +++++--
>>  1 files changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index 5dd873a339e5fce36bc13ea8bde65dbefc5d617f..a1c3a26f0f88c80616bad5336be1126a1f9b99bf 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -1069,9 +1069,12 @@ static int handle_section(struct parser_obj *po, uint32_t *action)
>>      }
>>  
>>      /* Save the line number of the last found key */
>> -    po->keylinenum = po->linenum;
>> +    po->seclinenum = po->linenum;
>>  
>> -    /* Complete processing of this value */
>> +    /* Complete processing of this value.
>> +     * A new section will be created inside and a special
>> +     * value will be added.
>> +     */
>>      error = complete_value_processing(po);
>>      if (error) {
>>          TRACE_ERROR_NUMBER("Failed to complete value processing", error);
>> -- 
>> 1.5.5.6
>>
>> From 6d4b2785106cd6614eedb62069da76ecc2e90457 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:08:19 -0500
>> Subject: [PATCH] [INI] Refactor section processing
>>
>> This patch adds functionality to respect
>> merge section flags and to process section
>> collisions differently dpending on these flags.
>> ---
>>  ini/ini_parse.c |  114 ++++++++++++++++++++++++++++++++++++++++++++++++++----
>>  1 files changed, 105 insertions(+), 9 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index a1c3a26f0f88c80616bad5336be1126a1f9b99bf..d9c6d0a9bc71f197eb4bf752fd56958ba6f64df2 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -499,25 +499,121 @@ static int merge_section(struct parser_obj *po)
>>  static int parser_save_section(struct parser_obj *po)
>>  {
>>      int error = EOK;
>> +    uint32_t mergemode;
>> +    int add = 0;
>> +    int merge = 0;
>>  
>>      TRACE_FLOW_ENTRY();
>>  
>>      if (po->sec) {
>>  
>> -        /* For now just add as we did.
>> -         * Add merge code here !!!!
>> -         */
>> -        error = col_add_collection_to_collection(po->top,
>> -                                                 NULL, NULL,
>> -                                                 po->sec,
>> -                                                 COL_ADD_MODE_EMBED);
>> +        TRACE_INFO_STRING("Section exists.", "");
>>  
>> +        /* First detect if we have collision */
>> +        error = check_section_collision(po);
>>          if (error) {
>> -            TRACE_ERROR_NUMBER("Failed to embed section", error);
>> +            TRACE_ERROR_NUMBER("Failed to check for collision", error);
>>              return error;
>>          }
>>  
>> -        po->sec = NULL;
>> +        if (po->merge_sec) {
>> +
>> +            TRACE_INFO_STRING("Merge collision detected", "");
>> +
>> +            mergemode = po->collision_flags & INI_MS_MASK;
>> +
>> +            switch (mergemode) {
>> +            case INI_MS_ERROR:      /* Report error and return */
>> +                                    TRACE_INFO_STRING("Reporting error",
>> +                                                      "duplicate section");
>> +                                    error = save_error(po->el,
>> +                                                       po->seclinenum,
>> +                                                       ERR_DUPSECTION,
>> +                                                       0);
>> +                                    if (error) {
>> +                                        TRACE_ERROR_NUMBER("Failed to "
>> +                                                           "save error",
>> +                                                            error);
>> +                                        return error;
>> +                                    }
>> +                                    /* Return error */
>> +                                    TRACE_FLOW_RETURN(EEXIST);
>> +                                    return EEXIST;
>> +
>> +            case INI_MS_PRESERVE:   /* Delete new section */
>> +                                    TRACE_INFO_STRING("Preserve mode", "");
>> +                                    col_destroy_collection_with_cb(
>> +                                                            po->sec,
>> +                                                            ini_cleanup_cb,
>> +                                                            NULL);
>> +                                    po->sec = NULL;
>> +                                    break;
>> +
>> +            case INI_MS_ALLOW:      TRACE_INFO_STRING("Allow mode", "");
>> +                                    add = 1;
>> +                                    break;
>> +
>> +            case INI_MS_OVERWRITE:  /* Empty existing section */
>> +                                    TRACE_INFO_STRING("Ovewrite mode", "");
>> +                                    error = empty_section(po->merge_sec);
>> +                                    if (error) {
>> +                                        TRACE_ERROR_NUMBER("Failed to "
>> +                                                           "empty section",
>> +                                                            error);
>> +                                        return error;
>> +                                    }
>> +                                    merge = 1;
>> +                                    break;
>> +
>> +            case INI_MS_DETECT:     /* Detect mode */
>> +                                    TRACE_INFO_STRING("Detect mode", "");
>> +                                    po->merge_error = EEXIST;
>> +                                    error = save_error(po->el,
>> +                                                       po->seclinenum,
>> +                                                       ERR_DUPSECTION,
>> +                                                       0);
>> +                                    if (error) {
>> +                                        TRACE_ERROR_NUMBER("Failed to "
>> +                                                           "save error",
>> +                                                            error);
>> +                                        return error;
>> +                                    }
>> +                                    merge = 1;
>> +                                    break;
>> +
>> +            case INI_MS_MERGE:      /* Merge */
>> +            default:                TRACE_INFO_STRING("Merge mode", "");
>> +                                    merge = 1;
>> +                                    break;
>> +            }
>> +
>> +            if (merge) {
>> +                error = merge_section(po);
>> +                if (error) {
>> +                    TRACE_ERROR_NUMBER("Failed to merge section", error);
>> +                    return error;
>> +                }
>> +            }
>> +
>> +            po->merge_sec = NULL;
>> +        }
>> +        else add = 1;
>> +
>> +        if (add) {
>> +            /* Add section to configuration */
>> +            TRACE_INFO_STRING("Now adding collection", "");
>> +            error = col_add_collection_to_collection(po->top,
>> +                                                     NULL, NULL,
>> +                                                     po->sec,
>> +                                                     COL_ADD_MODE_EMBED);
>> +
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Failed to embed section", error);
>> +                return error;
>> +            }
>> +
>> +            po->sec = NULL;
>> +        }
>>      }
>>  
>>      TRACE_FLOW_EXIT();
>> -- 
>> 1.5.5.6
>>
>> From 8fb3799dc674770bb622b630267a7a436449069e Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:10:23 -0500
>> Subject: [PATCH] [INI] Return error in DETECT mode
>>
>> If in merge DETECT mode and there were no parsing errors
>> return error if there were merge collisions.
>> ---
>>  ini/ini_parse.c |   11 +++++++++++
>>  1 files changed, 11 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index d9c6d0a9bc71f197eb4bf752fd56958ba6f64df2..3f45ac71ae46dff3413a6d4db5dd59a0838d078b 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -1441,7 +1441,18 @@ int parser_run(struct parser_obj *po)
>>          col_delete_item(item);
>>  
>>          if (action == PARSE_DONE) {
>> +
>>              TRACE_INFO_NUMBER("We are done", error);
>> +
>> +            /* Report merge error in detect mode
>> +             * if no other error was detected. */
>> +            if ((po->ret == 0) &&
>> +                (po->merge_error != 0) &&
>> +                ((po->collision_flags & INI_MV1S_DETECT) ||
>> +                 (po->collision_flags & INI_MV2S_DETECT) ||
>> +                 (po->collision_flags & INI_MS_DETECT)))
>> +                po->ret = po->merge_error;
>> +
>>              error = po->ret;
>>              break;
>>          }
>> -- 
>> 1.5.5.6
>>
>> From 235393228925b4aee6138820276450c233853464 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:15:12 -0500
>> Subject: [PATCH] [INI] New test files for section merge
>>
>> smerge.conf  - test file
>> sexpect.conf - file contains output of the smerge.conf
>>                processed in different merge modes
>>                for sections and values
>> ---
>>  Makefile.am            |    4 +-
>>  ini/ini.d/sexpect.conf |  644 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  ini/ini.d/smerge.conf  |   33 +++
>>  3 files changed, 680 insertions(+), 1 deletions(-)
>>  create mode 100644 ini/ini.d/sexpect.conf
>>  create mode 100644 ini/ini.d/smerge.conf
>>
>> diff --git a/Makefile.am b/Makefile.am
>> index 1780e9b234e68006af910413b3e8312c9db72c7b..4f9ab3427bb309b19ddec0da5e2125046d03babc 100644
>> --- a/Makefile.am
>> +++ b/Makefile.am
>> @@ -280,7 +280,9 @@ dist_noinst_DATA += \
>>      ini/ini.d/test.conf \
>>      ini/ini.d/ipa.conf \
>>      ini/ini.d/foo.conf \
>> -    ini/ini.d/mysssd.conf
>> +    ini/ini.d/mysssd.conf \
>> +    ini/ini.d/smerge.conf \
>> +    ini/ini.d/sexpect.conf
>>  
>>  check_PROGRAMS += \
>>      ini_config_ut \
>> diff --git a/ini/ini.d/sexpect.conf b/ini/ini.d/sexpect.conf
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..6a9222b0a9003ed1a6e9c10401573832980a3706
>> --- /dev/null
>> +++ b/ini/ini.d/sexpect.conf
>> @@ -0,0 +1,644 @@
>> +# Section mode: MERGE, value mode: OVERWRITE
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: MERGE, value mode: ERROR
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: MERGE, value mode: PRESERVE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +#End of file
>> +# Section mode: MERGE, value mode: ALLOW
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: MERGE, value mode: DETECT
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: ERROR, value mode: OVERWRITE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: ERROR, value mode: ERROR
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: ERROR, value mode: PRESERVE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: ERROR, value mode: ALLOW
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: ERROR, value mode: DETECT
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: OVERWRITE, value mode: OVERWRITE
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: OVERWRITE, value mode: ERROR
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: OVERWRITE, value mode: PRESERVE
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: OVERWRITE, value mode: ALLOW
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: OVERWRITE, value mode: DETECT
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: PRESERVE, value mode: OVERWRITE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: PRESERVE, value mode: ERROR
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: PRESERVE, value mode: PRESERVE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: PRESERVE, value mode: ALLOW
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: PRESERVE, value mode: DETECT
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: ALLOW, value mode: OVERWRITE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: ALLOW, value mode: ERROR
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: ALLOW, value mode: PRESERVE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: ALLOW, value mode: ALLOW
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: ALLOW, value mode: DETECT
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: DETECT, value mode: OVERWRITE
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: DETECT, value mode: ERROR
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Section mode: DETECT, value mode: PRESERVE
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +#End of file
>> +# Section mode: DETECT, value mode: ALLOW
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> +# Section mode: DETECT, value mode: DETECT
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> diff --git a/ini/ini.d/smerge.conf b/ini/ini.d/smerge.conf
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..bdec46d86fb6ecd0d39f4b50aca01a53dabd9ebf
>> --- /dev/null
>> +++ b/ini/ini.d/smerge.conf
>> @@ -0,0 +1,33 @@
>> +[section1]
>> +# Key 1
>> +key1 = section1a Value 1
>> +# Key 2
>> +key2 = section1a Value 2
>> +# Key 3
>> +key3 = section1a Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2a Value 1
>> +# Key 2
>> +key2 = section2a Value 2
>> +# Key 3
>> +key3 = section2a Value 3
>> +
>> +[section1]
>> +# Key 1
>> +key1 = section1b Value 1
>> +# Key 2
>> +key2 = section1b Value 2
>> +# Key 3
>> +key3 = section1b Value 3
>> +
>> +[section2]
>> +# Key 1
>> +key1 = section2b Value 1
>> +# Key 2
>> +key2 = section2b Value 2
>> +# Key 3
>> +key3 = section2b Value 3
>> +
>> +#End of file
>> -- 
>> 1.5.5.6
>>
>> From a2b8565c919f3b39e9d7eeef9baa29081bb80f43 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:18:48 -0500
>> Subject: [PATCH] [INI] Test DETECT mode and use new file
>>
>> Patch adds smerge.conf file to the list
>> of files to test and adds test for the DETECT
>> mode for inidividual values.
>> ---
>>  ini/ini_parse_ut.c |   30 +++++++++++++++++++++++-------
>>  1 files changed, 23 insertions(+), 7 deletions(-)
>>
>> diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
>> index 9327ebaab2dc529b593e0b550011bbac6b949b94..40c64bb15c0535dd2e8f5e62129a31ae53c3f35f 100644
>> --- a/ini/ini_parse_ut.c
>> +++ b/ini/ini_parse_ut.c
>> @@ -173,6 +173,7 @@ int read_save_test(void)
>>                              "mysssd",
>>                              "ipa",
>>                              "test",
>> +                            "smerge",
>>                              NULL };
>>  
>>  
>> @@ -203,6 +204,7 @@ int read_again_test(void)
>>                              "mysssd",
>>                              "ipa",
>>                              "test",
>> +                            "smerge",
>>                              NULL };
>>  
>>  
>> @@ -279,14 +281,25 @@ int create_expect(const char *checkname)
>>      fprintf(ff,"#Second value\n");
>>      fprintf(ff,"bar = second value\n");
>>      fprintf(ff,"#End of section\n");
>> +    /* Detect */
>> +    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)
>>  {
>> @@ -301,12 +314,14 @@ int merge_values_test(void)
>>      uint32_t mflags[] = { INI_MV1S_OVERWRITE,
>>                            INI_MV1S_ERROR,
>>                            INI_MV1S_PRESERVE,
>> -                          INI_MV1S_ALLOW };
>> +                          INI_MV1S_ALLOW,
>> +                          INI_MV1S_DETECT };
>>  
>>      const char *mstr[] = { "OVERWRITE",
>>                             "ERROR",
>>                             "PRESERVE",
>> -                           "ALLOW" };
>> +                           "ALLOW",
>> +                           "DETECT" };
>>  
>>      char filename[PATH_MAX];
>>      const char *resname = "./merge.conf.out";
>> @@ -323,7 +338,7 @@ int merge_values_test(void)
>>          return error;
>>      }
>>  
>> -    for (i = 0; i < 4; i++) {
>> +    for (i = 0; i < 5; i++) {
>>  
>>          INIOUT(printf("<==== Testing mode %s  ====>\n", mstr[i]));
>>  
>> @@ -360,8 +375,9 @@ int merge_values_test(void)
>>                  ini_config_free_errors(error_list);
>>              }
>>  
>> -            if ((mflags[i] != INI_MV1S_ERROR) ||
>> -                ((mflags[i] = INI_MV1S_ERROR) && (error != EEXIST))) {
>> +            if (((mflags[i] != INI_MV1S_ERROR) && (mflags[i]!= INI_MV1S_DETECT)) ||
>> +                ((mflags[i] = INI_MV1S_ERROR) && (error != EEXIST)) ||
>> +                ((mflags[i] = INI_MV1S_DETECT) && (error != EEXIST))) {
>>                  printf("This is unexpected error %d in mode %d\n", error, mflags[i]);
>>  	            ini_config_destroy(ini_config);
>>  		        simplebuffer_free(sbobj);
>> -- 
>> 1.5.5.6
>>
>> From 24241d6fa2aa0b7e1595f0600d28238945f647d3 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 00:20:54 -0500
>> Subject: [PATCH] [INI] Test for all section merge modes
>>
>> New test reads smerge.conf in all possible
>> modes and compares the combined result with
>> the sexpect.conf function.
>> ---
>>  ini/ini_parse_ut.c |  188 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 188 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
>> index 40c64bb15c0535dd2e8f5e62129a31ae53c3f35f..95793dc39cfa73966192802a12d9bdc54c5ec545 100644
>> --- a/ini/ini_parse_ut.c
>> +++ b/ini/ini_parse_ut.c
>> @@ -438,6 +438,193 @@ int merge_values_test(void)
>>                    resname, checkname, error));
>>  
>>      return error;
>> +}
>> +
>> +/* Check merge modes */
>> +int merge_section_test(void)
>> +{
>> +    int error = EOK;
>> +    int i, j;
>> +    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 msecflags[] = { INI_MS_MERGE,
>> +                             INI_MS_ERROR,
>> +                             INI_MS_OVERWRITE,
>> +                             INI_MS_PRESERVE,
>> +                             INI_MS_ALLOW,
>> +                             INI_MS_DETECT };
>> +
>> +    uint32_t mflags[] = { INI_MV2S_OVERWRITE,
>> +                          INI_MV2S_ERROR,
>> +                          INI_MV2S_PRESERVE,
>> +                          INI_MV2S_ALLOW,
>> +                          INI_MV2S_DETECT };
>> +
>> +    const char *secmstr[] = { "MERGE",
>> +                              "ERROR",
>> +                              "OVERWRITE",
>> +                              "PRESERVE",
>> +                              "ALLOW",
>> +                              "DETECT" };
>> +
>> +    const char *mstr[] = { "OVERWRITE",
>> +                           "ERROR",
>> +                           "PRESERVE",
>> +                           "ALLOW",
>> +                           "DETECT" };
>> +
>> +    char filename[PATH_MAX];
>> +    char checkname[PATH_MAX];
>> +    const char *resname = "./smerge.conf.out";
>> +    char command[PATH_MAX * 3];
>> +    char mode[100];
>> +    char *srcdir;
>> +
>> +    srcdir = getenv("srcdir");
>> +    sprintf(filename, "%s/ini/ini.d/smerge.conf",
>> +                      (srcdir == NULL) ? "." : srcdir);
>> +    sprintf(checkname, "%s/ini/ini.d/sexpect.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 < 6; i++) {
>> +        for (j = 0; j < 5; j++) {
>> +
>> +            INIOUT(printf("<==== Testing mode %s + %s ====>\n",
>> +                          secmstr[i], mstr[j]));
>> +
>> +            sprintf(mode, "# Section mode: %s, value mode: %s\n",
>> +                          secmstr[i], mstr[j]);
>> +
>> +            error = simplebuffer_add_str(sbobj,
>> +                                         mode,
>> +                                         strlen(mode),
>> +                                         100);
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Failed to add string.",
>> +                                   error);
>> +    	        simplebuffer_free(sbobj);
>> +                return error;
>> +            }
>> +
>> +            /* 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,
>> +                                         msecflags[i] | mflags[j],
>> +                                         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 (((msecflags[i] == INI_MS_ERROR) &&
>> +                     (error == EEXIST)) ||
>> +                    ((msecflags[i] == INI_MS_DETECT) &&
>> +                     (error == EEXIST)) ||
>> +                    ((msecflags[i] == INI_MS_MERGE) &&
>> +                     ((mflags[j] == INI_MV2S_ERROR) ||
>> +                      (mflags[j] == INI_MV2S_DETECT)) &&
>> +                      (error == EEXIST))) {
>> +                    INIOUT(printf("This is an expected error "
>> +                                  "%d in mode %d + %d\n",
>> +                                  error,
>> +                                  msecflags[i],
>> +                                  mflags[j]));
>> +                    /* We do not return here intentionally */
>> +                }
>> +                else {
>> +                    printf("This is unexpected error %d in mode %d + %d\n",
>> +                            error, msecflags[i], mflags[j]);
>> +    	            ini_config_destroy(ini_config);
>> +    		        simplebuffer_free(sbobj);
>> +    		        ini_config_file_close(file_ctx);
>> +                    return error;
>> +                }
>> +            }
>> +
>> +            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);
>> +
>> +    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;
>>  
>>  
>>  }
>> @@ -449,6 +636,7 @@ int main(int argc, char *argv[])
>>      test_fn tests[] = { read_save_test,
>>                          read_again_test,
>>                          merge_values_test,
>> +                        merge_section_test,
>>                          NULL };
>>      test_fn t;
>>      int i = 0;
>> -- 
>> 1.5.5.6
>>
>> From 8176dad24b0124a634450741f00b2d03c48ce437 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 18:08:27 -0500
>> Subject: [PATCH] [INI] Separate close and destroy
>>
>> Allow closing file without destroying the context.
>> ---
>>  ini/ini_configobj.h |    5 ++++-
>>  ini/ini_fileobj.c   |   25 +++++++++++++++++++------
>>  ini/ini_parse_ut.c  |   10 +++++-----
>>  3 files changed, 28 insertions(+), 12 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index f0c6882dbb1e066972f98309bf78a99670b985c5..e728c2e3cbc787586a542a184a07593ad1e03331 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -213,9 +213,12 @@ int ini_config_file_open(const char *filename,
>>                           uint32_t metadata_flags,
>>                           struct ini_cfgfile **file_ctx);
>>  
>> -/* Close file context and destroy the object */
>> +/* Close file context */
>>  void ini_config_file_close(struct ini_cfgfile *file_ctx);
>>  
>> +/* Close file context and destroy the object */
>> +void ini_config_file_destroy(struct ini_cfgfile *file_ctx);
>> +
>>  /* How many errors do we have in the list ? */
>>  unsigned ini_config_error_count(struct ini_cfgfile *file_ctx);
>>  
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index 7bc599b948563964830f9ade75966cf4f9aa3018..f998eb09d1b5c271be9a316196a510bac39455c2 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -75,12 +75,25 @@ static int valid_collision_flags(uint32_t collision_flags)
>>      return 1;
>>  }
>>  
>> -/* Close file context and destroy the object */
>> +
>> +/* Close file but not destroy the object */
>>  void ini_config_file_close(struct ini_cfgfile *file_ctx)
>>  {
>>      TRACE_FLOW_ENTRY();
>>  
>>      if(file_ctx) {
>> +        if(file_ctx->file) fclose(file_ctx->file);
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +}
>> +
>> +/* Close file context and destroy the object */
>> +void ini_config_file_destroy(struct ini_cfgfile *file_ctx)
>> +{
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    if(file_ctx) {
>>          free(file_ctx->filename);
>>          col_destroy_collection(file_ctx->error_list);
>>          col_destroy_collection(file_ctx->metadata);
>> @@ -144,7 +157,7 @@ int ini_config_file_open(const char *filename,
>>      new_ctx->filename = malloc(PATH_MAX + 1);
>>      if (!(new_ctx->filename)) {
>>          error = errno;
>> -        ini_config_file_close(new_ctx);
>> +        ini_config_file_destroy(new_ctx);
>>          TRACE_ERROR_NUMBER("Failed to allocate memory for file path.", error);
>>          return error;
>>      }
>> @@ -155,7 +168,7 @@ int ini_config_file_open(const char *filename,
>>                                            filename);
>>      if(error) {
>>          TRACE_ERROR_NUMBER("Failed to resolve path", error);
>> -        ini_config_file_close(new_ctx);
>> +        ini_config_file_destroy(new_ctx);
>>          return error;
>>      }
>>  
>> @@ -166,7 +179,7 @@ int ini_config_file_open(const char *filename,
>>      if (!(new_ctx->file)) {
>>          error = errno;
>>          TRACE_ERROR_NUMBER("Failed to open file", error);
>> -        ini_config_file_close(new_ctx);
>> +        ini_config_file_destroy(new_ctx);
>>          return error;
>>      }
>>  
>> @@ -176,7 +189,7 @@ int ini_config_file_open(const char *filename,
>>                                    COL_CLASS_INI_PERROR);
>>      if (error) {
>>          TRACE_ERROR_NUMBER("Failed to create error list", error);
>> -        ini_config_file_close(new_ctx);
>> +        ini_config_file_destroy(new_ctx);
>>          return error;
>>      }
>>  
>> @@ -185,7 +198,7 @@ int ini_config_file_open(const char *filename,
>>                                    COL_CLASS_INI_META);
>>      if (error) {
>>          TRACE_ERROR_NUMBER("Failed to create metadata collection", error);
>> -        ini_config_file_close(new_ctx);
>> +        ini_config_file_destroy(new_ctx);
>>          return error;
>>      }
>>  
>> diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
>> index 95793dc39cfa73966192802a12d9bdc54c5ec545..f0a95527aa315b559e5c6fa42740fe4a3ba4e4b3 100644
>> --- a/ini/ini_parse_ut.c
>> +++ b/ini/ini_parse_ut.c
>> @@ -91,7 +91,7 @@ int test_one_file(const char *in_filename,
>>          /* We do not return here intentionally */
>>      }
>>  
>> -    ini_config_file_close(file_ctx);
>> +    ini_config_file_destroy(file_ctx);
>>  
>>      INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT));
>>  
>> @@ -381,13 +381,13 @@ int merge_values_test(void)
>>                  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);
>> +		        ini_config_file_destroy(file_ctx);
>>                  return error;
>>              }
>>              /* We do not return here intentionally */
>>          }
>>  
>> -        ini_config_file_close(file_ctx);
>> +        ini_config_file_destroy(file_ctx);
>>  
>>          INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT));
>>  
>> @@ -572,12 +572,12 @@ int merge_section_test(void)
>>                              error, msecflags[i], mflags[j]);
>>      	            ini_config_destroy(ini_config);
>>      		        simplebuffer_free(sbobj);
>> -    		        ini_config_file_close(file_ctx);
>> +    		        ini_config_file_destroy(file_ctx);
>>                      return error;
>>                  }
>>              }
>>  
>> -            ini_config_file_close(file_ctx);
>> +            ini_config_file_destroy(file_ctx);
>>  
>>              INIOUT(col_debug_collection(ini_config->cfg,
>>                     COL_TRAVERSE_DEFAULT));
>> -- 
>> 1.5.5.6
>>
>> From 29e4a0a028b802eaa3bb1dfbc7a9080fc7f21f4b Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 18:34:02 -0500
>> Subject: [PATCH] [INI] Function to reopen file
>>
>> This patch would allow to reopen file
>> and create a new context based on the old one.
>> ---
>>  ini/ini_configobj.h |    4 ++
>>  ini/ini_fileobj.c   |   86 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 90 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index e728c2e3cbc787586a542a184a07593ad1e03331..3321bd3ea8985b9b12f04ab792fbcac307bbbe2e 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -213,6 +213,10 @@ int ini_config_file_open(const char *filename,
>>                           uint32_t metadata_flags,
>>                           struct ini_cfgfile **file_ctx);
>>  
>> +/* Create a file object from existing one */
>> +int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
>> +                           struct ini_cfgfile **file_ctx_out);
>> +
>>  /* Close file context */
>>  void ini_config_file_close(struct ini_cfgfile *file_ctx);
>>  
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index f998eb09d1b5c271be9a316196a510bac39455c2..200c7af9de896c1abd26a57a508f9f540475b19d 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -210,6 +210,92 @@ int ini_config_file_open(const char *filename,
>>      return error;
>>  }
>>  
>> +/* Create a file object from existing one */
>> +int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
>> +                           struct ini_cfgfile **file_ctx_out)
>> +{
>> +    int error = EOK;
>> +    struct ini_cfgfile *new_ctx = NULL;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    if ((!file_ctx_in) || (!file_ctx_out)) {
>> +        TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
>> +        return EINVAL;
>> +    }
>> +
>> +    /* Allocate structure */
>> +    errno = 0;
>> +    new_ctx = malloc(sizeof(struct ini_cfgfile));
>> +    if (!new_ctx) {
>> +        error = errno;
>> +        TRACE_ERROR_NUMBER("Failed to allocate file ctx.", error);
>> +        return error;
>> +    }
>> +
>> +    new_ctx->filename = NULL;
>> +    new_ctx->file = NULL;
>> +    new_ctx->error_list = NULL;
>> +    new_ctx->metadata = NULL;
>> +
>> +    /* TBD - decide whether we actually need an FD.
>> +       It will be done when we move the metadata
>> +       processing into this function. */
>> +    new_ctx->fd = -1;
>> +
>> +    /* Store flags */
>> +    new_ctx->error_level = file_ctx_in->error_level;
>> +    new_ctx->collision_flags = file_ctx_in->collision_flags;
>> +    new_ctx->metadata_flags = file_ctx_in->metadata_flags;
>> +    new_ctx->count = 0;
>> +
>> +    /* Copy full file path */
>> +    errno = 0;
>> +    new_ctx->filename = strndup(file_ctx_in->filename, PATH_MAX);
>> +    if (!(new_ctx->filename)) {
>> +        error = errno;
>> +        ini_config_file_destroy(new_ctx);
>> +        TRACE_ERROR_NUMBER("Failed to allocate memory for file path.", error);
>> +        return error;
>> +    }
>> +
>> +    /* Open file */
>> +    TRACE_INFO_STRING("File", new_ctx->filename);
>> +    errno = 0;
>> +    new_ctx->file = fopen(new_ctx->filename, "r");
>> +    if (!(new_ctx->file)) {
>> +        error = errno;
>> +        TRACE_ERROR_NUMBER("Failed to open file", error);
>> +        ini_config_file_destroy(new_ctx);
>> +        return error;
>> +    }
>> +
>> +    /* Create internal collections */
>> +    error = col_create_collection(&(new_ctx->error_list),
>> +                                  INI_ERROR,
>> +                                  COL_CLASS_INI_PERROR);
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed to create error list", error);
>> +        ini_config_file_close(new_ctx);
>> +        return error;
>> +    }
>> +
>> +    error = col_create_collection(&(new_ctx->metadata),
>> +                                  INI_METADATA,
>> +                                  COL_CLASS_INI_META);
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed to create metadata collection", error);
>> +        ini_config_file_destroy(new_ctx);
>> +        return error;
>> +    }
>> +
>> +
>> +    /* TBD - Add metadata processing here */
>> +
>> +    *file_ctx_out = new_ctx;
>> +    TRACE_FLOW_EXIT();
>> +    return error;
>> +}
>>  
>>  /* How many errors do we have in the list ? */
>>  unsigned ini_config_error_count(struct ini_cfgfile *file_ctx)
>> -- 
>> 1.5.5.6
>>
>> From 7030e51bb22e62e6f5d63b0faa923d2151336818 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 19:21:32 -0500
>> Subject: [PATCH] [INI] Metadata collection is gone
>>
>> After some more thinking I decided not to use
>> metadata collection. It seems to be an overhead.
>> Patch does following:
>> * Replaces metadata collection in file context structure
>>   with standard file stats
>> * Removes all operations against old metadata collection
>> * Defines new flags for data to collect
>> * Creates a function that consolidates common operations
>>   between open and reopen functions.
>> ---
>>  ini/ini_config_priv.h |   11 +++--
>>  ini/ini_configobj.h   |   20 ++++++++
>>  ini/ini_fileobj.c     |  121 ++++++++++++++++++++-----------------------------
>>  3 files changed, 75 insertions(+), 77 deletions(-)
>>
>> diff --git a/ini/ini_config_priv.h b/ini/ini_config_priv.h
>> index 1880c3ff4caa91e7ed3a37e445a20294e76d8037..84742c70ceeb5b493b935c635e81c57d5ef487ce 100644
>> --- a/ini/ini_config_priv.h
>> +++ b/ini/ini_config_priv.h
>> @@ -22,6 +22,9 @@
>>  #ifndef INI_CONFIG_PRIV_H
>>  #define INI_CONFIG_PRIV_H
>>  
>> +#include <sys/types.h>
>> +#include <sys/stat.h>
>> +#include <unistd.h>
>>  #include "collection.h"
>>  
>>  /* Configuration object */
>> @@ -45,21 +48,19 @@ struct ini_cfgfile {
>>      char *filename;
>>      /* File stream */
>>      FILE *file;
>> -    /* File descriptor that is passed in */
>> -    int fd;
>>      /* Error level */
>>      int error_level;
>>      /* Collision flags - define how to merge things */
>>      uint32_t collision_flags;
>> -    /* Collision flags - define how to merge things */
>> +    /* What meta data to collect */
>>      uint32_t metadata_flags;
>>      /**********************/
>>      /* Internal variables */
>>      /**********************/
>>      /* Collection of errors detected during parsing */
>>      struct collection_item *error_list;
>> -    /* Metadata about the file */
>> -    struct collection_item *metadata;
>> +    /* File stats */
>> +    struct stat file_stats;
>>      /* Count of error lines */
>>      unsigned count;
>>  };
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index 3321bd3ea8985b9b12f04ab792fbcac307bbbe2e..898440252ef3a547566fe2b5e80753d7971a8e47 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -82,6 +82,26 @@
>>  /** @brief Size of the error array. */
>>  #define ERR_MAXPARSE        ERR_DUPSECTION
>>  
>> +/**
>> + * @}
>> + */
>> +
>> +/**
>> + * @defgroup metacollect Constants that define what meta data to collect
>> + *
>> + * Constants in this section define what meta data to collect
>> + *
>> + *
>> + * @{
>> + */
>> +/** @brief Do not collect any data. */
>> +#define INI_META_NONE     0
>> +/** @brief Collect file stats. */
>> +#define INI_META_STATS    1
>> +
>> +/**
>> + * @}
>> + */
>>  
>>  /**
>>   * @defgroup collisionflags Flags that define collision resolution logic.
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index 200c7af9de896c1abd26a57a508f9f540475b19d..c10b4bf954366ddae90f1d06c53acd0332968599 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -96,7 +96,6 @@ void ini_config_file_destroy(struct ini_cfgfile *file_ctx)
>>      if(file_ctx) {
>>          free(file_ctx->filename);
>>          col_destroy_collection(file_ctx->error_list);
>> -        col_destroy_collection(file_ctx->metadata);
>>          if(file_ctx->file) fclose(file_ctx->file);
>>          free(file_ctx);
>>      }
>> @@ -104,6 +103,47 @@ void ini_config_file_destroy(struct ini_cfgfile *file_ctx)
>>      TRACE_FLOW_EXIT();
>>  }
>>  
>> +/* Internal common initialization part */
>> +static int common_file_init(struct ini_cfgfile *file_ctx)
>> +{
>> +    int error = EOK;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    /* Open file */
>> +    TRACE_INFO_STRING("File", file_ctx->filename);
>> +    errno = 0;
>> +    file_ctx->file = fopen(file_ctx->filename, "r");
>> +    if (!(file_ctx->file)) {
>> +        error = errno;
>> +        TRACE_ERROR_NUMBER("Failed to open file", error);
>> +        return error;
>> +    }
>> +
>> +    /* Create internal collections */
>> +    error = col_create_collection(&(file_ctx->error_list),
>> +                                  INI_ERROR,
>> +                                  COL_CLASS_INI_PERROR);
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed to create error list", error);
>> +        return error;
>> +    }
>> +
>> +    /* Collect stats */
>> +    if (file_ctx->metadata_flags & INI_META_STATS) {
>> +        errno = 0;
>> +        if (fstat(fileno(file_ctx->file),
>> +                  &(file_ctx->file_stats)) < 0) {
>> +            error = errno;
>> +            TRACE_ERROR_NUMBER("Failed to get file stats.", error);
>> +            return error;
>> +        }
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return EOK;
>> +}
>> +
>>  /* Create a file object for parsing a config file */
>>  int ini_config_file_open(const char *filename,
>>                           int error_level,
>> @@ -135,16 +175,9 @@ int ini_config_file_open(const char *filename,
>>          return error;
>>      }
>>  
>> -
>>      new_ctx->filename = NULL;
>>      new_ctx->file = NULL;
>>      new_ctx->error_list = NULL;
>> -    new_ctx->metadata = NULL;
>> -
>> -    /* TBD - decide whether we actually need an FD.
>> -       It will be done when we move the metadata
>> -       processing into this function. */
>> -    new_ctx->fd = -1;
>>  
>>      /* Store flags */
>>      new_ctx->error_level = error_level;
>> @@ -172,39 +205,14 @@ int ini_config_file_open(const char *filename,
>>          return error;
>>      }
>>  
>> -    /* Open file */
>> -    TRACE_INFO_STRING("File", new_ctx->filename);
>> -    errno = 0;
>> -    new_ctx->file = fopen(new_ctx->filename, "r");
>> -    if (!(new_ctx->file)) {
>> -        error = errno;
>> -        TRACE_ERROR_NUMBER("Failed to open file", error);
>> -        ini_config_file_destroy(new_ctx);
>> -        return error;
>> -    }
>> -
>> -    /* Create internal collections */
>> -    error = col_create_collection(&(new_ctx->error_list),
>> -                                  INI_ERROR,
>> -                                  COL_CLASS_INI_PERROR);
>> -    if (error) {
>> -        TRACE_ERROR_NUMBER("Failed to create error list", error);
>> -        ini_config_file_destroy(new_ctx);
>> -        return error;
>> -    }
>> -
>> -    error = col_create_collection(&(new_ctx->metadata),
>> -                                  INI_METADATA,
>> -                                  COL_CLASS_INI_META);
>> -    if (error) {
>> -        TRACE_ERROR_NUMBER("Failed to create metadata collection", error);
>> +    /* Do common init */
>> +    error = common_file_init(new_ctx);
>> +    if(error) {
>> +        TRACE_ERROR_NUMBER("Failed to do common init", error);
>>          ini_config_file_destroy(new_ctx);
>>          return error;
>>      }
>>  
>> -
>> -    /* TBD - Add metadata processing here */
>> -
>>      *file_ctx = new_ctx;
>>      TRACE_FLOW_EXIT();
>>      return error;
>> @@ -236,12 +244,6 @@ int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
>>      new_ctx->filename = NULL;
>>      new_ctx->file = NULL;
>>      new_ctx->error_list = NULL;
>> -    new_ctx->metadata = NULL;
>> -
>> -    /* TBD - decide whether we actually need an FD.
>> -       It will be done when we move the metadata
>> -       processing into this function. */
>> -    new_ctx->fd = -1;
>>  
>>      /* Store flags */
>>      new_ctx->error_level = file_ctx_in->error_level;
>> @@ -259,39 +261,14 @@ int ini_config_file_reopen(struct ini_cfgfile *file_ctx_in,
>>          return error;
>>      }
>>  
>> -    /* Open file */
>> -    TRACE_INFO_STRING("File", new_ctx->filename);
>> -    errno = 0;
>> -    new_ctx->file = fopen(new_ctx->filename, "r");
>> -    if (!(new_ctx->file)) {
>> -        error = errno;
>> -        TRACE_ERROR_NUMBER("Failed to open file", error);
>> -        ini_config_file_destroy(new_ctx);
>> -        return error;
>> -    }
>> -
>> -    /* Create internal collections */
>> -    error = col_create_collection(&(new_ctx->error_list),
>> -                                  INI_ERROR,
>> -                                  COL_CLASS_INI_PERROR);
>> -    if (error) {
>> -        TRACE_ERROR_NUMBER("Failed to create error list", error);
>> -        ini_config_file_close(new_ctx);
>> -        return error;
>> -    }
>> -
>> -    error = col_create_collection(&(new_ctx->metadata),
>> -                                  INI_METADATA,
>> -                                  COL_CLASS_INI_META);
>> -    if (error) {
>> -        TRACE_ERROR_NUMBER("Failed to create metadata collection", error);
>> +    /* Do common init */
>> +    error = common_file_init(new_ctx);
>> +    if(error) {
>> +        TRACE_ERROR_NUMBER("Failed to do common init", error);
>>          ini_config_file_destroy(new_ctx);
>>          return error;
>>      }
>>  
>> -
>> -    /* TBD - Add metadata processing here */
>> -
>>      *file_ctx_out = new_ctx;
>>      TRACE_FLOW_EXIT();
>>      return error;
>> -- 
>> 1.5.5.6
>>
>> From 87be7cfc4b8cc143a582a38a0d024e166d91df35 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 19:46:15 -0500
>> Subject: [PATCH] [INI] Check access function
>>
>> Added check access constants and the check access function.
>> The function is effectively copied from ini_metadata.c
>> The flags are copied from ini_config.h
>> ---
>>  ini/ini_configobj.h |   35 +++++++++++++++++++++++
>>  ini/ini_fileobj.c   |   75 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 110 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index 898440252ef3a547566fe2b5e80753d7971a8e47..eccad71c7245f1d035c5133c13b6078fc3e3dc5b 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -104,6 +104,41 @@
>>   */
>>  
>>  /**
>> + * @defgroup accesscheck Access control check flags
>> + *
>> + * @{
>> + */
>> +
>> +/**
>> + * @brief Validate access mode
>> + *
>> + * If this flag is specified the mode parameter
>> + * will be matched against the permissions set on the file
>> + * using the provided mask.
>> + */
>> +#define INI_ACCESS_CHECK_MODE   0x00000001
>> +
>> +/**
>> + * @brief Validate uid
>> + *
>> + * Provided uid will be checked against uid
>> + * of the file.
>> + */
>> +#define INI_ACCESS_CHECK_UID   0x00000002
>> +
>> +/**
>> + * @brief Validate gid
>> + *
>> + * Provided gid will be checked against gid
>> + * of the file.
>> + */
>> +#define INI_ACCESS_CHECK_GID   0x00000004
>> +
>> +/**
>> + * @}
>> + */
>> +
>> +/**
>>   * @defgroup collisionflags Flags that define collision resolution logic.
>>   *
>>   * @{
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index c10b4bf954366ddae90f1d06c53acd0332968599..93a9372f3e37489862a33f1676ca68a532c62441 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -406,3 +406,78 @@ const char *ini_config_get_filename(struct ini_cfgfile *file_ctx)
>>      TRACE_FLOW_EXIT();
>>      return ret;
>>  }
>> +
>> +
>> +/* Check access */
>> +int ini_config_access_check(struct ini_cfgfile *file_ctx,
>> +                            uint32_t flags,
>> +                            uid_t uid,
>> +                            gid_t gid,
>> +                            mode_t mode,
>> +                            mode_t mask)
>> +{
>> +    int error = EOK;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    flags &= INI_ACCESS_CHECK_MODE |
>> +             INI_ACCESS_CHECK_GID |
>> +             INI_ACCESS_CHECK_UID;
>> +
>> +    if ((file_ctx == NULL) || (flags == 0)) {
>> +        TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
>> +        return EINVAL;
>> +
>> +    }
>> +
>> +    /* Check mode */
>> +    if (flags & INI_ACCESS_CHECK_MODE) {
>> +
>> +        TRACE_INFO_NUMBER("File mode as saved.",
>> +                          file_ctx->file_stats.st_mode);
>> +
>> +        file_ctx->file_stats.st_mode &= S_IRWXU | S_IRWXG | S_IRWXO;
>> +        TRACE_INFO_NUMBER("File mode adjusted.",
>> +                          file_ctx->file_stats.st_mode);
>> +
>> +        TRACE_INFO_NUMBER("Mode as provided.", mode);
>> +        mode &= S_IRWXU | S_IRWXG | S_IRWXO;
>> +        TRACE_INFO_NUMBER("Mode adjusted.", mode);
>> +
>> +        /* Adjust mask */
>> +        if (mask == 0) mask = S_IRWXU | S_IRWXG | S_IRWXO;
>> +        else mask &= S_IRWXU | S_IRWXG | S_IRWXO;
>> +
>> +        if ((mode & mask) != (file_ctx->file_stats.st_mode & mask)) {
>> +            TRACE_INFO_NUMBER("File mode:", (mode & mask));
>> +            TRACE_INFO_NUMBER("Mode adjusted.",
>> +                              (file_ctx->file_stats.st_mode & mask));
>> +            TRACE_ERROR_NUMBER("Access denied.", EACCES);
>> +            return EACCES;
>> +        }
>> +    }
>> +
>> +    /* Check uid */
>> +    if (flags & INI_ACCESS_CHECK_UID) {
>> +        if (file_ctx->file_stats.st_uid != uid) {
>> +            TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_uid);
>> +            TRACE_ERROR_NUMBER("GID passed in.", uid);
>> +            TRACE_ERROR_NUMBER("Access denied.", EACCES);
>> +            return EACCES;
>> +        }
>> +    }
>> +
>> +    /* Check gid */
>> +    if (flags & INI_ACCESS_CHECK_GID) {
>> +        if (file_ctx->file_stats.st_gid != gid) {
>> +            TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_gid);
>> +            TRACE_ERROR_NUMBER("GID passed in.", gid);
>> +            TRACE_ERROR_NUMBER("Access denied.", EACCES);
>> +            return EACCES;
>> +        }
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return error;
>> +
>> +}
>> -- 
>> 1.5.5.6
>>
>> From c4d2383282b898f4c5914bf720301cdcabf5eb51 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 21:24:08 -0500
>> Subject: [PATCH] [INI] Avoid double free
>>
>> I might squash this patch into one of the previous ones.
>> ---
>>  ini/ini_fileobj.c |    5 ++++-
>>  1 files changed, 4 insertions(+), 1 deletions(-)
>>
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index 93a9372f3e37489862a33f1676ca68a532c62441..2d47c8ff1d1a613dfc5baa8efd00b05c368f8447 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -82,7 +82,10 @@ void ini_config_file_close(struct ini_cfgfile *file_ctx)
>>      TRACE_FLOW_ENTRY();
>>  
>>      if(file_ctx) {
>> -        if(file_ctx->file) fclose(file_ctx->file);
>> +        if(file_ctx->file) {
>> +            fclose(file_ctx->file);
>> +            file_ctx->file = NULL;
>> +        }
>>      }
>>  
>>      TRACE_FLOW_EXIT();
>> -- 
>> 1.5.5.6
>>
>> From 81e90b377b432ef874fda11b6e82f6f0eb894807 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 21:28:59 -0500
>> Subject: [PATCH] [INI] Function to check for changes
>>
>> Added function to detect changes to the configuration file.
>> ---
>>  ini/ini_configobj.h |    4 ++--
>>  ini/ini_fileobj.c   |   37 +++++++++++++++++++++++++++++++++++++
>>  2 files changed, 39 insertions(+), 2 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index eccad71c7245f1d035c5133c13b6078fc3e3dc5b..913b91e6abed18d30ba256edc280a1d0aa9d76ef 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -335,8 +335,8 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx,
>>   * - device ID
>>   * - i-node
>>   */
>> -int ini_config_changed(struct ini_cfgfile *file_ctx,
>> -                       struct ini_cfgfile *file_ctx_saved,
>> +int ini_config_changed(struct ini_cfgfile *file_ctx1,
>> +                       struct ini_cfgfile *file_ctx2,
>>                         int *changed);
>>  
>>  
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index 2d47c8ff1d1a613dfc5baa8efd00b05c368f8447..6de963971a0e9501fb86c23ee5faf6bfb5824bf0 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -484,3 +484,40 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx,
>>      return error;
>>  
>>  }
>> +
>> +/* Determins if two file context different by comparing
>> + * - time stamp
>> + * - device ID
>> + * - i-node
>> + */
>> +int ini_config_changed(struct ini_cfgfile *file_ctx1,
>> +                       struct ini_cfgfile *file_ctx2,
>> +                       int *changed)
>> +{
>> +
>> +    int error = EOK;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    if ((file_ctx1 == NULL) ||
>> +        (file_ctx2 == NULL) ||
>> +        (changed == NULL)) {
>> +        TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
>> +        return EINVAL;
>> +    }
>> +
>> +    *changed = 0;
>> +
>> +    if((file_ctx1->file_stats.st_mtime !=
>> +        file_ctx2->file_stats.st_mtime) ||
>> +       (file_ctx1->file_stats.st_dev !=
>> +        file_ctx2->file_stats.st_dev) ||
>> +       (file_ctx1->file_stats.st_ino !=
>> +        file_ctx2->file_stats.st_ino)) {
>> +        TRACE_INFO_STRING("File changed!", "");
>> +        *changed = 1;
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return error;
>> +}
>> -- 
>> 1.5.5.6
>>
>> From 2ec4163bcbbcfa6c025b32cdbdbd3adf8c7a84f2 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 21:31:33 -0500
>> Subject: [PATCH] [INI] Tests for access and changes
>>
>> Patch adds two functions. One tests permissions,
>> another validates if the file has changed or not.
>> ---
>>  ini/ini_parse_ut.c |  248 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 248 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
>> index f0a95527aa315b559e5c6fa42740fe4a3ba4e4b3..f5b1c2c5b4e82de2baf29955901da28afc082cbb 100644
>> --- a/ini/ini_parse_ut.c
>> +++ b/ini/ini_parse_ut.c
>> @@ -625,10 +625,256 @@ int merge_section_test(void)
>>                    resname, checkname, error));
>>  
>>      return error;
>> +}
>> +
>> +int startup_test(void)
>> +{
>> +    int error = EOK;
>> +    struct ini_cfgfile *file_ctx = NULL;
>> +    struct ini_cfgobj *ini_config = NULL;
>> +    char **error_list = NULL;
>> +    char filename[PATH_MAX];
>> +    char *srcdir;
>> +
>> +    srcdir = getenv("srcdir");
>> +    sprintf(filename, "%s/ini/ini.d/foo.conf",
>> +                      (srcdir == NULL) ? "." : srcdir);
>> +
>> +    INIOUT(printf("<==== Startup test ====>\n"));
>> +
>> +    /* Open config file */
>> +    error = ini_config_file_open(filename,
>> +                                 INI_STOP_ON_NONE,
>> +                                 0,
>> +                                 INI_META_STATS,
>> +                                 &file_ctx);
>> +    if (error) {
>> +        printf("Failed to open file for reading. Error %d.\n",  error);
>> +        return error;
>> +    }
>> +
>> +    /* We will check just permissions here. */
>> +    error = ini_config_access_check(file_ctx,
>> +                                INI_ACCESS_CHECK_MODE, /* add uid & gui flags
>> +                                                        * in real case
>> +                                                        */
>> +                                0, /* <- will be real uid in real case */
>> +                                0, /* <- will be real gid in real case */
>> +                                0440, /* Checking for r--r----- */
>> +                                0);
>> +    /* This check is expected to fail since
>> +     * the actual permissions on the test file are: rw-rw-r--
>> +     */
>> +
>> +    if (!error) {
>> +        printf("Expected error got success!\n");
>> +        ini_config_file_destroy(file_ctx);
>> +        return EACCES;
>> +    }
>> +
>> +    error = ini_config_access_check(
>> +                        file_ctx,
>> +                        INI_ACCESS_CHECK_MODE, /* add uid & gui flags
>> +                                                * in real case
>> +                                                */
>> +                        0, /* <- will be real uid in real case */
>> +                        0, /* <- will be real gid in real case */
>> +                        0664, /* Checkling for rw-rw-r-- */
>> +                        0);
>> +
>> +    if (error) {
>> +        printf("Access check failed %d!\n", error);
>> +        ini_config_file_destroy(file_ctx);
>> +        return EACCES;
>> +    }
>> +
>> +
>> +    /* Create config object */
>> +    error = ini_config_create(&ini_config);
>> +    if (error) {
>> +        printf("Failed to create collection. Error %d.\n", error);
>> +        ini_config_file_destroy(file_ctx);
>> +        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);
>> +        }
>> +        /* We do not return here intentionally */
>> +    }
>> +
>> +    ini_config_file_destroy(file_ctx);
>> +
>> +    INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT));
>> +
>> +    ini_config_destroy(ini_config);
>> +
>> +    return 0;
>> +}
>> +
>> +int reload_test(void)
>> +{
>> +    int error = EOK;
>> +    struct ini_cfgfile *file_ctx = NULL;
>> +    struct ini_cfgfile *file_ctx_new = NULL;
>> +    char infile[PATH_MAX];
>> +    char outfile[PATH_MAX];
>> +    char command[PATH_MAX * 3];
>> +    char *srcdir;
>> +    char *builddir;
>> +    int changed = 0;
>> +
>> +    INIOUT(printf("<==== Reload test ====>\n"));
>> +
>> +    srcdir = getenv("srcdir");
>> +    sprintf(infile, "%s/ini/ini.d/foo.conf",
>> +                      (srcdir == NULL) ? "." : srcdir);
>> +    builddir = getenv("builddir");
>> +    sprintf(outfile, "%s/foo.conf",
>> +                      (builddir == NULL) ? "." : builddir);
>> +    sprintf(command, "cp %s %s", infile, outfile);
>> +    errno = 0;
>> +    if(system(command)) {
>> +        error = errno;
>> +        printf("Failed to run copy command %d.\n",  error);
>> +        return error;
>> +    }
>> +
>> +    /* Open config file */
>> +    error = ini_config_file_open(outfile,
>> +                                 INI_STOP_ON_NONE,
>> +                                 0,
>> +                                 INI_META_STATS,
>> +                                 &file_ctx);
>> +    if (error) {
>> +        printf("Failed to open file for reading. Error %d.\n",  error);
>> +        return error;
>> +    }
>> +
>> +    error = ini_config_access_check(
>> +                    file_ctx,
>> +                    INI_ACCESS_CHECK_MODE, /* add uid & gui flags
>> +                                            * in real case
>> +                                            */
>> +                    0, /* <- will be real uid in real case */
>> +                    0, /* <- will be real gid in real case */
>> +                    0664, /* Checkling for rw-rw-r-- */
>> +                    0);
>> +
>> +    if (error) {
>> +        printf("Access check failed %d!\n", error);
>> +        ini_config_file_destroy(file_ctx);
>> +        return EACCES;
>> +    }
>> +
>> +    /* ... Create config object and read configuration - not shown here.
>> +     *     See other examples ... */
>> +
>> +    /* Now close file but leave the context around */
>> +    ini_config_file_close(file_ctx);
>> +
>> +    /* Some time passed and we received a signal to reload... */
>> +    error = ini_config_file_reopen(file_ctx, &file_ctx_new);
>> +    if (error) {
>> +        printf("Failed to open file for reading. Error %d.\n",  error);
>> +        ini_config_file_destroy(file_ctx);
>> +        return error;
>> +    }
>> +
>> +    changed = 0;
>> +    error = ini_config_changed(file_ctx,
>> +                               file_ctx_new,
>> +                               &changed);
>> +    if (error) {
>> +        printf("Failed to compare files. Error %d.\n",  error);
>> +        ini_config_file_destroy(file_ctx);
>> +        ini_config_file_destroy(file_ctx_new);
>> +        return error;
>> +    }
>> +
>> +    /* Check if file changed */
>> +    if (changed) {
>> +        printf("File changed when it shouldn't. This is unexpected error.\n");
>> +        ini_config_file_destroy(file_ctx);
>> +        ini_config_file_destroy(file_ctx_new);
>> +        return EINVAL;
>> +    }
>> +
>> +    /* Close file */
>> +    ini_config_file_destroy(file_ctx_new);
>>  
>> +    /* Emulate as if file changed */
>> +    errno = 0;
>> +    if (unlink(outfile)) {
>> +        error = errno;
>> +        printf("Failed to delete file %d.\n",  error);
>> +        ini_config_file_destroy(file_ctx);
>> +        return error;
>> +    }
>> +
>> +    sleep(1);
>> +
>> +    errno = 0;
>> +    if (system(command)) {
>> +        error = errno;
>> +        printf("Failed to run copy command %d.\n",  error);
>> +        ini_config_file_destroy(file_ctx);
>> +        return error;
>> +    }
>> +
>> +    /* Read again */
>> +    file_ctx_new = NULL;
>> +    error = ini_config_file_reopen(file_ctx, &file_ctx_new);
>> +    if (error) {
>> +        printf("Failed to open file for reading. Error %d.\n",  error);
>> +        ini_config_file_destroy(file_ctx);
>> +        return error;
>> +    }
>>  
>> +    changed = 0;
>> +    error = ini_config_changed(file_ctx,
>> +                               file_ctx_new,
>> +                               &changed);
>> +    if (error) {
>> +        printf("Failed to compare files. Error %d.\n",  error);
>> +        ini_config_file_destroy(file_ctx);
>> +        ini_config_file_destroy(file_ctx_new);
>> +        return error;
>> +    }
>> +
>> +    /* Check if file changed */
>> +    if (!changed) {
>> +        printf("File did not change when it should. This is an error.\n");
>> +        ini_config_file_destroy(file_ctx);
>> +        ini_config_file_destroy(file_ctx_new);
>> +        return EINVAL;
>> +    }
>> +
>> +    /* We do not need original context any more. */
>> +    ini_config_file_destroy(file_ctx);
>> +
>> +    /* New context is now original context */
>> +    file_ctx = file_ctx_new;
>> +
>> +    /* ... Create config object and read configuration - not shown here.
>> +     *     See other examples ... */
>> +
>> +    ini_config_file_destroy(file_ctx);
>> +
>> +    return 0;
>>  }
>>  
>> +
>> +
>>  /* Main function of the unit test */
>>  int main(int argc, char *argv[])
>>  {
>> @@ -637,6 +883,8 @@ int main(int argc, char *argv[])
>>                          read_again_test,
>>                          merge_values_test,
>>                          merge_section_test,
>> +                        startup_test,
>> +                        reload_test,
>>                          NULL };
>>      test_fn t;
>>      int i = 0;
>> -- 
>> 1.5.5.6
>>
>> From 8654e77c640ac19e6997542589fbd1500ae6532d Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 21:45:39 -0500
>> Subject: [PATCH] [INI] Rename error print function
>>
>> All config file processing functions start with "ini_config".
>> The only function that does not comply is
>> ini_print_errors. We can't rename it since
>> it is a part of the current active interface.
>> I marked that function needs to be removed when we
>> remove old interface and created a copy with
>> the correct name. I also updated unit test accordingly.
>> ---
>>  ini/ini_configobj.h |    5 +----
>>  ini/ini_parse_ut.c  |    8 ++++----
>>  ini/ini_print.c     |   24 ++++++++++++++++++++++++
>>  3 files changed, 29 insertions(+), 8 deletions(-)
>>
>> diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
>> index 913b91e6abed18d30ba256edc280a1d0aa9d76ef..7089b62da31887e21eeffd6dceff76907ca9f1a6 100644
>> --- a/ini/ini_configobj.h
>> +++ b/ini/ini_configobj.h
>> @@ -300,10 +300,7 @@ 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);
>> -
>> -
>> -
>> +void ini_config_print_errors(FILE *file, char **error_list);
>>  
>>  /* Merge two configurations together creating a new one */
>>  int ini_config_merge(struct ini_cfgobj *first,
>> diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
>> index f5b1c2c5b4e82de2baf29955901da28afc082cbb..696735aacee25fc71f2b60708aaf3d0e14081f2c 100644
>> --- a/ini/ini_parse_ut.c
>> +++ b/ini/ini_parse_ut.c
>> @@ -85,7 +85,7 @@ int test_one_file(const char *in_filename,
>>              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));
>> +            INIOUT(ini_config_print_errors(stdout, error_list));
>>              ini_config_free_errors(error_list);
>>          }
>>          /* We do not return here intentionally */
>> @@ -371,7 +371,7 @@ int merge_values_test(void)
>>                  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));
>> +                INIOUT(ini_config_print_errors(stdout, error_list));
>>                  ini_config_free_errors(error_list);
>>              }
>>  
>> @@ -548,7 +548,7 @@ int merge_section_test(void)
>>                      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));
>> +                    INIOUT(ini_config_print_errors(stdout, error_list));
>>                      ini_config_free_errors(error_list);
>>                  }
>>  
>> @@ -706,7 +706,7 @@ int startup_test(void)
>>              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));
>> +            INIOUT(ini_config_print_errors(stdout, error_list));
>>              ini_config_free_errors(error_list);
>>          }
>>          /* We do not return here intentionally */
>> diff --git a/ini/ini_print.c b/ini/ini_print.c
>> index 1dcfa54bfd72daf2c790d04c37ff2b3107eb7295..42bd6fc221c71bf4f390a79e02a4928ecf747bd9 100644
>> --- a/ini/ini_print.c
>> +++ b/ini/ini_print.c
>> @@ -461,6 +461,9 @@ void print_config_parsing_errors(FILE *file,
>>  
>>  
>>  /* Function to print errors from the list */
>> +/* THIS FUNCTION SHOUD BE REMOVED WHEN
>> + * OLD INTERFACE IS REMOVED - TBD.
>> + */
>>  void ini_print_errors(FILE *file, char **error_list)
>>  {
>>      unsigned count = 0;
>> @@ -480,3 +483,24 @@ void ini_print_errors(FILE *file, char **error_list)
>>      TRACE_FLOW_EXIT();
>>      return;
>>  }
>> +
>> +/* Function to print errors from the list */
>> +void ini_config_print_errors(FILE *file, char **error_list)
>> +{
>> +    unsigned count = 0;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    if (!error_list) {
>> +        TRACE_FLOW_STRING("List is empty.", "");
>> +        return;
>> +    }
>> +
>> +    while (error_list[count]) {
>> +        fprintf(file, "%s\n", error_list[count]);
>> +        count++;
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return;
>> +}
>> -- 
>> 1.5.5.6
>>
>> From baa1b6eb9604729fa678a0988138a7aaa3f5c9c6 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Sun, 26 Dec 2010 21:58:24 -0500
>> Subject: [PATCH] [INI] Initialize variables in loops
>>
>> It occured to me that one of the issues that Coverity
>> did not like (and I could not understand what it is
>> complaining about) is related to intializing the variables
>> in the loop.
>> This patch adds initialization in the loops.
>> ---
>>  ini/ini_parse_ut.c    |    4 ++++
>>  ini/ini_valueobj_ut.c |    3 +++
>>  2 files changed, 7 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c
>> index 696735aacee25fc71f2b60708aaf3d0e14081f2c..5de79d954673a194bd080660f967c7b304dbdefe 100644
>> --- a/ini/ini_parse_ut.c
>> +++ b/ini/ini_parse_ut.c
>> @@ -343,6 +343,7 @@ int merge_values_test(void)
>>          INIOUT(printf("<==== Testing mode %s  ====>\n", mstr[i]));
>>  
>>          /* Create config collection */
>> +        ini_config = NULL;
>>          error = ini_config_create(&ini_config);
>>          if (error) {
>>              printf("Failed to create collection. Error %d.\n", error);
>> @@ -350,6 +351,7 @@ int merge_values_test(void)
>>              return error;
>>          }
>>  
>> +        file_ctx = NULL;
>>          error = ini_config_file_open(filename,
>>                                       INI_STOP_ON_ANY,
>>                                       mflags[i],
>> @@ -517,6 +519,7 @@ int merge_section_test(void)
>>              }
>>  
>>              /* Create config collection */
>> +            ini_config = NULL;
>>              error = ini_config_create(&ini_config);
>>              if (error) {
>>                  printf("Failed to create collection. "
>> @@ -525,6 +528,7 @@ int merge_section_test(void)
>>                  return error;
>>              }
>>  
>> +            file_ctx = NULL;
>>              error = ini_config_file_open(filename,
>>                                           INI_STOP_ON_ANY,
>>                                           msecflags[i] | mflags[j],
>> diff --git a/ini/ini_valueobj_ut.c b/ini/ini_valueobj_ut.c
>> index af62c140d215f9298dd5671d8aa544a84ef254b1..c07ec85772084f1fd6dd157a9befae26c9aa6806 100644
>> --- a/ini/ini_valueobj_ut.c
>> +++ b/ini/ini_valueobj_ut.c
>> @@ -404,6 +404,7 @@ int vo_basic_test(void)
>>              return error;
>>          }
>>  
>> +        vo = NULL;
>>          error = value_create_new(strvalue,
>>                                   strlen(strvalue),
>>                                   INI_VALUE_CREATED,
>> @@ -541,6 +542,7 @@ int vo_copy_test(void)
>>  
>>          TRACE_INFO_NUMBER("Iteration:", wrap);
>>  
>> +        vo_copy = NULL;
>>          error = value_copy(vo, &vo_copy);
>>          if (error) {
>>              printf("Failed to create a new value object %d.\n", error);
>> @@ -559,6 +561,7 @@ int vo_copy_test(void)
>>          }
>>  
>>          /* Get comment from the value */
>> +        ic = NULL;
>>          error = value_extract_comment(vo_copy, &ic);
>>          if (error) {
>>              printf("Failed to extract comment %d.\n", error);
>> -- 
>> 1.5.5.6
>>
>> From f9f8998bef402648d2565ada6789305e0c0ad46e Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Mon, 3 Jan 2011 14:36:47 -0500
>> Subject: [PATCH] [INI] Exposing functions
>>
>> This patch makes two internal functions
>> resusable from different source modules.
>> ---
>>  ini/ini_config_priv.h |    8 ++++++++
>>  ini/ini_fileobj.c     |    2 +-
>>  ini/ini_parse.c       |    2 +-
>>  3 files changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/ini/ini_config_priv.h b/ini/ini_config_priv.h
>> index 84742c70ceeb5b493b935c635e81c57d5ef487ce..e714774a68a5e16b7a221f712abe2ea9179e88c8 100644
>> --- a/ini/ini_config_priv.h
>> +++ b/ini/ini_config_priv.h
>> @@ -82,4 +82,12 @@ void ini_cleanup_cb(const char *property,
>>  /* Get parsing error */
>>  const char *ini_get_error_str(int parsing_error, int family);
>>  
>> +/* Check if collision flags are valid */
>> +int valid_collision_flags(uint32_t collision_flags);
>> +
>> +/* Empty section */
>> +int empty_section(struct collection_item *sec);
>> +
>> +
>> +
>>  #endif
>> diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
>> index 6de963971a0e9501fb86c23ee5faf6bfb5824bf0..30a2f1545d7b22dcab3bb4eccd53c91fe7edfd62 100644
>> --- a/ini/ini_fileobj.c
>> +++ b/ini/ini_fileobj.c
>> @@ -34,7 +34,7 @@
>>  
>>  
>>  /* Check if collision flags are valid */
>> -static int valid_collision_flags(uint32_t collision_flags)
>> +int valid_collision_flags(uint32_t collision_flags)
>>  {
>>      uint32_t flag;
>>  
>> diff --git a/ini/ini_parse.c b/ini/ini_parse.c
>> index 3f45ac71ae46dff3413a6d4db5dd59a0838d078b..e3ae938847b867780cb0d0d60d12868826952a85 100644
>> --- a/ini/ini_parse.c
>> +++ b/ini/ini_parse.c
>> @@ -357,7 +357,7 @@ static int check_section_collision(struct parser_obj *po)
>>  }
>>  
>>  /* Clean all items in the section */
>> -static int empty_section(struct collection_item *sec)
>> +int empty_section(struct collection_item *sec)
>>  {
>>      int error = EOK;
>>      struct collection_item *item = NULL;
>> -- 
>> 1.5.5.6
>>
>> From 02c14799196cd1ffd0f53c6b2e995ac17a7f8917 Mon Sep 17 00:00:00 2001
>> From: Dmitri Pal <dpal at redhat.com>
>> Date: Mon, 3 Jan 2011 14:41:03 -0500
>> Subject: [PATCH] [INI] Function to merge two configurations
>>
>> This patch provides first draft of the implementation
>> of the code to merge different configurations.
>> It is similar to the merge code that is implemented inside
>> the parser but different since it is not done during parsing
>> of one file but addresses use case when the configuration
>> is provided by different files that need to be merged together.
>>
>> NOTE: It would make more sence to review function by function
>> from bottom of the patch rather than from the top.
>> ---
>>  ini/ini_configobj.c |  456 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 456 insertions(+), 0 deletions(-)
>>
>> diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c
>> index 5e5e40b566abdf3ce2cbc225babe4b1e20c2dd47..d8583a224e8eebc4365df3909b6bac7c98477dc5 100644
>> --- a/ini/ini_configobj.c
>> +++ b/ini/ini_configobj.c
>> @@ -30,11 +30,19 @@
>>  #include "ini_config_priv.h"
>>  #include "ini_defines.h"
>>  #include "ini_valueobj.h"
>> +#include "ini_configobj.h"
>>  
>>  /* This constant belongs to ini_defines.h. Move from ini_config - TBD */
>>  #define COL_CLASS_INI_BASE        20000
>>  #define COL_CLASS_INI_CONFIG      COL_CLASS_INI_BASE + 0
>>  
>> +struct merge_data {
>> +    struct collection_item *ci;
>> +    uint32_t flags;
>> +    int error;
>> +    int found;
>> +};
>> +
>>  /* Callback */
>>  void ini_cleanup_cb(const char *property,
>>                      int property_len,
>> @@ -254,3 +262,451 @@ int ini_config_copy(struct ini_cfgobj *ini_config,
>>      TRACE_FLOW_EXIT();
>>      return error;
>>  }
>> +
>> +/* Callback to process merging of the sections */
>> +static int merge_section_handler(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;
>> +    struct value_obj *new_vo = NULL;
>> +    struct value_obj *vo_old = NULL;
>> +    struct merge_data *passed_data;
>> +    struct collection_item *acceptor = NULL;
>> +    struct collection_item *item = NULL;
>> +    unsigned insertmode;
>> +    uint32_t mergemode;
>> +    int suppress = 0;
>> +    int doinsert = 0;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    if ((type != COL_TYPE_BINARY) ||
>> +        ((type == COL_TYPE_BINARY) &&
>> +         (strncmp(property, INI_SECTION_KEY,
>> +                     sizeof(INI_SECTION_KEY)) == 0))) {
>> +        /* Skip items we do not care about */
>> +        TRACE_FLOW_EXIT();
>> +        return EOK;
>> +    }
>> +
>> +    /* Get value */
>> +    vo = *((struct value_obj **)(data));
>> +
>> +    /* Copy it */
>> +    error = value_copy(vo, &new_vo);
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed to copy value", error);
>> +        return error;
>> +    }
>> +
>> +    passed_data = (struct merge_data *)(custom_data);
>> +    acceptor = passed_data->ci;
>> +    mergemode = passed_data->flags & INI_MV2S_MASK;
>> +
>> +    switch (mergemode) {
>> +    case INI_MV2S_ERROR:     insertmode = COL_INSERT_DUPERROR;
>> +                             doinsert = 1;
>> +                             break;
>> +    case INI_MV2S_PRESERVE:  insertmode = COL_INSERT_DUPERROR;
>> +                             doinsert = 1;
>> +                             suppress = 1;
>> +                             break;
>> +    case INI_MV2S_ALLOW:     insertmode = COL_INSERT_NOCHECK;
>> +                             doinsert = 1;
>> +                             break;
>> +    case INI_MV2S_OVERWRITE: /* Special handling */
>> +    case INI_MV2S_DETECT:
>> +    default:
>> +                             break;
>> +    }
>> +
>> +    /* Do not insert but search for dups first */
>> +    if (!doinsert) {
>> +        TRACE_INFO_STRING("Overwrite mode. Looking for:",
>> +                          property);
>> +
>> +        error = col_get_item(acceptor,
>> +                             property,
>> +                             COL_TYPE_BINARY,
>> +                             COL_TRAVERSE_DEFAULT,
>> +                             &item);
>> +
>> +        if (error) {
>> +            TRACE_ERROR_NUMBER("Failed searching for dup", error);
>> +            value_destroy(new_vo);
>> +            return error;
>> +        }
>> +
>> +        /* Check if there is a dup */
>> +        if (item) {
>> +            /* Check if we are in the detect mode */
>> +            if (mergemode == INI_MV2S_DETECT) {
>> +                passed_data->error = EEXIST;
>> +                doinsert = 1;
>> +                insertmode = COL_INSERT_NOCHECK;
>> +            }
>> +            else {
>> +
>> +                /* Dup exists - update it */
>> +                vo_old = *((struct value_obj **)(col_get_item_data(item)));
>> +                error = col_modify_binary_item(item,
>> +                                               NULL,
>> +                                               &new_vo,
>> +                                               sizeof(struct value_obj *));
>> +                if (error) {
>> +                    TRACE_ERROR_NUMBER("Failed updating the value", error);
>> +                    value_destroy(new_vo);
>> +                    return error;
>> +                }
>> +
>> +                /* If we failed to update it is better to leak then crash,
>> +                 * so destroy 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(acceptor,
>> +                                           NULL,
>> +                                           COL_DSP_END,
>> +                                           NULL,
>> +                                           0,
>> +                                           insertmode,
>> +                                           property,
>> +                                           &new_vo,
>> +                                           sizeof(struct value_obj *));
>> +        if (error) {
>> +            value_destroy(new_vo);
>> +
>> +            if ((suppress) && (error == EEXIST)) {
>> +                TRACE_INFO_STRING("Preseved exisitng value",
>> +                                  property);
>> +            }
>> +            else {
>> +                /* Check if this is a critical error or not */
>> +                if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) {
>> +                    TRACE_ERROR_NUMBER("Failed to add value object "
>> +                                       "to the section", error);
>> +                    passed_data->error = EEXIST;
>> +                    *dummy = 1;
>> +                }
>> +                else {
>> +                    TRACE_ERROR_NUMBER("Failed to add value object"
>> +                                       " to the section", error);
>> +                    return error;
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return error;
>> +}
>> +
>> +
>> +/* Internal function to merge two configs */
>> +static int merge_two_sections(struct collection_item *acceptor,
>> +                              struct collection_item *donor,
>> +                              uint32_t flags)
>> +{
>> +    int error = EOK;
>> +    struct merge_data data;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    data.ci = acceptor;
>> +    data.flags = flags;
>> +    data.error = 0;
>> +    data.found = 0;
>> +
>> +    error = col_traverse_collection(donor,
>> +                                    COL_TRAVERSE_ONELEVEL,
>> +                                    merge_section_handler,
>> +                                    (void *)(&data));
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Merge values failed", error);
>> +        return error;
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return data.error;
>> +}
>> +
>> +
>> +
>> +/* Callback to process the accepting config */
>> +static int acceptor_handler(const char *property,
>> +                            int property_len,
>> +                            int type,
>> +                            void *data,
>> +                            int length,
>> +                            void *custom_data,
>> +                            int *dummy)
>> +{
>> +    int error = EOK;
>> +    struct merge_data *passed_data;
>> +    struct collection_item *acceptor = NULL;
>> +    struct collection_item *donor = NULL;
>> +    uint32_t mergemode;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    passed_data = (struct merge_data *)(custom_data);
>> +    passed_data->found = 1;
>> +
>> +    donor = passed_data->ci;
>> +    acceptor = *((struct collection_item **)(data));
>> +
>> +    mergemode = passed_data->flags & INI_MS_MASK;
>> +
>> +    switch (mergemode) {
>> +    case INI_MS_ERROR:      /* Report error and return */
>> +                            TRACE_INFO_STRING("Error ",
>> +                                              "duplicate section");
>> +                            passed_data->error = EEXIST;
>> +                            break;
>> +
>> +    case INI_MS_PRESERVE:   /* Preserve what we have */
>> +                            TRACE_INFO_STRING("Preserve mode", "");
>> +                            break;
>> +
>> +    case INI_MS_OVERWRITE:  /* Empty existing section */
>> +                            TRACE_INFO_STRING("Ovewrite mode", "");
>> +                            error = empty_section(acceptor);
>> +                            if (error) {
>> +                                TRACE_ERROR_NUMBER("Failed to "
>> +                                                    "empty section",
>> +                                                    error);
>> +                                return error;
>> +                            }
>> +                            error = merge_two_sections(acceptor,
>> +                                                       donor,
>> +                                                       passed_data->flags);
>> +                            if (error) {
>> +                                TRACE_ERROR_NUMBER("Failed to merge "
>> +                                                    "sections", error);
>> +                                return error;
>> +                            }
>> +                            break;
>> +
>> +    case INI_MS_DETECT:     /* Detect mode */
>> +                            TRACE_INFO_STRING("Detect mode", "");
>> +                            passed_data->error = EEXIST;
>> +                            passed_data->found = 0;
>> +                            break;
>> +
>> +    case INI_MS_MERGE:      /* Merge */
>> +    default:                TRACE_INFO_STRING("Merge mode", "");
>> +                            error = merge_two_sections(acceptor,
>> +                                                       donor,
>> +                                                       passed_data->flags);
>> +                            if (error) {
>> +                                if (error != EEXIST) {
>> +                                    TRACE_ERROR_NUMBER("Failed to merge "
>> +                                                        "sections", error);
>> +                                    return error;
>> +                                }
>> +                                passed_data->error = error;
>> +                            }
>> +                            break;
>> +    }
>> +
>> +    *dummy = 1;
>> +    TRACE_FLOW_EXIT();
>> +    return EOK;
>> +}
>> +
>> +/* Callback to process the donating config */
>> +static int donor_handler(const char *property,
>> +                         int property_len,
>> +                         int type,
>> +                         void *data,
>> +                         int length,
>> +                         void *custom_data,
>> +                         int *dummy)
>> +{
>> +    int error = EOK;
>> +    struct merge_data *passed_data;
>> +    struct merge_data acceptor_data;
>> +    struct collection_item *new_ci = NULL;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    passed_data = (struct merge_data *)(custom_data);
>> +
>> +    /* All section are subcollections */
>> +    if(type == COL_TYPE_COLLECTIONREF) {
>> +
>> +        acceptor_data.flags = passed_data->flags;
>> +        acceptor_data.ci = *((struct collection_item **)(data));
>> +        acceptor_data.error = 0;
>> +        acceptor_data.found = 0;
>> +
>> +        /* Try to resolve collision only non ALLOW modes */
>> +        if (!(acceptor_data.flags & INI_MS_ALLOW)) {
>> +            error = col_get_item_and_do(passed_data->ci,
>> +                                        property,
>> +                                        COL_TYPE_COLLECTIONREF,
>> +                                        COL_TRAVERSE_ONELEVEL,
>> +                                        acceptor_handler,
>> +                                        (void *)(&acceptor_data));
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Critical error", error);
>> +                return error;
>> +            }
>> +        }
>> +
>> +        /* Was duplicate found ? */
>> +        if (acceptor_data.found) {
>> +            /* Check for logical error. It can be only EEXIST */
>> +            if (acceptor_data.error) {
>> +                /* Save error anyway */
>> +                passed_data->error = acceptor_data.error;
>> +                /* If it is section DETECT or MERGE+DETECT */
>> +                if ((passed_data->flags & INI_MS_DETECT) ||
>> +                    ((passed_data->flags & INI_MS_MERGE) &&
>> +                     (passed_data->flags & INI_MV2S_DETECT))) {
>> +                    TRACE_INFO_NUMBER("Non-critical error",
>> +                                      acceptor_data.error);
>> +                }
>> +                else {
>> +                    /* In any other mode we need to stop */
>> +                    TRACE_INFO_NUMBER("Merge error detected",
>> +                                      acceptor_data.error);
>> +                    /* Force stop */
>> +                    *dummy = 1;
>> +                }
>> +            }
>> +        }
>> +        else {
>> +            /* Not found? Then create a copy... */
>> +            error = col_copy_collection_with_cb(&new_ci,
>> +                                                acceptor_data.ci,
>> +                                                NULL,
>> +                                                COL_COPY_NORMAL,
>> +                                                ini_copy_cb,
>> +                                                NULL);
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Failed to copy collection", error);
>> +                return error;
>> +            }
>> +
>> +            /* ... and embed into the existing collection */
>> +            error = col_add_collection_to_collection(passed_data->ci,
>> +                                                     NULL,
>> +                                                     NULL,
>> +                                                     new_ci,
>> +                                                     COL_ADD_MODE_EMBED);
>> +            if (error) {
>> +                TRACE_ERROR_NUMBER("Failed to copy collection", error);
>> +                col_destroy_collection(new_ci);
>> +                return error;
>> +            }
>> +        }
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return EOK;
>> +}
>> +
>> +
>> +/* Internal function to merge two configs */
>> +static int merge_configs(struct ini_cfgobj *first,
>> +                         struct ini_cfgobj *second,
>> +                         uint32_t collision_flags)
>> +{
>> +    int error = EOK;
>> +    struct merge_data data;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    data.ci = first->cfg;
>> +    data.flags = collision_flags;
>> +    data.error = 0;
>> +    data.found = 0;
>> +
>> +    error = col_traverse_collection(second->cfg,
>> +                                    COL_TRAVERSE_ONELEVEL,
>> +                                    donor_handler,
>> +                                    (void *)(&data));
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Merge failed", error);
>> +        return error;
>> +    }
>> +
>> +    /* If boundaries are different re-align the values */
>> +    if (first->boundary != second->boundary) {
>> +
>> +        error = ini_config_set_wrap(first, first->boundary);
>> +        if (error) {
>> +            TRACE_ERROR_NUMBER("Failed to re-align", error);
>> +            return error;
>> +        }
>> +    }
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return error;
>> +}
>> +
>> +
>> +/* Merge two configurations together creating a new one */
>> +int ini_config_merge(struct ini_cfgobj *first,
>> +                     struct ini_cfgobj *second,
>> +                     uint32_t collision_flags,
>> +                     struct ini_cfgobj **result)
>> +{
>> +    int error = EOK;
>> +    struct ini_cfgobj *new_co = NULL;
>> +
>> +    TRACE_FLOW_ENTRY();
>> +
>> +    /* Check input params */
>> +    if ((!first) ||
>> +        (!second) ||
>> +        (!result)) {
>> +        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
>> +        return EINVAL;
>> +    }
>> +
>> +    /* Check collision flags */
>> +    if (!valid_collision_flags(collision_flags)) {
>> +        TRACE_ERROR_NUMBER("Invalid flags.", EINVAL);
>> +        return EINVAL;
>> +    }
>> +
>> +    /* Create a copy */
>> +    /* TBD - create a COMPACT copy */
>> +    error = ini_config_copy(first, &new_co);
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed to copy collection", error);
>> +        return error;
>> +    }
>> +
>> +    /* Merge configs */
>> +    error = merge_configs(new_co, second, collision_flags);
>> +    if (error) {
>> +        TRACE_ERROR_NUMBER("Failed to copy collection", error);
>> +        ini_config_destroy(new_co);
>> +        return error;
>> +    }
>> +
>> +    *result = new_co;
>> +
>> +    TRACE_FLOW_EXIT();
>> +    return error;
>> +
>> +}
>> -- 
>> 1.5.5.6
>>
>> _______________________________________________
>> sssd-devel mailing list
>> sssd-devel at lists.fedorahosted.org
>> https://fedorahosted.org/mailman/listinfo/sssd-devel
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel


-- 
Thank you,
Dmitri Pal

Sr. Engineering Manager IPA project,
Red Hat Inc.


-------------------------------
Looking to carve out IT costs?
www.redhat.com/carveoutcosts/



-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0016-INI-Separate-close-and-destroy.patch
Type: text/x-patch
Size: 5263 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0017-INI-Function-to-reopen-file.patch
Type: text/x-patch
Size: 4007 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0001.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0018-INI-Metadata-collection-is-gone.patch
Type: text/x-patch
Size: 8226 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0002.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0019-INI-Check-access-function.patch
Type: text/x-patch
Size: 3989 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0003.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0020-INI-Function-to-check-for-changes.patch
Type: text/x-patch
Size: 2078 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0004.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0021-INI-Tests-for-access-and-changes.patch
Type: text/x-patch
Size: 9801 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0005.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0022-INI-Rename-error-print-function.patch
Type: text/x-patch
Size: 4200 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0006.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0023-INI-Initialize-variables-in-loops.patch
Type: text/x-patch
Size: 3265 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0007.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0024-INI-Exposing-functions.patch
Type: text/x-patch
Size: 1665 bytes
Desc: not available
URL: <https://lists.fedorahosted.org/pipermail/sssd-devel/attachments/20120318/8c0144e2/attachment-0008.bin>


More information about the sssd-devel mailing list