LCOV - code coverage report
Current view: top level - ini - ini_configobj.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 300 328 91.5 %
Date: 2014-04-01 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /*
       2             :     INI LIBRARY
       3             : 
       4             :     Module represents interface to the main INI object.
       5             : 
       6             :     Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
       7             : 
       8             :     INI Library is free software: you can redistribute it and/or modify
       9             :     it under the terms of the GNU Lesser General Public License as published by
      10             :     the Free Software Foundation, either version 3 of the License, or
      11             :     (at your option) any later version.
      12             : 
      13             :     INI Library is distributed in the hope that it will be useful,
      14             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :     GNU Lesser General Public License for more details.
      17             : 
      18             :     You should have received a copy of the GNU Lesser General Public License
      19             :     along with INI Library.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "config.h"
      23             : #include <errno.h>
      24             : #include <stdio.h>
      25             : #include <string.h>
      26             : #include <stdint.h>
      27             : #include <stdlib.h>
      28             : /* For error text */
      29             : #include <libintl.h>
      30             : #define _(String) gettext (String)
      31             : #include "trace.h"
      32             : #include "collection.h"
      33             : #include "collection_tools.h"
      34             : #include "ini_configobj.h"
      35             : #include "ini_config_priv.h"
      36             : #include "ini_defines.h"
      37             : #include "ini_valueobj.h"
      38             : #include "ini_configobj.h"
      39             : 
      40             : /* Internal structure used during the merge operation */
      41             : struct merge_data {
      42             :     struct collection_item *ci;
      43             :     uint32_t flags;
      44             :     int error;
      45             :     int found;
      46             : };
      47             : 
      48             : /* Callback */
      49      139360 : void ini_cleanup_cb(const char *property,
      50             :                     int property_len,
      51             :                     int type,
      52             :                     void *data,
      53             :                     int length,
      54             :                     void *custom_data)
      55             : {
      56      139360 :     struct value_obj *vo = NULL;
      57             : 
      58             :     TRACE_FLOW_ENTRY();
      59             :     TRACE_INFO_STRING("Cleaning ", property);
      60             : 
      61             :     /* Banary items are the values */
      62      139360 :     if(type == COL_TYPE_BINARY) {
      63      133473 :         vo = *((struct value_obj **)(data));
      64      133473 :         value_destroy(vo);
      65             :     }
      66             : 
      67             :     TRACE_FLOW_EXIT();
      68      139360 : }
      69             : 
      70             : /* Clean the search state */
      71         501 : void ini_config_clean_state(struct ini_cfgobj *ini_config)
      72             : {
      73             :     TRACE_FLOW_ENTRY();
      74             : 
      75         501 :     if (ini_config) {
      76         501 :         if (ini_config->iterator) col_unbind_iterator(ini_config->iterator);
      77         501 :         ini_config->iterator = NULL;
      78         501 :         free(ini_config->section);
      79         501 :         ini_config->section = NULL;
      80         501 :         free(ini_config->name);
      81         501 :         ini_config->name = NULL;
      82         501 :         ini_config->section_len = 0;
      83         501 :         ini_config->name_len = 0;
      84             :     }
      85             : 
      86             :     TRACE_FLOW_EXIT();
      87         501 : }
      88             : 
      89             : 
      90             : 
      91             : /* Traverse the collection and clean the object */
      92         425 : void ini_config_destroy(struct ini_cfgobj *ini_config)
      93             : {
      94             :     TRACE_FLOW_ENTRY();
      95             : 
      96         425 :     ini_config_clean_state(ini_config);
      97             : 
      98         425 :     if (ini_config) {
      99         425 :         if (ini_config->cfg) {
     100             : 
     101         425 :             col_destroy_collection_with_cb(ini_config->cfg,
     102             :                                            ini_cleanup_cb,
     103             :                                            NULL);
     104             :         }
     105         425 :         if (ini_config->last_comment) {
     106         186 :             ini_comment_destroy(ini_config->last_comment);
     107             :         }
     108             : 
     109         425 :         if (ini_config->error_list) {
     110         246 :             col_destroy_collection(ini_config->error_list);
     111             :         }
     112             : 
     113         425 :         free(ini_config);
     114             :     }
     115             : 
     116             :     TRACE_FLOW_EXIT();
     117         425 : }
     118             : 
     119             : /* Create a config object */
     120         246 : int ini_config_create(struct ini_cfgobj **ini_config)
     121             : {
     122         246 :     int error = EOK;
     123         246 :     struct ini_cfgobj *new_co = NULL;
     124             : 
     125             :     TRACE_FLOW_ENTRY();
     126             : 
     127         246 :     if (!ini_config) {
     128             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
     129             :         return EINVAL;
     130             :     }
     131             : 
     132         246 :     new_co = malloc(sizeof(struct ini_cfgobj));
     133         246 :     if (!new_co) {
     134             :         TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
     135             :         return ENOMEM;
     136             :     }
     137             : 
     138         246 :     new_co->cfg = NULL;
     139         246 :     new_co->boundary = INI_WRAP_BOUNDARY;
     140         246 :     new_co->last_comment = NULL;
     141         246 :     new_co->section = NULL;
     142         246 :     new_co->name = NULL;
     143         246 :     new_co->section_len = 0;
     144         246 :     new_co->name_len = 0;
     145         246 :     new_co->iterator = NULL;
     146         246 :     new_co->error_list = NULL;
     147         246 :     new_co->count = 0;
     148             : 
     149             :     /* Create a collection to hold configuration data */
     150         246 :     error = col_create_collection(&(new_co->cfg),
     151             :                                   INI_CONFIG_NAME,
     152             :                                   COL_CLASS_INI_CONFIG);
     153         246 :     if (error != EOK) {
     154             :         TRACE_ERROR_NUMBER("Failed to create collection.", error);
     155           0 :         ini_config_destroy(new_co);
     156           0 :         return error;
     157             :     }
     158             : 
     159             :     /* Create error list collection */
     160         246 :     error = col_create_collection(&(new_co->error_list),
     161             :                                   INI_ERROR,
     162             :                                   COL_CLASS_INI_PERROR);
     163         246 :     if (error) {
     164             :         TRACE_ERROR_NUMBER("Failed to create error list", error);
     165           0 :         ini_config_destroy(new_co);
     166           0 :         return error;
     167             :     }
     168             : 
     169         246 :     *ini_config = new_co;
     170             : 
     171             :     TRACE_FLOW_EXIT();
     172         246 :     return error;
     173             : }
     174             : 
     175             : /* Callback to set the boundary */
     176       68138 : static int ini_boundary_cb(const char *property,
     177             :                            int property_len,
     178             :                            int type,
     179             :                            void *data,
     180             :                            int length,
     181             :                            void *custom_data,
     182             :                            int *dummy)
     183             : {
     184       68138 :     int error = EOK;
     185       68138 :     struct value_obj *vo = NULL;
     186             :     uint32_t boundary;
     187             : 
     188             :     TRACE_FLOW_ENTRY();
     189             : 
     190       68138 :     boundary = *((uint32_t *)(custom_data));
     191             :     /* Banary items are the values */
     192       68138 :     if(type == COL_TYPE_BINARY) {
     193       65744 :         vo = *((struct value_obj **)(data));
     194       65744 :         error = value_set_boundary(vo, boundary);
     195             :     }
     196             : 
     197             :     TRACE_FLOW_EXIT();
     198       68138 :     return error;
     199             : }
     200             : 
     201             : /* Set the folding boundary for multiline values.
     202             :  * Use before serializing and saving to a file if the
     203             :  * default boundary of 80 characters does not work for you.
     204             :  */
     205         154 : int ini_config_set_wrap(struct ini_cfgobj *ini_config,
     206             :                         uint32_t boundary)
     207             : {
     208         154 :     int error = EOK;
     209             : 
     210             :     TRACE_FLOW_ENTRY();
     211             : 
     212         154 :     if (!ini_config) {
     213             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
     214             :         return EINVAL;
     215             :     }
     216             : 
     217         154 :     ini_config->boundary = boundary;
     218         154 :     error = col_traverse_collection(ini_config->cfg,
     219             :                                     COL_TRAVERSE_DEFAULT,
     220             :                                     ini_boundary_cb,
     221         154 :                                     (void *)(&(ini_config->boundary)));
     222             :     if (error) {
     223             :         TRACE_ERROR_NUMBER("Failed to set wrapping boundary", error);
     224             :         return error;
     225             :     }
     226             : 
     227             : 
     228             :     TRACE_FLOW_EXIT();
     229             :     return error;
     230             : }
     231             : 
     232             : /* Configuration copy callback */
     233       66243 : static int ini_copy_cb(struct collection_item *item,
     234             :                        void *ext_data,
     235             :                        int *skip)
     236             : {
     237       66243 :     int error = EOK;
     238       66243 :     struct value_obj *vo = NULL;
     239       66243 :     struct value_obj *new_vo = NULL;
     240             : 
     241             :     TRACE_FLOW_ENTRY();
     242             : 
     243       66243 :     *skip = 0;
     244             : 
     245             :     /* Binary items are the values */
     246       66243 :     if(col_get_item_type(item) == COL_TYPE_BINARY) {
     247       66243 :         vo = *((struct value_obj **)(col_get_item_data(item)));
     248             : 
     249       66243 :         error = value_copy(vo, &new_vo);
     250       66243 :         if (error) {
     251             :             TRACE_ERROR_NUMBER("Failed to copy value", error);
     252             :             return error;
     253             :         }
     254             : 
     255       66243 :         error = col_modify_binary_item(item,
     256             :                                        NULL,
     257             :                                        &new_vo,
     258             :                                        sizeof(struct value_obj *));
     259       66243 :         if (error) {
     260             :             TRACE_ERROR_NUMBER("Failed to copy value", error);
     261           0 :             value_destroy(new_vo);
     262           0 :             return error;
     263             :         }
     264             :     }
     265             : 
     266             :     TRACE_FLOW_EXIT();
     267       66243 :     return error;
     268             : }
     269             : 
     270             : /* Copy configuration */
     271         179 : int ini_config_copy(struct ini_cfgobj *ini_config,
     272             :                     struct ini_cfgobj **ini_new)
     273             : {
     274         179 :     int error = EOK;
     275         179 :     struct ini_cfgobj *new_co = NULL;
     276             : 
     277             :     TRACE_FLOW_ENTRY();
     278             : 
     279         358 :     if ((!ini_config) ||
     280         179 :         (!ini_new)) {
     281             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
     282             :         return EINVAL;
     283             :     }
     284             : 
     285             :     /* Create a new configuration object */
     286         179 :     new_co = malloc(sizeof(struct ini_cfgobj));
     287         179 :     if (!new_co) {
     288             :         TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
     289             :         return ENOMEM;
     290             :     }
     291             : 
     292         179 :     new_co->cfg = NULL;
     293         179 :     new_co->boundary = ini_config->boundary;
     294         179 :     new_co->last_comment = NULL;
     295         179 :     new_co->section = NULL;
     296         179 :     new_co->name = NULL;
     297         179 :     new_co->section_len = 0;
     298         179 :     new_co->name_len = 0;
     299         179 :     new_co->iterator = NULL;
     300         179 :     new_co->error_list = NULL;
     301         179 :     new_co->count = 0;
     302             : 
     303         179 :     error = col_copy_collection_with_cb(&(new_co->cfg),
     304             :                                         ini_config->cfg,
     305             :                                         INI_CONFIG_NAME,
     306             :                                         COL_COPY_NORMAL,
     307             :                                         ini_copy_cb,
     308             :                                         NULL);
     309         179 :     if (error) {
     310             :         TRACE_ERROR_NUMBER("Failed to copy collection", error);
     311           0 :         ini_config_destroy(new_co);
     312           0 :         return error;
     313             :     }
     314             : 
     315         179 :     if (ini_config->last_comment) {
     316          67 :         error = ini_comment_copy(ini_config->last_comment,
     317             :                                  &(new_co->last_comment));
     318          67 :         if (error) {
     319             :             TRACE_ERROR_NUMBER("Failed to copy comment", error);
     320           0 :             ini_config_destroy(new_co);
     321           0 :             return error;
     322             :         }
     323             :     }
     324             : 
     325         179 :     *ini_new = new_co;
     326             : 
     327             :     TRACE_FLOW_EXIT();
     328         179 :     return error;
     329             : }
     330             : 
     331             : 
     332             : /* Callback to process merging of the sections */
     333         181 : static int merge_section_handler(const char *property,
     334             :                                  int property_len,
     335             :                                  int type,
     336             :                                  void *data,
     337             :                                  int length,
     338             :                                  void *custom_data,
     339             :                                  int *dummy)
     340             : {
     341         181 :     int error = EOK;
     342         181 :     struct value_obj *vo = NULL;
     343         181 :     struct value_obj *new_vo = NULL;
     344         181 :     struct value_obj *vo_old = NULL;
     345             :     struct merge_data *passed_data;
     346         181 :     struct collection_item *acceptor = NULL;
     347         181 :     struct collection_item *item = NULL;
     348             :     unsigned insertmode;
     349             :     uint32_t mergemode;
     350         181 :     int suppress = 0;
     351         181 :     int doinsert = 0;
     352             : 
     353             :     TRACE_FLOW_ENTRY();
     354             : 
     355         181 :     if ((type != COL_TYPE_BINARY) ||
     356         153 :         ((type == COL_TYPE_BINARY) &&
     357         153 :          (strncmp(property, INI_SECTION_KEY,
     358             :                      sizeof(INI_SECTION_KEY)) == 0))) {
     359             :         /* Skip items we do not care about */
     360             :         TRACE_FLOW_EXIT();
     361             :         return EOK;
     362             :     }
     363             : 
     364             :     /* Get value */
     365         125 :     vo = *((struct value_obj **)(data));
     366             : 
     367             :     /* Copy it */
     368         125 :     error = value_copy(vo, &new_vo);
     369         125 :     if (error) {
     370             :         TRACE_ERROR_NUMBER("Failed to copy value", error);
     371             :         return error;
     372             :     }
     373             : 
     374         125 :     passed_data = (struct merge_data *)(custom_data);
     375         125 :     acceptor = passed_data->ci;
     376         125 :     mergemode = passed_data->flags & INI_MV2S_MASK;
     377             : 
     378         125 :     switch (mergemode) {
     379           5 :     case INI_MV2S_ERROR:     insertmode = COL_INSERT_DUPERROR;
     380           5 :                              doinsert = 1;
     381           5 :                              break;
     382          15 :     case INI_MV2S_PRESERVE:  insertmode = COL_INSERT_DUPERROR;
     383          15 :                              doinsert = 1;
     384          15 :                              suppress = 1;
     385          15 :                              break;
     386          45 :     case INI_MV2S_ALLOW:     insertmode = COL_INSERT_NOCHECK;
     387          45 :                              doinsert = 1;
     388          45 :                              break;
     389             :     case INI_MV2S_OVERWRITE: /* Special handling */
     390             :     case INI_MV2S_DETECT:
     391             :     default:
     392             :                              break;
     393             :     }
     394             : 
     395             :     /* Do not insert but search for dups first */
     396         125 :     if (!doinsert) {
     397             :         TRACE_INFO_STRING("Overwrite mode. Looking for:",
     398             :                           property);
     399             : 
     400          60 :         error = col_get_item(acceptor,
     401             :                              property,
     402             :                              COL_TYPE_BINARY,
     403             :                              COL_TRAVERSE_DEFAULT,
     404             :                              &item);
     405             : 
     406          60 :         if (error) {
     407             :             TRACE_ERROR_NUMBER("Failed searching for dup", error);
     408           0 :             value_destroy(new_vo);
     409           0 :             return error;
     410             :         }
     411             : 
     412             :         /* Check if there is a dup */
     413          60 :         if (item) {
     414             :             /* Check if we are in the detect mode */
     415          46 :             if (mergemode == INI_MV2S_DETECT) {
     416          38 :                 passed_data->error = EEXIST;
     417          38 :                 doinsert = 1;
     418          38 :                 insertmode = COL_INSERT_NOCHECK;
     419             :             }
     420             :             else {
     421             : 
     422             :                 /* We are in the OVERWRITE mode.
     423             :                  * Dup exists - update it.
     424             :                  */
     425           8 :                 vo_old = *((struct value_obj **)(col_get_item_data(item)));
     426           8 :                 error = col_modify_binary_item(item,
     427             :                                                NULL,
     428             :                                                &new_vo,
     429             :                                                sizeof(struct value_obj *));
     430           8 :                 if (error) {
     431             :                     TRACE_ERROR_NUMBER("Failed updating the value", error);
     432           0 :                     value_destroy(new_vo);
     433           0 :                     return error;
     434             :                 }
     435             : 
     436             :                 /* If we failed to update it is better to leak then crash,
     437             :                  * so destroy original value only on the successful update.
     438             :                  */
     439           8 :                 value_destroy(vo_old);
     440             :             }
     441             :         }
     442             :         else {
     443             :             /* No dup found so we can insert with no check */
     444             :             doinsert = 1;
     445             :             insertmode = COL_INSERT_NOCHECK;
     446             :         }
     447             :     }
     448             : 
     449         125 :     if (doinsert) {
     450             :         /* Add value to collection */
     451         117 :         error = col_insert_binary_property(acceptor,
     452             :                                            NULL,
     453             :                                            COL_DSP_END,
     454             :                                            NULL,
     455             :                                            0,
     456             :                                            insertmode,
     457             :                                            property,
     458             :                                            &new_vo,
     459             :                                            sizeof(struct value_obj *));
     460         117 :         if (error) {
     461          12 :             value_destroy(new_vo);
     462             : 
     463          12 :             if ((suppress) && (error == EEXIST)) {
     464             :                 /* We are here is we do not allow dups
     465             :                  * but found one and need to ignore it.
     466             :                  */
     467             :                 TRACE_INFO_STRING("Preseved exisitng value",
     468             :                                   property);
     469             :                 error = 0;
     470             :             }
     471             :             else {
     472             :                 /* Check if this is a critical error or not */
     473           4 :                 if ((mergemode == INI_MV2S_ERROR) && (error == EEXIST)) {
     474             :                     TRACE_ERROR_NUMBER("Failed to add value object to "
     475             :                                        "the section in error mode ", error);
     476           4 :                     passed_data->error = EEXIST;
     477           4 :                     *dummy = 1;
     478             :                 }
     479             :                 else {
     480             :                     TRACE_ERROR_NUMBER("Failed to add value object"
     481             :                                        " to the section", error);
     482             :                     return error;
     483             :                 }
     484             :             }
     485             :         }
     486             :     }
     487             : 
     488             :     TRACE_FLOW_EXIT();
     489         125 :     return error;
     490             : }
     491             : 
     492             : 
     493             : /* Internal function to merge two configs */
     494          28 : static int merge_two_sections(struct collection_item *donor,
     495             :                               struct collection_item *acceptor,
     496             :                               uint32_t flags)
     497             : {
     498          28 :     int error = EOK;
     499             :     struct merge_data data;
     500             : 
     501             :     TRACE_FLOW_ENTRY();
     502             : 
     503          28 :     data.ci = acceptor;
     504          28 :     data.flags = flags;
     505          28 :     data.error = 0;
     506          28 :     data.found = 0;
     507             : 
     508          28 :     error = col_traverse_collection(donor,
     509             :                                     COL_TRAVERSE_ONELEVEL,
     510             :                                     merge_section_handler,
     511             :                                     (void *)(&data));
     512          28 :     if (error) {
     513             :         TRACE_ERROR_NUMBER("Merge values failed", error);
     514             :         return error;
     515             :     }
     516             : 
     517             :     TRACE_FLOW_EXIT();
     518          28 :     return data.error;
     519             : }
     520             : 
     521             : 
     522             : 
     523             : /* Callback to process the accepting config */
     524          43 : static int acceptor_handler(const char *property,
     525             :                             int property_len,
     526             :                             int type,
     527             :                             void *data,
     528             :                             int length,
     529             :                             void *custom_data,
     530             :                             int *dummy)
     531             : {
     532          43 :     int error = EOK;
     533             :     struct merge_data *passed_data;
     534          43 :     struct collection_item *acceptor = NULL;
     535          43 :     struct collection_item *donor = NULL;
     536             :     uint32_t mergemode;
     537             : 
     538             :     TRACE_FLOW_ENTRY();
     539             : 
     540             :     /* This callback is called when the dup section is found */
     541          43 :     passed_data = (struct merge_data *)(custom_data);
     542          43 :     passed_data->found = 1;
     543             : 
     544          43 :     donor = passed_data->ci;
     545          43 :     acceptor = *((struct collection_item **)(data));
     546             : 
     547          43 :     mergemode = passed_data->flags & INI_MS_MASK;
     548             : 
     549          43 :     switch (mergemode) {
     550             :     case INI_MS_ERROR:      /* Report error and return */
     551             :                             TRACE_INFO_STRING("Error ",
     552             :                                               "duplicate section");
     553           5 :                             passed_data->error = EEXIST;
     554           5 :                             break;
     555             : 
     556             :     case INI_MS_PRESERVE:   /* Preserve what we have */
     557             :                             TRACE_INFO_STRING("Preserve mode", "");
     558             :                             break;
     559             : 
     560             :     case INI_MS_OVERWRITE:  /* Empty existing section */
     561             :                             TRACE_INFO_STRING("Ovewrite mode", "");
     562           9 :                             error = empty_section(acceptor);
     563           9 :                             if (error) {
     564             :                                 TRACE_ERROR_NUMBER("Failed to "
     565             :                                                     "empty section",
     566             :                                                     error);
     567             :                                 return error;
     568             :                             }
     569           9 :                             error = merge_two_sections(donor,
     570             :                                                        acceptor,
     571             :                                                        passed_data->flags);
     572           9 :                             if (error) {
     573             :                                 TRACE_ERROR_NUMBER("Failed to merge "
     574             :                                                     "sections", error);
     575           3 :                                 if (error == EEXIST) {
     576           3 :                                     passed_data->error = error;
     577             :                                 }
     578           3 :                                 return error;
     579             :                             }
     580             :                             break;
     581             : 
     582             :     case INI_MS_DETECT:     /* Detect mode */
     583             :                             TRACE_INFO_STRING("Detect mode", "");
     584          10 :                             passed_data->error = EEXIST;
     585          10 :                             error = merge_two_sections(donor,
     586             :                                                        acceptor,
     587             :                                                        passed_data->flags);
     588          10 :                             if (error) {
     589           4 :                                 if (error != EEXIST) {
     590             :                                     TRACE_ERROR_NUMBER("Failed to merge "
     591             :                                                        "sections", error);
     592             :                                     return error;
     593             :                                 }
     594             :                             }
     595             :                             break;
     596             : 
     597             :     case INI_MS_MERGE:      /* Merge */
     598             :     default:                TRACE_INFO_STRING("Merge mode", "");
     599           9 :                             error = merge_two_sections(donor,
     600             :                                                        acceptor,
     601             :                                                        passed_data->flags);
     602           9 :                             if (error) {
     603           3 :                                 if (error != EEXIST) {
     604             :                                     TRACE_ERROR_NUMBER("Failed to merge "
     605             :                                                        "sections", error);
     606             :                                     return error;
     607             :                                 }
     608           3 :                                 passed_data->error = error;
     609             :                             }
     610             :                             break;
     611             :     }
     612             : 
     613          40 :     *dummy = 1;
     614             :     TRACE_FLOW_EXIT();
     615          40 :     return EOK;
     616             : }
     617             : 
     618             : /* Callback to process the donating config */
     619          86 : static int donor_handler(const char *property,
     620             :                          int property_len,
     621             :                          int type,
     622             :                          void *data,
     623             :                          int length,
     624             :                          void *custom_data,
     625             :                          int *dummy)
     626             : {
     627          86 :     int error = EOK;
     628             :     struct merge_data *passed_data;
     629             :     struct merge_data acceptor_data;
     630          86 :     struct collection_item *new_ci = NULL;
     631             : 
     632             :     TRACE_FLOW_ENTRY();
     633             : 
     634          86 :     *dummy = 0;
     635             : 
     636             :     /* Opaque data passed to callback is merge data */
     637          86 :     passed_data = (struct merge_data *)(custom_data);
     638             : 
     639             :     TRACE_INFO_STRING("Property: ", property);
     640             :     TRACE_INFO_NUMBER("Type is: ", type);
     641             :     TRACE_INFO_NUMBER("Flags: ", passed_data->flags);
     642             : 
     643             :     /* All sections are subcollections */
     644          86 :     if(type == COL_TYPE_COLLECTIONREF) {
     645             : 
     646             :         /* Prepare data for the next callback */
     647          61 :         acceptor_data.flags = passed_data->flags;
     648          61 :         acceptor_data.ci = *((struct collection_item **)(data));
     649          61 :         acceptor_data.error = 0;
     650          61 :         acceptor_data.found = 0;
     651             : 
     652             :         /* Try to find same section as the current one */
     653          61 :         error = col_get_item_and_do(passed_data->ci,
     654             :                                     property,
     655             :                                     COL_TYPE_COLLECTIONREF,
     656             :                                     COL_TRAVERSE_ONELEVEL,
     657             :                                     acceptor_handler,
     658             :                                     (void *)(&acceptor_data));
     659          61 :         if (error) {
     660             :             TRACE_ERROR_NUMBER("Critical error", error);
     661             :             return error;
     662             :         }
     663             : 
     664             :         /* Was duplicate found ? */
     665          61 :         if (acceptor_data.found) {
     666             :             /* Check for logical error. It can be only EEXIST */
     667          43 :             if (acceptor_data.error) {
     668             :                 /* Save error anyway */
     669          21 :                 passed_data->error = acceptor_data.error;
     670             :                 /* If it is section DETECT or MERGE+DETECT */
     671          21 :                 if (((passed_data->flags & INI_MS_MASK) == INI_MS_DETECT) ||
     672           6 :                     (((passed_data->flags & INI_MS_MASK) != INI_MS_ERROR) &&
     673           6 :                      ((passed_data->flags & INI_MV2S_MASK) ==
     674             :                        INI_MV2S_DETECT))) {
     675             :                     TRACE_INFO_NUMBER("Non-critical error",
     676             :                                       acceptor_data.error);
     677             :                 }
     678             :                 else {
     679             :                     /* In any other mode we need to stop */
     680             :                     TRACE_INFO_NUMBER("Merge error detected",
     681             :                                       acceptor_data.error);
     682             :                     /* Force stop */
     683           7 :                     *dummy = 1;
     684             :                 }
     685             :             }
     686             :         }
     687             :         else {
     688             :             /* Not found? Then create a copy... */
     689          18 :             error = col_copy_collection_with_cb(&new_ci,
     690             :                                                 acceptor_data.ci,
     691             :                                                 NULL,
     692             :                                                 COL_COPY_NORMAL,
     693             :                                                 ini_copy_cb,
     694             :                                                 NULL);
     695          18 :             if (error) {
     696             :                 TRACE_ERROR_NUMBER("Failed to copy collection", error);
     697             :                 return error;
     698             :             }
     699             : 
     700             :             /* ... and embed into the existing collection */
     701          18 :             error = col_add_collection_to_collection(passed_data->ci,
     702             :                                                      NULL,
     703             :                                                      NULL,
     704             :                                                      new_ci,
     705             :                                                      COL_ADD_MODE_EMBED);
     706          18 :             if (error) {
     707             :                 TRACE_ERROR_NUMBER("Failed to copy collection", error);
     708           0 :                 col_destroy_collection(new_ci);
     709           0 :                 return error;
     710             :             }
     711             :         }
     712             :     }
     713             : 
     714             :     TRACE_FLOW_EXIT();
     715             :     return EOK;
     716             : }
     717             : 
     718          17 : static int merge_comment(struct ini_cfgobj *donor,
     719             :                          struct ini_cfgobj *acceptor)
     720             : {
     721          17 :     int error = EOK;
     722             : 
     723             :     TRACE_FLOW_ENTRY();
     724             : 
     725          17 :     if (donor->last_comment) {
     726             : 
     727          17 :         if (acceptor->last_comment) {
     728             : 
     729          17 :             error = ini_comment_add(donor->last_comment,
     730             :                                     acceptor->last_comment);
     731          17 :             if (error) {
     732             :                 TRACE_ERROR_NUMBER("Merge comment failed", error);
     733             :                 return error;
     734             :             }
     735             : 
     736             :         }
     737             :         else {
     738           0 :             error = ini_comment_copy(donor->last_comment,
     739             :                                      &(acceptor->last_comment));
     740           0 :             if (error) {
     741             :                 TRACE_ERROR_NUMBER("Copy comment failed", error);
     742             :                 return error;
     743             :             }
     744             :         }
     745             :     }
     746             : 
     747             :     TRACE_FLOW_EXIT();
     748             :     return EOK;
     749             : }
     750             : 
     751             : 
     752             : 
     753             : /* Internal function to merge two configs */
     754          42 : static int merge_configs(struct ini_cfgobj *donor,
     755             :                          struct ini_cfgobj *acceptor,
     756             :                          uint32_t collision_flags)
     757             : {
     758          25 :     int error = EOK;
     759             :     struct merge_data data;
     760             : 
     761             :     TRACE_FLOW_ENTRY();
     762             : 
     763          25 :     data.ci = acceptor->cfg;
     764          25 :     data.flags = collision_flags;
     765          25 :     data.error = 0;
     766          25 :     data.found = 0;
     767             : 
     768             :     /* Loop through the donor collection calling
     769             :      * donor_handler callback for every section we find.
     770             :      */
     771          25 :     error = col_traverse_collection(donor->cfg,
     772             :                                     COL_TRAVERSE_ONELEVEL,
     773             :                                     donor_handler,
     774             :                                     (void *)(&data));
     775          25 :     if (error) {
     776             :         TRACE_ERROR_NUMBER("Merge failed", error);
     777             :         return error;
     778             :     }
     779             : 
     780             :     /* Check if we got error */
     781          39 :     if ((data.error) &&
     782          23 :         (((collision_flags & INI_MS_MASK) == INI_MS_ERROR) ||
     783           9 :          ((collision_flags & INI_MV2S_MASK) == INI_MV2S_ERROR))) {
     784             :         TRACE_ERROR_NUMBER("Got error in error mode", data.error);
     785             :         return data.error;
     786             :     }
     787             : 
     788             :     /* If boundaries are different re-align the values */
     789          17 :     if (acceptor->boundary != donor->boundary) {
     790           0 :         error = ini_config_set_wrap(acceptor, acceptor->boundary);
     791           0 :         if (error) {
     792             :             TRACE_ERROR_NUMBER("Failed to re-align", error);
     793             :             return error;
     794             :         }
     795             :     }
     796             : 
     797             :     /* Merge last comment */
     798          17 :     error = merge_comment(donor, acceptor);
     799          17 :     if (error) {
     800             :         TRACE_ERROR_NUMBER("Failed to merge comment", error);
     801             :         return error;
     802             :     }
     803             : 
     804             :     /* Check if we got error */
     805          23 :     if ((data.error) &&
     806           8 :         (((collision_flags & INI_MS_MASK) == INI_MS_DETECT) ||
     807           2 :          ((collision_flags & INI_MV2S_MASK) == INI_MV2S_DETECT))) {
     808             :         TRACE_ERROR_NUMBER("Got error in error or detect mode", data.error);
     809           6 :         error = data.error;
     810             :     }
     811             : 
     812             :     TRACE_FLOW_EXIT();
     813          17 :     return error;
     814             : }
     815             : 
     816             : /* Check if collision flags are valid */
     817         271 : int valid_collision_flags(uint32_t collision_flags)
     818             : {
     819             :     uint32_t flag;
     820             : 
     821             :     TRACE_FLOW_ENTRY();
     822             : 
     823         271 :     flag = collision_flags & INI_MV1S_MASK;
     824         271 :     if ((flag != INI_MV1S_OVERWRITE) &&
     825             :         (flag != INI_MV1S_ERROR) &&
     826             :         (flag != INI_MV1S_PRESERVE) &&
     827             :         (flag != INI_MV1S_ALLOW) &&
     828             :         (flag != INI_MV1S_DETECT)) {
     829             :         TRACE_ERROR_STRING("Invalid value collision flag","");
     830             :         return 0;
     831             :     }
     832             : 
     833         271 :     flag = collision_flags & INI_MV2S_MASK;
     834         542 :     if ((flag != INI_MV2S_OVERWRITE) &&
     835         271 :         (flag != INI_MV2S_ERROR) &&
     836         142 :         (flag != INI_MV2S_PRESERVE) &&
     837          81 :         (flag != INI_MV2S_ALLOW) &&
     838             :         (flag != INI_MV2S_DETECT)) {
     839             :         TRACE_ERROR_STRING("Invalid value cross-section collision flag","");
     840             :         return 0;
     841             :     }
     842             : 
     843         271 :     flag = collision_flags & INI_MS_MASK;
     844         542 :     if ((flag != INI_MS_MERGE) &&
     845         271 :         (flag != INI_MS_OVERWRITE) &&
     846         100 :         (flag != INI_MS_ERROR) &&
     847          70 :         (flag != INI_MS_PRESERVE) &&
     848             :         (flag != INI_MS_DETECT)) {
     849             :         TRACE_ERROR_STRING("Invalid section collision flag","");
     850             :         return 0;
     851             :     }
     852             : 
     853             :     TRACE_FLOW_EXIT();
     854         271 :     return 1;
     855             : }
     856             : 
     857             : /* Merge two configurations together creating a new one */
     858          25 : int ini_config_merge(struct ini_cfgobj *first,
     859             :                      struct ini_cfgobj *second,
     860             :                      uint32_t collision_flags,
     861             :                      struct ini_cfgobj **result)
     862             : {
     863          25 :     int error = EOK;
     864          25 :     struct ini_cfgobj *new_co = NULL;
     865             : 
     866             :     TRACE_FLOW_ENTRY();
     867             : 
     868             :     /* Check input params */
     869          50 :     if ((!first) ||
     870          50 :         (!second) ||
     871             :         (!result)) {
     872             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
     873             :         return EINVAL;
     874             :     }
     875             : 
     876             :     /* Check collision flags */
     877          25 :     if (!valid_collision_flags(collision_flags)) {
     878             :         TRACE_ERROR_NUMBER("Invalid flags.", EINVAL);
     879             :         return EINVAL;
     880             :     }
     881             : 
     882             :     /* NOTE: We assume that the configuration we merge to
     883             :      * is consistent regarding duplicate values.
     884             :      * For example, if the duplicates are not allowed,
     885             :      * the parsing function should have been instructed
     886             :      * to not allow duplicates.
     887             :      * If in future we decide to be explicite we would need
     888             :      * to introduce a "compacting" function and call it here
     889             :      * after we create a copy.
     890             :      * For now it is treated as a corner case and thus not worth
     891             :      * implementing.
     892             :      */
     893             : 
     894             :     /* Create a new config object */
     895          25 :     error = ini_config_copy(first, &new_co);
     896          25 :     if (error) {
     897             :         TRACE_ERROR_NUMBER("Failed to copy configuration", error);
     898             :         return error;
     899             :     }
     900             : 
     901             :     /* Merge configs */
     902          25 :     error = merge_configs(second, new_co, collision_flags);
     903          25 :     if (error) {
     904             :         TRACE_ERROR_NUMBER("Failed to merge configuration", error);
     905          28 :         if ((error == EEXIST) &&
     906          19 :             ((((collision_flags & INI_MS_MASK) == INI_MS_DETECT) &&
     907          15 :               ((collision_flags & INI_MV2S_MASK) != INI_MV2S_ERROR)) ||
     908           5 :              (((collision_flags & INI_MS_MASK) != INI_MS_ERROR) &&
     909           5 :               ((collision_flags & INI_MV2S_MASK) == INI_MV2S_DETECT)))) {
     910             :             TRACE_ERROR_NUMBER("Got error in detect mode", error);
     911             :             /* Fall through! */
     912             :         }
     913             :         else {
     914             :             /* Got an error in any other mode */
     915             :             TRACE_ERROR_NUMBER("Got error in non detect mode", error);
     916           8 :             ini_config_destroy(new_co);
     917           8 :             return error;
     918             :         }
     919             :     }
     920             : 
     921          17 :     *result = new_co;
     922             :     TRACE_FLOW_EXIT();
     923          17 :     return error;
     924             : 
     925             : }
     926             : 
     927             : /* How many errors do we have in the list ? */
     928          32 : unsigned ini_config_error_count(struct ini_cfgobj *cfg_ctx)
     929             : {
     930          32 :     unsigned count = 0;
     931             : 
     932             :     TRACE_FLOW_ENTRY();
     933             : 
     934          32 :     count = cfg_ctx->count;
     935             : 
     936             :     TRACE_FLOW_EXIT();
     937          32 :     return count;
     938             : 
     939             : }
     940             : 
     941             : /* Free error strings */
     942          26 : void ini_config_free_errors(char **errors)
     943             : {
     944             :     TRACE_FLOW_ENTRY();
     945             : 
     946          26 :     col_free_property_list(errors);
     947             : 
     948             :     TRACE_FLOW_EXIT();
     949          26 : }
     950             : 
     951             : /* Get the list of error strings */
     952          26 : int ini_config_get_errors(struct ini_cfgobj *cfg_ctx,
     953             :                           char ***errors)
     954             : {
     955          26 :     char **errlist = NULL;
     956          26 :     struct collection_iterator *iterator = NULL;
     957             :     int error;
     958          26 :     struct collection_item *item = NULL;
     959             :     struct ini_parse_error *pe;
     960          26 :     unsigned int count = 0;
     961             :     char *line;
     962             : 
     963             :     TRACE_FLOW_ENTRY();
     964             : 
     965             :     /* If we have something to print print it */
     966          26 :     if ((!errors) || (!cfg_ctx)) {
     967             :         TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
     968             :         return EINVAL;
     969             :     }
     970             : 
     971          26 :     errlist = calloc(cfg_ctx->count + 1, sizeof(char *));
     972          26 :     if (!errlist) {
     973             :         TRACE_ERROR_NUMBER("Failed to allocate memory for errors.", ENOMEM);
     974             :         return ENOMEM;
     975             :     }
     976             : 
     977             :     /* Bind iterator */
     978          26 :     error =  col_bind_iterator(&iterator,
     979             :                                cfg_ctx->error_list,
     980             :                                COL_TRAVERSE_DEFAULT);
     981          26 :     if (error) {
     982             :         TRACE_ERROR_NUMBER("Faile to bind iterator:", error);
     983           0 :         ini_config_free_errors(errlist);
     984           0 :         return error;
     985             :     }
     986             : 
     987             :     while(1) {
     988             :         /* Loop through a collection */
     989         143 :         error = col_iterate_collection(iterator, &item);
     990         143 :         if (error) {
     991             :             TRACE_ERROR_NUMBER("Error iterating collection", error);
     992           0 :             col_unbind_iterator(iterator);
     993           0 :             ini_config_free_errors(errlist);
     994           0 :             return error;
     995             :         }
     996             : 
     997             :         /* Are we done ? */
     998         143 :         if (item == NULL) break;
     999             : 
    1000             :         /* Process collection header */
    1001         117 :         if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
    1002          26 :             continue;
    1003             :         }
    1004             :         else {
    1005             :             /* Put error into provided format */
    1006          91 :             pe = (struct ini_parse_error *)(col_get_item_data(item));
    1007             : 
    1008             :             /* Would be nice to have asprintf function...
    1009             :              * ...but for now we know that all the errors
    1010             :              * are pretty short and will fir into the predefined
    1011             :              * error length buffer.
    1012             :              */
    1013          91 :             line = malloc(MAX_ERROR_LINE + 1);
    1014          91 :             if (!line) {
    1015             :                 TRACE_ERROR_NUMBER("Failed to get memory for error.", ENOMEM);
    1016           0 :                 col_unbind_iterator(iterator);
    1017           0 :                 ini_config_free_errors(errlist);
    1018           0 :                 return ENOMEM;
    1019             :             }
    1020             : 
    1021          91 :             snprintf(line, MAX_ERROR_LINE, LINE_FORMAT,
    1022             :                      col_get_item_property(item, NULL),
    1023             :                      pe->error,
    1024             :                      pe->line,
    1025             :                      ini_get_error_str(pe->error,
    1026             :                                        INI_FAMILY_PARSING));
    1027             : 
    1028          91 :             errlist[count] = line;
    1029          91 :             count++;
    1030             :         }
    1031             : 
    1032             :     }
    1033             : 
    1034             :     /* Do not forget to unbind iterator - otherwise there will be a leak */
    1035          26 :     col_unbind_iterator(iterator);
    1036             : 
    1037          26 :     *errors = errlist;
    1038             : 
    1039             :     TRACE_FLOW_EXIT();
    1040          26 :     return error;
    1041             : }

Generated by: LCOV version 1.10