LCOV - code coverage report
Current view: top level - ini - ini_parse.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 516 586 88.1 %
Date: 2014-04-01 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /*
       2             :     INI LIBRARY
       3             : 
       4             :     Low level parsing functions
       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 <string.h>
      25             : #include <ctype.h>
      26             : /* For error text */
      27             : #include <libintl.h>
      28             : #define _(String) gettext (String)
      29             : #include "trace.h"
      30             : #include "ini_defines.h"
      31             : #include "ini_valueobj.h"
      32             : #include "ini_config_priv.h"
      33             : #include "ini_configobj.h"
      34             : #include "collection.h"
      35             : #include "collection_queue.h"
      36             : 
      37             : #define INI_WARNING 0xA0000000 /* Warning bit */
      38             : 
      39             : /* This constant belongs to ini_defines.h. Move from ini_config - TBD */
      40             : #define COL_CLASS_INI_BASE        20000
      41             : #define COL_CLASS_INI_SECTION     COL_CLASS_INI_BASE + 1
      42             : /**
      43             :  * @brief Name of the default section.
      44             :  *
      45             :  * This is the name of the implied section where orphan key-value
      46             :  * pairs will be put.
      47             :  */
      48             : #define INI_DEFAULT_SECTION "default"
      49             : 
      50             : 
      51             : struct parser_obj {
      52             :     /* Externally passed and saved data */
      53             :     FILE *file;
      54             :     struct collection_item *top;
      55             :     struct collection_item *el;
      56             :     const char *filename;
      57             :     struct ini_cfgobj *co;
      58             :     /* Level of error reporting */
      59             :     int error_level;
      60             :     /* Collistion flags */
      61             :     uint32_t collision_flags;
      62             :     /* Parseing flags */
      63             :     uint32_t parse_flags;
      64             :     /* Wrapping boundary */
      65             :     uint32_t boundary;
      66             :     /* Action queue */
      67             :     struct collection_item *queue;
      68             :     /* Last error */
      69             :     uint32_t last_error;
      70             :     /* Last line number */
      71             :     uint32_t linenum;
      72             :     /* Line number of the last found key */
      73             :     uint32_t keylinenum;
      74             :     /* Line number of the last found section */
      75             :     uint32_t seclinenum;
      76             :     /* Internal variables */
      77             :     struct collection_item *sec;
      78             :     struct collection_item *merge_sec;
      79             :     struct ini_comment *ic;
      80             :     char *last_read;
      81             :     uint32_t last_read_len;
      82             :     int inside_comment;
      83             :     char *key;
      84             :     uint32_t key_len;
      85             :     struct ref_array *raw_lines;
      86             :     struct ref_array *raw_lengths;
      87             :     char *merge_key;
      88             :     struct value_obj *merge_vo;
      89             :     /* Merge error */
      90             :     uint32_t merge_error;
      91             :     int ret;
      92             : };
      93             : 
      94             : typedef int (*action_fn)(struct parser_obj *);
      95             : 
      96             : #define PARSE_ACTION       "action"
      97             : 
      98             : /* Actions */
      99             : #define PARSE_READ      0 /* Read from the file */
     100             : #define PARSE_INSPECT   1 /* Process read string */
     101             : #define PARSE_POST      2 /* Reading is complete  */
     102             : #define PARSE_ERROR     3 /* Handle error */
     103             : #define PARSE_DONE      4 /* We are done */
     104             : 
     105             : /* Declarations of the reusble functions: */
     106             : static int complete_value_processing(struct parser_obj *po);
     107             : static int save_error(struct collection_item *el,
     108             :                       unsigned line,
     109             :                       int error,
     110             :                       const char *err_txt);
     111             : 
     112             : 
     113          82 : static int is_just_spaces(const char *str, uint32_t len)
     114             : {
     115             :     uint32_t i;
     116             : 
     117             :     TRACE_FLOW_ENTRY();
     118             : 
     119         227 :     for (i = 0; i < len; i++) {
     120         227 :         if (!isspace(str[i])) return 0;
     121             :     }
     122             : 
     123             :     TRACE_FLOW_EXIT();
     124             :     return 1;
     125             : }
     126             : 
     127             : /* Functions checks whether the line
     128             :  * starts with the sequence of allowed blank characters.
     129             :  * If spaces are allowed - function will say that line
     130             :  * is OK. If tabls are allowed the function also would
     131             :  * say that line is OK. If the mixture of both is allowed
     132             :  * the line is OK too.
     133             :  * Any other character will cause an error.
     134             :  */
     135          16 : static int is_allowed_spaces(const char *str,
     136             :                              uint32_t len,
     137             :                              uint32_t parse_flags,
     138             :                              int *error)
     139             : {
     140             :     uint32_t i;
     141          16 :     int line_ok = 1;
     142             : 
     143             :     TRACE_FLOW_ENTRY();
     144             : 
     145          41 :     for (i = 0; i < len; i++) {
     146          62 :         if ((str[i] == ' ') &&
     147          21 :             (parse_flags & INI_PARSE_NOSPACE)) {
     148             :             /* Leading spaces are not allowed */
     149           5 :             *error = ERR_SPACE;
     150           5 :             line_ok = 0;
     151           5 :             break;
     152             :         }
     153          50 :         else if ((str[i] == '\t') &&
     154          14 :             (parse_flags & INI_PARSE_NOTAB)) {
     155             :             /* Leading tabs are not allowed */
     156           5 :             *error = ERR_TAB;
     157           5 :             line_ok = 0;
     158           5 :             break;
     159             :         }
     160          31 :         else if ((str[i] == '\f') ||
     161          31 :                  (str[i] == '\n') ||
     162          31 :                  (str[i] == '\r') ||
     163             :                  (str[i] == '\v')) {
     164           0 :             *error = ERR_SPECIAL;
     165           0 :             line_ok = 0;
     166           0 :             break;
     167             :         }
     168          31 :         if (!isblank(str[i])) break;
     169             :     }
     170             : 
     171             :     TRACE_FLOW_EXIT();
     172          16 :     return line_ok;
     173             : }
     174             : 
     175             : /* Destroy parser object */
     176         246 : static void parser_destroy(struct parser_obj *po)
     177             : {
     178             :     TRACE_FLOW_ENTRY();
     179             : 
     180         246 :     if(po) {
     181         246 :         col_destroy_queue(po->queue);
     182         246 :         col_destroy_collection_with_cb(po->sec, ini_cleanup_cb, NULL);
     183         246 :         ini_comment_destroy(po->ic);
     184         246 :         value_destroy_arrays(po->raw_lines,
     185             :                              po->raw_lengths);
     186         246 :         if (po->last_read) free(po->last_read);
     187         246 :         if (po->key) free(po->key);
     188         246 :         col_destroy_collection_with_cb(po->top, ini_cleanup_cb, NULL);
     189         246 :         free(po);
     190             :     }
     191             : 
     192             :     TRACE_FLOW_EXIT();
     193         246 : }
     194             : 
     195             : /* Create parse object
     196             :  *
     197             :  * It assumes that the ini collection
     198             :  * has been precreated.
     199             :  */
     200         246 : static int parser_create(struct ini_cfgobj *co,
     201             :                          FILE *file,
     202             :                          const char *config_filename,
     203             :                          int error_level,
     204             :                          uint32_t collision_flags,
     205             :                          uint32_t parse_flags,
     206             :                          struct parser_obj **po)
     207             : {
     208         246 :     int error = EOK;
     209         246 :     struct parser_obj *new_po = NULL;
     210         246 :     unsigned count = 0;
     211             : 
     212             :     TRACE_FLOW_ENTRY();
     213             : 
     214             :     /* Make sure that all the parts are initialized */
     215         492 :     if ((!po) ||
     216         492 :         (!co) ||
     217         246 :         (!(co->cfg)) ||
     218         492 :         (!file) ||
     219         246 :         (!config_filename)) {
     220             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
     221             :         return EINVAL;
     222             :     }
     223             : 
     224         246 :     error = col_get_collection_count(co->cfg, &count);
     225         246 :     if (error) {
     226             :         TRACE_ERROR_NUMBER("Failed to check object size", error);
     227             :         return error;
     228             :     }
     229             : 
     230         246 :     if (count != 1) {
     231             :         TRACE_ERROR_NUMBER("Configuration is not empty", EINVAL);
     232             :         return EINVAL;
     233             :     }
     234             : 
     235         246 :     new_po = malloc(sizeof(struct parser_obj));
     236         246 :     if (!new_po) {
     237             :         TRACE_ERROR_NUMBER("No memory", ENOMEM);
     238             :         return ENOMEM;
     239             :     }
     240             : 
     241             :     /* Save external data */
     242         246 :     new_po->file = file;
     243         246 :     new_po->el = co->error_list;
     244         246 :     new_po->filename = config_filename;
     245         246 :     new_po->error_level = error_level;
     246         246 :     new_po->collision_flags = collision_flags;
     247         246 :     new_po->parse_flags = parse_flags;
     248         246 :     new_po->boundary = co->boundary;
     249         246 :     new_po->co = co;
     250             : 
     251             :     /* Initialize internal varibles */
     252         246 :     new_po->sec = NULL;
     253         246 :     new_po->merge_sec = NULL;
     254         246 :     new_po->ic = NULL;
     255         246 :     new_po->last_error = 0;
     256         246 :     new_po->linenum = 0;
     257         246 :     new_po->keylinenum = 0;
     258         246 :     new_po->seclinenum = 0;
     259         246 :     new_po->last_read = NULL;
     260         246 :     new_po->last_read_len = 0;
     261         246 :     new_po->inside_comment = 0;
     262         246 :     new_po->key = NULL;
     263         246 :     new_po->key_len = 0;
     264         246 :     new_po->raw_lines = NULL;
     265         246 :     new_po->raw_lengths = NULL;
     266         246 :     new_po->ret = EOK;
     267         246 :     new_po->merge_key = NULL;
     268         246 :     new_po->merge_vo = NULL;
     269         246 :     new_po->merge_error = 0;
     270         246 :     new_po->top = NULL;
     271         246 :     new_po->queue = NULL;
     272             : 
     273             :     /* Create top collection */
     274         246 :     error = col_create_collection(&(new_po->top),
     275             :                                   INI_CONFIG_NAME,
     276             :                                   COL_CLASS_INI_CONFIG);
     277         246 :     if (error) {
     278             :         TRACE_ERROR_NUMBER("Failed to create top collection", error);
     279           0 :         parser_destroy(new_po);
     280           0 :         return error;
     281             :     }
     282             : 
     283             :     /* Create a queue */
     284         246 :     error = col_create_queue(&(new_po->queue));
     285         246 :     if (error) {
     286             :         TRACE_ERROR_NUMBER("Failed to create queue", error);
     287           0 :         parser_destroy(new_po);
     288           0 :         return error;
     289             :     }
     290             : 
     291         246 :     error = col_enqueue_unsigned_property(new_po->queue,
     292             :                                           PARSE_ACTION,
     293             :                                           PARSE_READ);
     294         246 :     if (error) {
     295             :         TRACE_ERROR_NUMBER("Failed to create queue", error);
     296           0 :         parser_destroy(new_po);
     297           0 :         return error;
     298             :     }
     299             : 
     300         246 :     *po = new_po;
     301             : 
     302             :     TRACE_FLOW_EXIT();
     303         246 :     return error;
     304             : }
     305             : 
     306             : /* Function to read next line from the file */
     307      248081 : static int parser_read(struct parser_obj *po)
     308             : {
     309      248081 :     int error = EOK;
     310      248081 :     char *buffer = NULL;
     311      248081 :     ssize_t res = 0;
     312      248081 :     size_t len = 0;
     313      248081 :     int32_t i = 0;
     314             :     uint32_t action;
     315             : 
     316             :     TRACE_FLOW_ENTRY();
     317             : 
     318             :     /* Adjust line number */
     319      248081 :     (po->linenum)++;
     320             : 
     321             :     /* Get line from the file */
     322      496162 :     res = getline(&buffer, &len, po->file);
     323      248081 :     if (res == -1) {
     324         238 :         if (feof(po->file)) {
     325             :             TRACE_FLOW_STRING("Read nothing", "");
     326         233 :             if (po->inside_comment) {
     327           1 :                 action = PARSE_ERROR;
     328           1 :                 po->last_error = ERR_BADCOMMENT;
     329             :             }
     330             :             else action = PARSE_POST;
     331             :         }
     332             :         else {
     333             :             TRACE_ERROR_STRING("Error reading", "");
     334           5 :             action = PARSE_ERROR;
     335           5 :             po->last_error = ERR_READ;
     336             :         }
     337         238 :         if(buffer) free(buffer);
     338             :     }
     339             :     else {
     340             :         /* Read Ok */
     341      247843 :         len = res;
     342             :         TRACE_INFO_STRING("Read line ok:", buffer);
     343             :         TRACE_INFO_NUMBER("Length:", len);
     344             :         TRACE_INFO_NUMBER("Strlen:", strlen(buffer));
     345             : 
     346      247843 :         if (buffer[0] == '\0') {
     347             :             /* Empty line - read again (should not ever happen) */
     348           4 :             action = PARSE_READ;
     349           4 :             free(buffer);
     350             :         }
     351             :         else {
     352             :             /* Check length */
     353      247839 :             if (len >= BUFFER_SIZE) {
     354             :                 TRACE_ERROR_STRING("Too long", "");
     355           0 :                 action = PARSE_ERROR;
     356           0 :                 po->last_error = ERR_LONGDATA;
     357           0 :                 free(buffer);
     358             :             }
     359             :             else {
     360             :                 /* Trim end line */
     361      247839 :                 i = len - 1;
     362     1323000 :                 while ((i >= 0) &&
     363      521847 :                        ((buffer[i] == '\r') ||
     364             :                         (buffer[i] == '\n'))) {
     365             :                     TRACE_INFO_NUMBER("Offset:", i);
     366             :                     TRACE_INFO_NUMBER("Code:", buffer[i]);
     367      305475 :                     buffer[i] = '\0';
     368      305475 :                     i--;
     369             :                 }
     370             : 
     371      247839 :                 po->last_read = buffer;
     372      247839 :                 po->last_read_len = i + 1;
     373      247839 :                 action = PARSE_INSPECT;
     374             :                 TRACE_INFO_STRING("Line:", po->last_read);
     375             :                 TRACE_INFO_NUMBER("Linelen:", po->last_read_len);
     376             :             }
     377             :         }
     378             :     }
     379             : 
     380             :     /* Move to the next action */
     381      248081 :     error = col_enqueue_unsigned_property(po->queue,
     382             :                                           PARSE_ACTION,
     383             :                                           action);
     384      248081 :     if (error) {
     385             :         TRACE_ERROR_NUMBER("Failed to schedule an action", error);
     386           0 :         return error;
     387             :     }
     388             : 
     389             :     TRACE_FLOW_EXIT();
     390             :     return EOK;
     391             : }
     392             : 
     393             : /* Find if there is a collistion */
     394        1425 : static int check_section_collision(struct parser_obj *po)
     395             : {
     396        1425 :     int error = EOK;
     397        1425 :     struct collection_item *item = NULL;
     398             : 
     399             :     TRACE_FLOW_ENTRY();
     400             : 
     401             :     TRACE_INFO_STRING("Searching for:", col_get_item_property(po->sec, NULL));
     402             : 
     403        1425 :     error = col_get_item(po->top,
     404             :                          col_get_item_property(po->sec, NULL),
     405             :                          COL_TYPE_ANY,
     406             :                          COL_TRAVERSE_DEFAULT,
     407             :                          &item);
     408             : 
     409        1425 :     if (error) {
     410             :         TRACE_ERROR_NUMBER("Failed searching for dup", error);
     411             :         return error;
     412             :     }
     413             : 
     414             :     /* Check if there is a dup */
     415        1425 :     if (item) {
     416             :         TRACE_INFO_STRING("Collision found:",
     417             :                           col_get_item_property(item, NULL));
     418             :         /* Get the actual section collection instead of reference */
     419          61 :         po->merge_sec = *((struct collection_item **)
     420          61 :                           (col_get_item_data(item)));
     421             :     }
     422             :     else {
     423             :         TRACE_INFO_STRING("Collision not found.", "");
     424        1364 :         po->merge_sec = NULL;
     425             :     }
     426             : 
     427             :     TRACE_FLOW_EXIT();
     428             :     return EOK;
     429             : }
     430             : 
     431             : /* Clean all items in the section */
     432          19 : int empty_section(struct collection_item *sec)
     433             : {
     434          19 :     int error = EOK;
     435          19 :     struct collection_item *item = NULL;
     436          19 :     struct collection_item *save_item = NULL;
     437          19 :     struct value_obj *vo = NULL;
     438          19 :     int work_to_do = 1;
     439             : 
     440             :     TRACE_FLOW_ENTRY();
     441             : 
     442             :     do {
     443         106 :         item = NULL;
     444         106 :         error = col_extract_item_from_current(sec,
     445             :                                               COL_DSP_FRONT,
     446             :                                               NULL,
     447             :                                               0,
     448             :                                               COL_TYPE_ANY,
     449             :                                               &item);
     450         106 :         if ((error) && (error != ENOENT)) {
     451             :             TRACE_ERROR_NUMBER("Failed to extract item.", error);
     452             :             return error;
     453             :         }
     454             : 
     455         106 :         if (item) {
     456             :             TRACE_INFO_STRING("Item found:",
     457             :                               col_get_item_property(item, NULL));
     458             : 
     459         174 :             if (strncmp(col_get_item_property(item, NULL),
     460          87 :                         INI_SECTION_KEY, 1) == 0) {
     461             :                 /* Just ignore the first item */
     462          19 :                 save_item = item;
     463          19 :                 continue;
     464             :             }
     465             : 
     466          68 :             vo = *((struct value_obj **)(col_get_item_data(item)));
     467          68 :             value_destroy(vo);
     468          68 :             col_delete_item(item);
     469             :         }
     470             :         else {
     471             :             TRACE_INFO_STRING("No more items:", "");
     472             :             /* Restore saved item */
     473          19 :             error = col_insert_item(sec,
     474             :                                     NULL,
     475             :                                     save_item,
     476             :                                     COL_DSP_END,
     477             :                                     NULL,
     478             :                                     0,
     479             :                                     COL_INSERT_NOCHECK);
     480          19 :             if (error) {
     481             :                 TRACE_ERROR_NUMBER("Failed to restore item.", error);
     482             :                 return error;
     483             :             }
     484             : 
     485             :             work_to_do = 0;
     486             :         }
     487             :     }
     488         106 :     while (work_to_do);
     489             : 
     490             :     TRACE_FLOW_EXIT();
     491             :     return EOK;
     492             : }
     493             : 
     494             : /* Merge contents of the section */
     495          46 : static int merge_section(struct parser_obj *po)
     496             : {
     497          46 :     int error = EOK;
     498          46 :     struct collection_item *item = NULL;
     499          46 :     struct value_obj *vo = NULL;
     500          46 :     int work_to_do = 1;
     501             :     const char *key;
     502             : 
     503             :     TRACE_FLOW_ENTRY();
     504             : 
     505             :     do {
     506             :         TRACE_INFO_STRING("Top of the merge loop", "");
     507             : 
     508         224 :         item = NULL;
     509         224 :         error = col_extract_item_from_current(po->sec,
     510             :                                               COL_DSP_FRONT,
     511             :                                               NULL,
     512             :                                               0,
     513             :                                               COL_TYPE_ANY,
     514             :                                               &item);
     515         224 :         if ((error) && (error != ENOENT)) {
     516             :             TRACE_ERROR_NUMBER("Failed to extract item.", error);
     517             :             return error;
     518             :         }
     519             : 
     520         224 :         if (item) {
     521             : 
     522             :             TRACE_INFO_STRING("Item found:", col_get_item_property(item, NULL));
     523             : 
     524         360 :             if (strncmp(col_get_item_property(item, NULL),
     525         180 :                         INI_SECTION_KEY, 1) == 0) {
     526             :                 /* Just ignore the first item */
     527          46 :                 vo = *((struct value_obj **)(col_get_item_data(item)));
     528          46 :                 value_destroy(vo);
     529          46 :                 col_delete_item(item);
     530          46 :                 continue;
     531             :             }
     532             : 
     533         134 :             po->merge_vo = *((struct value_obj **)(col_get_item_data(item)));
     534         134 :             key = col_get_item_property(item, NULL);
     535             :             /* To be able to use po->merge_key in the loop
     536             :              * we have to overcome constraints imposed by
     537             :              * the "const" declaration.
     538             :              */
     539         134 :             memcpy(&(po->merge_key), &key, sizeof(char *));
     540             : 
     541             :             /* Use the value processing function to inser the value */
     542         134 :             error = complete_value_processing(po);
     543             : 
     544             :             /* In case of error value is already cleaned */
     545         134 :             po->merge_vo = NULL;
     546         134 :             po->merge_key = NULL;
     547         134 :             col_delete_item(item);
     548             :             /* Now we can check the error */
     549         134 :             if (error) {
     550             :                 TRACE_ERROR_NUMBER("Failed to merge item.", error);
     551             :                 return error;
     552             :             }
     553             :         }
     554             :         else {
     555             :             TRACE_INFO_STRING("No more items:", "");
     556             :             work_to_do = 0;
     557             :         }
     558             :     }
     559         222 :     while (work_to_do);
     560             : 
     561             :     /* If we reached this place the incoming section is empty.
     562             :      * but just to be safe clean with callback. */
     563          44 :     col_destroy_collection_with_cb(po->sec, ini_cleanup_cb, NULL);
     564          44 :     po->sec = NULL;
     565             : 
     566             :     TRACE_FLOW_EXIT();
     567          44 :     return EOK;
     568             : }
     569             : 
     570             : /* Function to read next line from the file */
     571        1658 : static int parser_save_section(struct parser_obj *po)
     572             : {
     573        1658 :     int error = EOK;
     574             :     uint32_t mergemode;
     575        1658 :     int merge = 0;
     576             : 
     577             :     TRACE_FLOW_ENTRY();
     578             : 
     579        1658 :     if (po->sec) {
     580             : 
     581             :         TRACE_INFO_STRING("Section exists.", "");
     582             : 
     583             :         /* First detect if we have collision */
     584        1425 :         error = check_section_collision(po);
     585        1425 :         if (error) {
     586             :             TRACE_ERROR_NUMBER("Failed to check for collision", error);
     587             :             return error;
     588             :         }
     589             : 
     590        1425 :         if (po->merge_sec) {
     591             : 
     592             :             TRACE_INFO_STRING("Merge collision detected", "");
     593             : 
     594          61 :             mergemode = po->collision_flags & INI_MS_MASK;
     595             : 
     596          61 :             switch (mergemode) {
     597             :             case INI_MS_ERROR:
     598             :                 /* Report error and return */
     599             :                 TRACE_INFO_STRING("Reporting error", "duplicate section");
     600          10 :                 error = save_error(po->el,
     601             :                                    po->seclinenum,
     602             :                                    ERR_DUPSECTION,
     603           5 :                                    ERROR_TXT);
     604           5 :                 if (error) {
     605             :                     TRACE_ERROR_NUMBER("Failed to "
     606             :                                        "save error",
     607             :                                         error);
     608           0 :                     return error;
     609             :                 }
     610             :                 /* Return error */
     611             :                 TRACE_FLOW_RETURN(EEXIST);
     612             :                 return EEXIST;
     613             : 
     614             :             case INI_MS_PRESERVE:
     615             :                 /* Delete new section */
     616             :                 TRACE_INFO_STRING("Preserve mode", "");
     617          10 :                 col_destroy_collection_with_cb(
     618             :                                         po->sec,
     619             :                                         ini_cleanup_cb,
     620             :                                         NULL);
     621          10 :                 po->sec = NULL;
     622          10 :                 break;
     623             : 
     624             :             case INI_MS_OVERWRITE:
     625             :                 /* Empty existing section */
     626             :                 TRACE_INFO_STRING("Ovewrite mode", "");
     627          10 :                 error = empty_section(po->merge_sec);
     628          10 :                 if (error) {
     629             :                     TRACE_ERROR_NUMBER("Failed to "
     630             :                                        "empty section",
     631             :                                         error);
     632             :                     return error;
     633             :                 }
     634             :                 merge = 1;
     635             :                 break;
     636             : 
     637             :             case INI_MS_DETECT:
     638             :                 /* Detect mode */
     639             :                 TRACE_INFO_STRING("Detect mode", "");
     640           9 :                 po->merge_error = EEXIST;
     641          18 :                 error = save_error(po->el,
     642             :                                    po->seclinenum,
     643             :                                    ERR_DUPSECTION,
     644           9 :                                    ERROR_TXT);
     645           9 :                 if (error) {
     646             :                     TRACE_ERROR_NUMBER("Failed to "
     647             :                                        "save error",
     648             :                                         error);
     649             :                     return error;
     650             :                 }
     651             :                 merge = 1;
     652             :                 break;
     653             : 
     654             :             case INI_MS_MERGE:
     655             :                 /* Merge */
     656             :             default:
     657             :                 TRACE_INFO_STRING("Merge mode", "");
     658             :                 merge = 1;
     659             :                 break;
     660             :             }
     661             : 
     662          56 :             if (merge) {
     663          46 :                 error = merge_section(po);
     664          46 :                 if (error) {
     665             :                     TRACE_ERROR_NUMBER("Failed to merge section", error);
     666             :                     return error;
     667             :                 }
     668             :             }
     669             : 
     670          54 :             po->merge_sec = NULL;
     671             :         }
     672             :         else {
     673             :             /* Add section to configuration */
     674             :             TRACE_INFO_STRING("Now adding collection", "");
     675        1364 :             error = col_add_collection_to_collection(po->top,
     676             :                                                      NULL, NULL,
     677             :                                                      po->sec,
     678             :                                                      COL_ADD_MODE_EMBED);
     679             : 
     680        1364 :             if (error) {
     681             :                 TRACE_ERROR_NUMBER("Failed to embed section", error);
     682             :                 return error;
     683             :             }
     684             : 
     685        1364 :             po->sec = NULL;
     686             :         }
     687             :     }
     688             : 
     689             :     TRACE_FLOW_EXIT();
     690             :     return EOK;
     691             : 
     692             : }
     693             : 
     694             : /* Complete value processing */
     695       67882 : static int complete_value_processing(struct parser_obj *po)
     696             : {
     697       67882 :     int error = EOK;
     698       67882 :     int error2 = EOK;
     699       67882 :     struct value_obj *vo = NULL;
     700       67882 :     struct value_obj *vo_old = NULL;
     701             :     unsigned insertmode;
     702             :     uint32_t mergemode;
     703       67882 :     int suppress = 0;
     704       67882 :     int doinsert = 0;
     705       67882 :     struct collection_item *item = NULL;
     706       67882 :     struct collection_item *section = NULL;
     707       67882 :     int merging = 0;
     708             : 
     709             :     TRACE_FLOW_ENTRY();
     710             : 
     711       67882 :     if (po->merge_sec) {
     712             :         TRACE_INFO_STRING("Processing value in merge mode", "");
     713             :         section = po->merge_sec;
     714             :         merging = 1;
     715             :     }
     716       67748 :     else if(!(po->sec)) {
     717             :         TRACE_INFO_STRING("Creating default section", "");
     718             :         /* If there is not open section create a default one */
     719          13 :         error = col_create_collection(&po->sec,
     720             :                                       INI_DEFAULT_SECTION,
     721             :                                       COL_CLASS_INI_SECTION);
     722          13 :         if (error) {
     723             :             TRACE_ERROR_NUMBER("Failed to create default section", error);
     724             :             return error;
     725             :         }
     726          13 :         section = po->sec;
     727             :     }
     728             :     else {
     729             :         TRACE_INFO_STRING("Processing value in normal mode", "");
     730             :         section = po->sec;
     731             :     }
     732             : 
     733       67882 :     if (merging) {
     734             :         TRACE_INFO_STRING("Using merge key:", po->merge_key);
     735         134 :         vo = po->merge_vo;
     736             :         /* We are adding to the merge section so use MV2S flags.
     737             :          * But flags are done in such a way that deviding MV2S by MV1S mask
     738             :          * will translate MV2S flags into MV1S so we can use
     739             :          * MV1S constants. */
     740             :         TRACE_INFO_NUMBER("Collisions flags:", po->collision_flags);
     741         134 :         mergemode = (po->collision_flags & INI_MV2S_MASK) / INI_MV1S_MASK;
     742             :     }
     743             :     else {
     744             :         /* Construct value object from what we have */
     745       67748 :         error = value_create_from_refarray(po->raw_lines,
     746             :                                            po->raw_lengths,
     747             :                                            po->keylinenum,
     748             :                                            INI_VALUE_READ,
     749             :                                            po->key_len,
     750             :                                            po->boundary,
     751             :                                            po->ic,
     752             :                                            &vo);
     753             : 
     754       67748 :         if (error) {
     755             :             TRACE_ERROR_NUMBER("Failed to create value object", error);
     756             :             return error;
     757             :         }
     758             :         /* Forget about the arrays. They are now owned by the value object */
     759       67748 :         po->ic = NULL;
     760       67748 :         po->raw_lines = NULL;
     761       67748 :         po->raw_lengths = NULL;
     762       67748 :         mergemode = po->collision_flags & INI_MV1S_MASK;
     763             :     }
     764             : 
     765       67882 :     switch (mergemode) {
     766             :     case INI_MV1S_ERROR:
     767             : 
     768          13 :         insertmode = COL_INSERT_DUPERROR;
     769          13 :         doinsert = 1;
     770          13 :         break;
     771             : 
     772             :     case INI_MV1S_PRESERVE:
     773             : 
     774         248 :         insertmode = COL_INSERT_DUPERROR;
     775         248 :         doinsert = 1;
     776         248 :         suppress = 1;
     777         248 :         break;
     778             : 
     779             :     case INI_MV1S_ALLOW:
     780             : 
     781         753 :         insertmode = COL_INSERT_NOCHECK;
     782         753 :         doinsert = 1;
     783         753 :         break;
     784             : 
     785             :     case INI_MV1S_OVERWRITE: /* Special handling */
     786             :     case INI_MV1S_DETECT:
     787             :     default:
     788             :         break;
     789             :     }
     790             : 
     791             :     /* Do not insert but search for dups first */
     792       67882 :     if (!doinsert) {
     793             :         TRACE_INFO_STRING("Overwrite mode. Looking for:",
     794             :                           (char *)(merging ? po->merge_key : po->key));
     795             : 
     796       66868 :         error = col_get_item(section,
     797             :                              merging ? po->merge_key : po->key,
     798             :                              COL_TYPE_BINARY,
     799             :                              COL_TRAVERSE_DEFAULT,
     800             :                              &item);
     801             : 
     802       66868 :         if (error) {
     803             :             TRACE_ERROR_NUMBER("Failed searching for dup", error);
     804           0 :             value_destroy(vo);
     805           0 :             return error;
     806             :         }
     807             : 
     808             :         /* Check if there is a dup */
     809       66868 :         if (item) {
     810             :             /* Check if we are in the detect mode */
     811         376 :             if (mergemode == INI_MV1S_DETECT) {
     812          13 :                 po->merge_error = EEXIST;
     813             :                 /* There is a dup - inform user about it and continue */
     814          26 :                 error = save_error(po->el,
     815             :                                    merging ? po->seclinenum : po->keylinenum,
     816             :                                    merging ? ERR_DUPKEYSEC : ERR_DUPKEY,
     817          13 :                                    ERROR_TXT);
     818          13 :                 if (error) {
     819             :                     TRACE_ERROR_NUMBER("Failed to save error", error);
     820           0 :                     value_destroy(vo);
     821           0 :                     return error;
     822             :                 }
     823             :                 doinsert = 1;
     824             :                 insertmode = COL_INSERT_NOCHECK;
     825             : 
     826             :             }
     827             :             else {
     828             : 
     829             :                 /* Dup exists - update it */
     830         363 :                 vo_old = *((struct value_obj **)(col_get_item_data(item)));
     831         363 :                 error = col_modify_binary_item(item,
     832             :                                                NULL,
     833             :                                                &vo,
     834             :                                                sizeof(struct value_obj *));
     835         363 :                 if (error) {
     836             :                     TRACE_ERROR_NUMBER("Failed updating the value", error);
     837           0 :                     value_destroy(vo);
     838           0 :                     return error;
     839             :                 }
     840             : 
     841             :                 /* If we failed to update it is better to leak then crash,
     842             :                  * so destroy original value only on the successful update.
     843             :                  */
     844         363 :                 value_destroy(vo_old);
     845             :             }
     846             :         }
     847             :         else {
     848             :             /* No dup found so we can insert with no check */
     849             :             doinsert = 1;
     850             :             insertmode = COL_INSERT_NOCHECK;
     851             :         }
     852             :     }
     853             : 
     854       67882 :     if (doinsert) {
     855             :         /* Add value to collection */
     856       67519 :         error = col_insert_binary_property(section,
     857             :                                            NULL,
     858             :                                            COL_DSP_END,
     859             :                                            NULL,
     860             :                                            0,
     861             :                                            insertmode,
     862             :                                            merging ? po->merge_key : po->key,
     863             :                                            &vo,
     864             :                                            sizeof(struct value_obj *));
     865       67519 :         if (error) {
     866         146 :             value_destroy(vo);
     867             : 
     868         146 :             if ((suppress) && (error == EEXIST)) {
     869             :                 TRACE_INFO_STRING("Preseved exisitng value",
     870             :                                   (char *)(merging ? po->merge_key : po->key));
     871             :             }
     872             :             else {
     873             :                 /* Check if this is a critical error or not */
     874           3 :                 if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) {
     875             :                     TRACE_ERROR_NUMBER("Failed to add value object "
     876             :                                        "to the section", error);
     877           6 :                     error2 = save_error(po->el,
     878             :                                        merging ? po->seclinenum : po->keylinenum,
     879             :                                        merging ? ERR_DUPKEYSEC : ERR_DUPKEY,
     880           3 :                                        ERROR_TXT);
     881           3 :                     if (error2) {
     882             :                         TRACE_ERROR_NUMBER("Failed to save error", error2);
     883             :                         return error2;
     884             :                     }
     885           3 :                     return error;
     886             :                 }
     887             :                 else {
     888             :                     TRACE_ERROR_NUMBER("Failed to add value object"
     889             :                                        " to the section", error);
     890             :                     return error;
     891             :                 }
     892             :             }
     893             :         }
     894             :     }
     895             : 
     896       67879 :     if (!merging) {
     897       67747 :         free(po->key);
     898       67747 :         po->key = NULL;
     899       67747 :         po->key_len = 0;
     900             :     }
     901             : 
     902             :     TRACE_FLOW_EXIT();
     903             :     return EOK;
     904             : }
     905             : 
     906             : 
     907             : /* Process comment */
     908       32781 : static int handle_comment(struct parser_obj *po, uint32_t *action)
     909             : {
     910       32781 :     int error = EOK;
     911             : 
     912             :     TRACE_FLOW_ENTRY();
     913             : 
     914             :     /* We got a comment */
     915       32781 :     if (po->key) {
     916             :         /* Previous value if any is complete */
     917       31157 :         error = complete_value_processing(po);
     918       31157 :         if (error) {
     919             :             TRACE_ERROR_NUMBER("Failed to finish saving value", error);
     920             :             return error;
     921             :         }
     922             :     }
     923             : 
     924       32780 :     if (!(po->ic)) {
     925             :         /* Create a new comment */
     926       31592 :         error = ini_comment_create(&(po->ic));
     927       31592 :         if (error) {
     928             :             TRACE_ERROR_NUMBER("Failed to create comment", error);
     929             :             return error;
     930             :         }
     931             :     }
     932             : 
     933             :     /* Add line to comment */
     934       65560 :     error = ini_comment_build_wl(po->ic,
     935       32780 :                                  po->last_read,
     936             :                                  po->last_read_len);
     937       32780 :     if (error) {
     938             :         TRACE_ERROR_NUMBER("Failed to add line to comment", error);
     939             :         return error;
     940             :     }
     941             :     /*
     942             :      * We are done with the comment line.
     943             :      * Free it since comment keeps a copy.
     944             :      */
     945       32780 :     free(po->last_read);
     946       32780 :     po->last_read = NULL;
     947       32780 :     po->last_read_len = 0;
     948       32780 :     *action = PARSE_READ;
     949             : 
     950             :     TRACE_FLOW_EXIT();
     951       32780 :     return EOK;
     952             : }
     953             : 
     954             : /* Handle key-value pair */
     955       66335 : static int handle_kvp(struct parser_obj *po, uint32_t *action)
     956             : {
     957       66335 :     int error = EOK;
     958       66335 :     char *eq = NULL;
     959       66335 :     uint32_t len = 0;
     960       66335 :     char *dupval = NULL;
     961             :     char *str;
     962             :     uint32_t full_len;
     963             : 
     964             :     TRACE_FLOW_ENTRY();
     965             : 
     966       66335 :     str = po->last_read;
     967       66335 :     full_len = po->last_read_len;
     968             : 
     969             :     TRACE_INFO_STRING("Last read:", str);
     970             : 
     971             :     /* Trim spaces at the beginning */
     972      132693 :     while ((full_len > 0) && (isspace(*(str)))) {
     973          23 :         str++;
     974          23 :         full_len--;
     975             :     }
     976             : 
     977             :     /* Check if we have the key */
     978       66335 :     if (*(str) == '=') {
     979             :         TRACE_ERROR_STRING("No key", str);
     980           0 :         po->last_error = ERR_NOKEY;
     981           0 :         *action = PARSE_ERROR;
     982           0 :         return EOK;
     983             :     }
     984             : 
     985             :     /* Find "=" */
     986       66335 :     eq = strchr(str, '=');
     987       66335 :     if (eq == NULL) {
     988             :         TRACE_ERROR_STRING("No equal sign", str);
     989           0 :         po->last_error = ERR_NOEQUAL;
     990           0 :         *action = PARSE_ERROR;
     991           0 :         return EOK;
     992             :     }
     993             : 
     994             :     /* Strip spaces around "=" */
     995             :     /* Since eq > str we can substract 1 */
     996       66335 :     len = eq - str - 1;
     997       66335 :     while ((len > 0) && (isspace(*(str + len)))) len--;
     998             :     /* Adjust length properly */
     999       66335 :     len++;
    1000             : 
    1001             :     /* Check the key length */
    1002       66335 :     if(len >= MAX_KEY) {
    1003             :         TRACE_ERROR_STRING("Key name is too long", str);
    1004           0 :         po->last_error = ERR_LONGKEY;
    1005           0 :         *action = PARSE_ERROR;
    1006           0 :         return EOK;
    1007             :     }
    1008             : 
    1009       66335 :     if (po->key) {
    1010             :         /* Complete processing of the previous value */
    1011       35069 :         error = complete_value_processing(po);
    1012       35069 :         if (error) {
    1013             :             TRACE_ERROR_NUMBER("Failed to complete value processing", error);
    1014             :             return error;
    1015             :         }
    1016             :     }
    1017             : 
    1018             :     /* Dup the key name */
    1019       66335 :     po->key = malloc(len + 1);
    1020       66335 :     if (!(po->key)) {
    1021             :         TRACE_ERROR_NUMBER("Failed to dup key", ENOMEM);
    1022             :         return ENOMEM;
    1023             :     }
    1024             : 
    1025       66335 :     memcpy(po->key, str, len);
    1026       66335 :     *(po->key + len) = '\0';
    1027       66335 :     po->key_len = len;
    1028             : 
    1029             :     TRACE_INFO_STRING("Key:", po->key);
    1030             :     TRACE_INFO_NUMBER("Keylen:", po->key_len);
    1031             : 
    1032       66335 :     len = full_len - (eq - str) - 1;
    1033             : 
    1034             :     /* Trim spaces after equal sign */
    1035       66335 :     eq++;
    1036      160804 :     while (isspace(*eq)) {
    1037       28134 :         eq++;
    1038       28134 :         len--;
    1039             :     }
    1040             : 
    1041             :     TRACE_INFO_STRING("VALUE:", eq);
    1042             :     TRACE_INFO_NUMBER("LENGTH:", len);
    1043             : 
    1044             :     /* Dup the part of the value */
    1045       66335 :     dupval = malloc(len + 1);
    1046       66335 :     if (!dupval) {
    1047             :         TRACE_ERROR_NUMBER("Failed to dup value", ENOMEM);
    1048             :         return ENOMEM;
    1049             :     }
    1050             : 
    1051       66335 :     memcpy(dupval, eq, len);
    1052       66335 :     *(dupval + len) = '\0';
    1053             : 
    1054             :     /* Create new arrays */
    1055       66335 :     error = value_create_arrays(&(po->raw_lines),
    1056             :                                 &(po->raw_lengths));
    1057       66335 :     if (error) {
    1058             :         TRACE_ERROR_NUMBER("Failed to create arrays", error);
    1059           0 :         free(dupval);
    1060           0 :         return error;
    1061             :     }
    1062             : 
    1063             :     /* Save a duplicated part in the value */
    1064       66335 :     error = value_add_to_arrays(dupval,
    1065             :                                 len,
    1066             :                                 po->raw_lines,
    1067             :                                 po->raw_lengths);
    1068             : 
    1069       66335 :     if (error) {
    1070             :         TRACE_ERROR_NUMBER("Failed to add value to arrays", error);
    1071           0 :         free(dupval);
    1072           0 :         return error;
    1073             :     }
    1074             : 
    1075             :     /* Save the line number of the last found key */
    1076       66335 :     po->keylinenum = po->linenum;
    1077             : 
    1078             :     /* Prepare for reading */
    1079       66335 :     free(po->last_read);
    1080       66335 :     po->last_read = NULL;
    1081       66335 :     po->last_read_len = 0;
    1082             : 
    1083       66335 :     *action = PARSE_READ;
    1084             : 
    1085             :     TRACE_FLOW_EXIT();
    1086       66335 :     return EOK;
    1087             : }
    1088             : 
    1089             : /* Process line starts with space  */
    1090      147309 : static int handle_space(struct parser_obj *po, uint32_t *action)
    1091             : {
    1092      147309 :     int error = EOK;
    1093      147309 :     int space_err = 0;
    1094             : 
    1095             :     TRACE_FLOW_ENTRY();
    1096             : 
    1097      147309 :     if (po->parse_flags & INI_PARSE_NOWRAP) {
    1098             :         /* In this case an empty line is a comment. */
    1099          16 :         if (is_just_spaces(po->last_read, po->last_read_len)) {
    1100           0 :             error = handle_comment(po, action);
    1101             :             TRACE_FLOW_EXIT();
    1102           0 :             return error;
    1103             :         }
    1104             : 
    1105             :         /* Wrapping is not allowed */
    1106          16 :         if (!is_allowed_spaces(po->last_read,
    1107             :                                po->last_read_len,
    1108             :                                po->parse_flags,
    1109             :                                &space_err)) {
    1110          10 :             *action = PARSE_ERROR;
    1111          10 :             po->last_error = space_err;
    1112          10 :             error = EOK;
    1113             :         }
    1114             :         else {
    1115             :             /* Allowed spaces will be trimmed
    1116             :              * inside KVP processing.
    1117             :              */
    1118           6 :             error = handle_kvp(po, action);
    1119             :         }
    1120             :         TRACE_FLOW_EXIT();
    1121          16 :         return error;
    1122             :     }
    1123             : 
    1124             :     /* Do we have current value object? */
    1125      147293 :     if (po->key) {
    1126             :         /* This is a new line in a folded value */
    1127      147227 :         error = value_add_to_arrays(po->last_read,
    1128             :                                     po->last_read_len,
    1129             :                                     po->raw_lines,
    1130             :                                     po->raw_lengths);
    1131      147227 :         if (error) {
    1132             :             TRACE_ERROR_NUMBER("Failed to add line to value", error);
    1133             :             return error;
    1134             :         }
    1135             :         /* Do not free the line, it is now an element of the array */
    1136      147227 :         po->last_read = NULL;
    1137      147227 :         po->last_read_len = 0;
    1138      147227 :         *action = PARSE_READ;
    1139             :     }
    1140             :     else {
    1141             :         /* Check if this is a completely empty line */
    1142          66 :         if (is_just_spaces(po->last_read, po->last_read_len)) {
    1143           0 :             error = handle_comment(po, action);
    1144           0 :             if (error) {
    1145             :                 TRACE_ERROR_NUMBER("Failed to process comment", error);
    1146           0 :                 return error;
    1147             :             }
    1148             :         }
    1149             :         else {
    1150             :             /* We do not have an active value
    1151             :              * but have a line is starting with a space.
    1152             :              * For now it is error.
    1153             :              * We can change it in future if
    1154             :              * people find it being too restrictive
    1155             :              */
    1156          66 :             *action = PARSE_ERROR;
    1157          66 :             po->last_error = ERR_SPACE;
    1158             :         }
    1159             :     }
    1160             : 
    1161             :     TRACE_FLOW_EXIT();
    1162             :     return EOK;
    1163             : }
    1164             : 
    1165             : /* Parse and process section */
    1166        1420 : static int handle_section(struct parser_obj *po, uint32_t *action)
    1167             : {
    1168        1420 :     int error = EOK;
    1169             :     char *start;
    1170             :     char *end;
    1171             :     char *dupval;
    1172             :     uint32_t len;
    1173             : 
    1174             :     TRACE_FLOW_ENTRY();
    1175             : 
    1176             :     /* We are safe to substract 1
    1177             :      * since we know that there is at
    1178             :      * least one character on the line
    1179             :      * based on the check above.
    1180             :      */
    1181        1420 :     end = po->last_read + po->last_read_len - 1;
    1182        1420 :     while (isspace(*end)) end--;
    1183        1420 :     if (*end != ']') {
    1184           0 :         *action = PARSE_ERROR;
    1185           0 :         po->last_error = ERR_NOCLOSESEC;
    1186           0 :         return EOK;
    1187             :     }
    1188             : 
    1189             :     /* Skip spaces at the beginning of the section name */
    1190        1420 :     start = po->last_read + 1;
    1191        1420 :     while (isspace(*start)) start++;
    1192             : 
    1193             :     /* Check if there is a section name */
    1194        1420 :     if (start == end) {
    1195           0 :         *action = PARSE_ERROR;
    1196           0 :         po->last_error = ERR_NOSECTION;
    1197           0 :         return EOK;
    1198             :     }
    1199             : 
    1200             :     /* Skip spaces at the end of the section name */
    1201        1420 :     end--;
    1202        1420 :     while (isspace(*end)) end--;
    1203             : 
    1204             :     /* We got section name */
    1205        1420 :     len = end - start + 1;
    1206             : 
    1207        1420 :     if (len > MAX_KEY) {
    1208           0 :         *action = PARSE_ERROR;
    1209           0 :         po->last_error = ERR_SECTIONLONG;
    1210           0 :         return EOK;
    1211             :     }
    1212             : 
    1213        1420 :     if (po->key) {
    1214             :         /* Complete processing of the previous value */
    1215           0 :         error = complete_value_processing(po);
    1216           0 :         if (error) {
    1217             :             TRACE_ERROR_NUMBER("Failed to complete value processing", error);
    1218             :             return error;
    1219             :         }
    1220             :     }
    1221             : 
    1222             :     /* Save section if we have one*/
    1223        1420 :     error = parser_save_section(po);
    1224        1420 :     if (error) {
    1225             :         TRACE_ERROR_NUMBER("Failed to save section", error);
    1226             :         return error;
    1227             :     }
    1228             : 
    1229             :     /* Dup the name */
    1230        1413 :     dupval = malloc(len + 1);
    1231        1413 :     if (!dupval) {
    1232             :         TRACE_ERROR_NUMBER("Failed to dup section name", ENOMEM);
    1233             :         return ENOMEM;
    1234             :     }
    1235             : 
    1236        1413 :     memcpy(dupval, start, len);
    1237        1413 :     dupval[len] = '\0';
    1238             : 
    1239             :     /* Create a new section */
    1240        1413 :     error = col_create_collection(&po->sec,
    1241             :                                   dupval,
    1242             :                                   COL_CLASS_INI_SECTION);
    1243        1413 :     if (error) {
    1244             :         TRACE_ERROR_NUMBER("Failed to create a section", error);
    1245           0 :         free(dupval);
    1246           0 :         return error;
    1247             :     }
    1248             : 
    1249             :     /* But if there is just a comment then create a special key */
    1250        1413 :     po->key_len = sizeof(INI_SECTION_KEY) - 1;
    1251        1413 :     po->key = strndup(INI_SECTION_KEY, sizeof(INI_SECTION_KEY));
    1252             :     /* Create new arrays */
    1253        1413 :     error = value_create_arrays(&(po->raw_lines),
    1254             :                                 &(po->raw_lengths));
    1255        1413 :     if (error) {
    1256             :         TRACE_ERROR_NUMBER("Failed to create arrays", error);
    1257           0 :         free(dupval);
    1258           0 :         return error;
    1259             :     }
    1260             : 
    1261             :     /* Save a duplicated part in the value */
    1262        1413 :     error = value_add_to_arrays(dupval,
    1263             :                                 len,
    1264             :                                 po->raw_lines,
    1265             :                                 po->raw_lengths);
    1266        1413 :     if (error) {
    1267             :         TRACE_ERROR_NUMBER("Failed to add value to the arrays", error);
    1268           0 :         free(dupval);
    1269           0 :         return error;
    1270             :     }
    1271             : 
    1272             :     /* Save the line number of the last found key */
    1273        1413 :     po->seclinenum = po->linenum;
    1274             : 
    1275             :     /* Complete processing of this value.
    1276             :      * A new section will be created inside and a special
    1277             :      * value will be added.
    1278             :      */
    1279        1413 :     error = complete_value_processing(po);
    1280        1413 :     if (error) {
    1281             :         TRACE_ERROR_NUMBER("Failed to complete value processing", error);
    1282             :         return error;
    1283             :     }
    1284             : 
    1285             :     /* We are done dealing with section */
    1286        1413 :     free(po->last_read);
    1287        1413 :     po->last_read = NULL;
    1288        1413 :     po->last_read_len = 0;
    1289        1413 :     *action = PARSE_READ;
    1290             : 
    1291             :     TRACE_FLOW_EXIT();
    1292        1413 :     return EOK;
    1293             : 
    1294             : }
    1295             : 
    1296      247839 : static int check_for_comment(char *buffer,
    1297             :                              uint32_t buffer_len,
    1298             :                              int allow_c_comments,
    1299             :                              int *inside_comment)
    1300             : {
    1301             :     int pos;
    1302      247839 :     int is_comment = 0;
    1303             : 
    1304             :     TRACE_FLOW_ENTRY();
    1305             : 
    1306      247839 :     if (*inside_comment) {
    1307             :         /* We are already inside the comment
    1308             :          * and we are looking for the end of the comment
    1309             :          */
    1310         185 :         if (buffer_len) {
    1311         181 :             pos = buffer_len - 1;
    1312         181 :             while(isspace(buffer[pos]) && pos > 0) pos--;
    1313             : 
    1314             :             /* Check for comment at the end of the line */
    1315         181 :             if ((pos > 1) &&
    1316          88 :                 (buffer[pos] == '/') &&
    1317          88 :                 (buffer[pos - 1] == '*')) {
    1318          88 :                 *inside_comment = 0;
    1319             :             }
    1320             :         }
    1321             :         is_comment = 1;
    1322             :     }
    1323             :     else {
    1324             :         /* We do not allow spaces in front of comments
    1325             :          * so we expect the comment to start right away.
    1326             :          */
    1327      247654 :         if ((buffer[0] == '\0') ||
    1328      216103 :             (buffer[0] == ';') ||
    1329             :             (buffer[0] == '#')) {
    1330             :             is_comment = 1;
    1331             :         }
    1332      215432 :         else if ((allow_c_comments) && (buffer_len > 1)) {
    1333      215425 :             if (buffer[0] == '/') {
    1334         375 :                 if (buffer[1] == '/') is_comment = 1;
    1335         177 :                 else if (buffer[1] == '*') {
    1336             : 
    1337         176 :                     is_comment = 1;
    1338         176 :                     *inside_comment = 1;
    1339             : 
    1340             :                     /* Here we need to check whether this comment ends
    1341             :                      * on this line or not
    1342             :                      */
    1343         176 :                     pos = buffer_len - 1;
    1344         176 :                     while(isspace(buffer[pos]) && pos > 0) pos--;
    1345             : 
    1346             :                     /* Check for comment at the end of the line
    1347             :                      * but make sure we have at least two asterisks
    1348             :                      */
    1349         176 :                     if ((pos > 2) &&
    1350          87 :                         (buffer[pos] == '/') &&
    1351          87 :                         (buffer[pos - 1] == '*')) {
    1352          87 :                         *inside_comment = 0;
    1353             :                     }
    1354             :                 }
    1355             :             }
    1356             :         }
    1357             :     }
    1358             : 
    1359             :     TRACE_FLOW_EXIT();
    1360      247839 :     return is_comment;
    1361             : }
    1362             : 
    1363             : /* Inspect the line */
    1364      247839 : static int parser_inspect(struct parser_obj *po)
    1365             : {
    1366      247839 :     int error = EOK;
    1367      247839 :     uint32_t action = PARSE_DONE;
    1368             : 
    1369             :     TRACE_FLOW_ENTRY();
    1370             : 
    1371             :     TRACE_INFO_STRING("Buffer:", po->last_read);
    1372             :     TRACE_INFO_NUMBER("In comment:", po->inside_comment);
    1373             : 
    1374      495678 :     if (check_for_comment(po->last_read,
    1375             :                           po->last_read_len,
    1376      247839 :                           !(po->parse_flags & INI_PARSE_NO_C_COMMENTS),
    1377             :                           &(po->inside_comment))) {
    1378             : 
    1379       32781 :         error = handle_comment(po, &action);
    1380       32781 :         if (error) {
    1381             :             TRACE_ERROR_NUMBER("Failed to process comment", error);
    1382             :             return error;
    1383             :         }
    1384             :     }
    1385      215058 :     else if (isspace(*(po->last_read))) {
    1386             : 
    1387      147309 :         error = handle_space(po, &action);
    1388      147309 :         if (error) {
    1389             :             TRACE_ERROR_NUMBER("Failed to process line wrapping", error);
    1390             :             return error;
    1391             :         }
    1392             :     }
    1393       67749 :     else if (*(po->last_read) == '[') {
    1394             : 
    1395        1420 :         error = handle_section(po, &action);
    1396        1420 :         if (error) {
    1397             :             TRACE_ERROR_NUMBER("Failed to save section", error);
    1398             :             return error;
    1399             :         }
    1400             :     }
    1401             :     else {
    1402             : 
    1403       66329 :         error = handle_kvp(po, &action);
    1404       66329 :         if (error) {
    1405             :             TRACE_ERROR_NUMBER("Failed to save kvp", error);
    1406             :             return error;
    1407             :         }
    1408             :     }
    1409             : 
    1410             :     /* Move to the next action */
    1411      247831 :     error = col_enqueue_unsigned_property(po->queue,
    1412             :                                           PARSE_ACTION,
    1413             :                                           action);
    1414             :     if (error) {
    1415             :         TRACE_ERROR_NUMBER("Failed to schedule an action", error);
    1416             :         return error;
    1417             :     }
    1418             : 
    1419             :     TRACE_FLOW_EXIT();
    1420             :     return error;
    1421             : }
    1422             : 
    1423             : 
    1424             : /* Complete file processing */
    1425         232 : static int parser_post(struct parser_obj *po)
    1426             : {
    1427         232 :     int error = EOK;
    1428             : 
    1429             :     TRACE_FLOW_ENTRY();
    1430             : 
    1431             :     /* If there was just a comment at the bottom
    1432             :      * put it directly into the config object
    1433             :      */
    1434         232 :     if((po->ic) && (!(po->key))) {
    1435         119 :         if (po->co->last_comment) {
    1436           0 :             error = ini_comment_add(po->ic, po->co->last_comment);
    1437           0 :             if (error) {
    1438             :                 TRACE_ERROR_NUMBER("Failed to merge comment", error);
    1439             :                 return error;
    1440             :             }
    1441             :         }
    1442             :         else {
    1443         119 :             error = ini_comment_copy(po->ic, &(po->co->last_comment));
    1444         119 :             if (error) {
    1445             :                 TRACE_ERROR_NUMBER("Failed to copy comment", error);
    1446             :                 return error;
    1447             :             }
    1448             :         }
    1449             : 
    1450         119 :         ini_comment_destroy(po->ic);
    1451         119 :         po->ic = NULL;
    1452             :     }
    1453             : 
    1454             :     /* If there is a key being processed add it */
    1455         232 :     if (po->key) {
    1456         109 :         error = complete_value_processing(po);
    1457         109 :         if (error) {
    1458             :             TRACE_ERROR_NUMBER("Failed to complete value processing", error);
    1459             :             return error;
    1460             :         }
    1461             :     }
    1462             : 
    1463             :     /* If we are done save the section */
    1464         232 :     error = parser_save_section(po);
    1465         232 :     if (error) {
    1466             :         TRACE_ERROR_NUMBER("Failed to save section", error);
    1467             :         return error;
    1468             :     }
    1469             : 
    1470             :     /* Move to the next action */
    1471         232 :     error = col_enqueue_unsigned_property(po->queue,
    1472             :                                           PARSE_ACTION,
    1473             :                                           PARSE_DONE);
    1474         232 :     if (error) {
    1475             :         TRACE_ERROR_NUMBER("Failed to schedule an action", error);
    1476           0 :         return error;
    1477             :     }
    1478             : 
    1479             :     TRACE_FLOW_EXIT();
    1480             :     return EOK;
    1481             : }
    1482             : 
    1483             : 
    1484             : static int save_error(struct collection_item *el,
    1485             :                       unsigned line,
    1486             :                       int inerr,
    1487             :                       const char *err_txt)
    1488             : {
    1489         112 :     int error = EOK;
    1490             :     struct ini_parse_error pe;
    1491             : 
    1492             :     TRACE_FLOW_ENTRY();
    1493             : 
    1494             :     /* Clear the warning bit */
    1495         112 :     pe.error = inerr;
    1496         112 :     pe.line = line;
    1497         112 :     error = col_add_binary_property(el, NULL,
    1498             :                                     err_txt, &pe, sizeof(pe));
    1499             :     TRACE_FLOW_RETURN(error);
    1500             :     return error;
    1501             : }
    1502             : 
    1503             : 
    1504             : /* Error and warning processing */
    1505          82 : static int parser_error(struct parser_obj *po)
    1506             : {
    1507          82 :     int error = EOK;
    1508             :     uint32_t action;
    1509             :     const char *err_str;
    1510             : 
    1511             :     TRACE_FLOW_ENTRY();
    1512             : 
    1513          82 :     if (po->last_error & INI_WARNING) err_str = WARNING_TXT;
    1514          82 :     else err_str = ERROR_TXT;
    1515             : 
    1516         164 :     error = save_error(po->el,
    1517             :                        po->linenum,
    1518          82 :                        po->last_error & ~INI_WARNING,
    1519             :                        err_str);
    1520          82 :     if (error) {
    1521             :         TRACE_ERROR_NUMBER("Failed to add error to error list",
    1522             :                             error);
    1523             :         return error;
    1524             :     }
    1525             : 
    1526          82 :     if (po->last_error == ERR_BADCOMMENT) {
    1527             :         /* Avoid endless loop */
    1528           1 :         action = PARSE_DONE;
    1529           1 :         po->ret = EIO;
    1530             :     }
    1531          81 :     else if (po->error_level == INI_STOP_ON_ANY) {
    1532           0 :         action = PARSE_DONE;
    1533           0 :         if (po->last_error & INI_WARNING) po->ret = EILSEQ;
    1534           0 :         else po->ret = EIO;
    1535             :     }
    1536          81 :     else if (po->error_level == INI_STOP_ON_NONE) {
    1537          81 :         if (po->last_error != ERR_READ) {
    1538          76 :             action = PARSE_READ;
    1539          76 :             if (po->ret == 0) {
    1540          13 :                 if (po->last_error & INI_WARNING) po->ret = EILSEQ;
    1541          13 :                 else po->ret = EIO;
    1542             :             }
    1543             :             /* It it was warning but now if it is an error
    1544             :              * bump to return code to indicate error. */
    1545          63 :             else if((po->ret == EILSEQ) &&
    1546           0 :                     (!(po->last_error & INI_WARNING))) po->ret = EIO;
    1547             :         }
    1548             :         else {
    1549             :             /* Avoid endless loop */
    1550           5 :             action = PARSE_DONE;
    1551           5 :             po->ret = EIO;
    1552             :         }
    1553             :     }
    1554             :     else { /* Stop on error */
    1555           0 :         if (po->last_error & INI_WARNING) {
    1556           0 :             action = PARSE_READ;
    1557           0 :             po->ret = EILSEQ;
    1558             :         }
    1559             :         else {
    1560           0 :             action = PARSE_DONE;
    1561           0 :             po->ret = EIO;
    1562             :         }
    1563             :     }
    1564             : 
    1565             :     /* Prepare for reading */
    1566          82 :     if (action == PARSE_READ) {
    1567          76 :         if (po->last_read) {
    1568          76 :             free(po->last_read);
    1569          76 :             po->last_read = NULL;
    1570          76 :             po->last_read_len = 0;
    1571             :         }
    1572             :     }
    1573             :     else {
    1574             :         /* If we are done save the section */
    1575           6 :         error = parser_save_section(po);
    1576           6 :         if (error) {
    1577             :             TRACE_ERROR_NUMBER("Failed to save section", error);
    1578             :             /* If merging sections should produce error and we got error
    1579             :              * or if we merge sections but dup values produce error and
    1580             :              * we got error then it is not a fatal error so we need to handle
    1581             :              * it nicely and suppress it here. We already in the procees
    1582             :              * of handling another error and merge error does not matter here.
    1583             :              * We check for reverse condition and return error,
    1584             :              * otherwise fall through.
    1585             :              */
    1586           0 :             if (!((((po->collision_flags & INI_MS_MASK) == INI_MS_ERROR) &&
    1587             :                  (error == EEXIST)) ||
    1588           0 :                 (((po->collision_flags & INI_MS_MASK) == INI_MS_MERGE) &&
    1589             :                  ((po->collision_flags & INI_MV2S_MASK) == INI_MV2S_ERROR) &&
    1590             :                  (error == EEXIST)))) {
    1591             :                 return error;
    1592             :             }
    1593             :         }
    1594             :     }
    1595             : 
    1596             :     /* Move to the next action */
    1597          82 :     error = col_enqueue_unsigned_property(po->queue,
    1598             :                                           PARSE_ACTION,
    1599             :                                           action);
    1600          82 :     if (error) {
    1601             :         TRACE_ERROR_NUMBER("Failed to schedule an action", error);
    1602           0 :         return error;
    1603             :     }
    1604             : 
    1605             :     TRACE_FLOW_EXIT();
    1606             :     return EOK;
    1607             : }
    1608             : 
    1609             : 
    1610             : /* Run parser */
    1611         246 : static int parser_run(struct parser_obj *po)
    1612             : {
    1613         246 :     int error = EOK;
    1614         246 :     struct collection_item *item = NULL;
    1615         246 :     uint32_t action = 0;
    1616         246 :     action_fn operations[] = { parser_read,
    1617             :                                parser_inspect,
    1618             :                                parser_post,
    1619             :                                parser_error,
    1620             :                                NULL };
    1621             : 
    1622             :     TRACE_FLOW_ENTRY();
    1623             : 
    1624             :     while(1) {
    1625             :         /* Get next action */
    1626      496472 :         item = NULL;
    1627      496472 :         error = col_dequeue_item(po->queue, &item);
    1628      496472 :         if (error) {
    1629             :             TRACE_ERROR_NUMBER("Failed to get action", error);
    1630             :             return error;
    1631             :         }
    1632             : 
    1633             :         /* Get action, run operation */
    1634      496472 :         action = *((uint32_t *)(col_get_item_data(item)));
    1635      496472 :         col_delete_item(item);
    1636             : 
    1637      496472 :         if (action == PARSE_DONE) {
    1638             : 
    1639             :             TRACE_INFO_NUMBER("We are done", error);
    1640             : 
    1641             :             /* Report merge error in detect mode
    1642             :              * if no other error was detected. */
    1643         458 :             if ((po->ret == 0) &&
    1644         226 :                 (po->merge_error != 0) &&
    1645             :                 ((po->collision_flags & INI_MV1S_DETECT) ||
    1646           6 :                  (po->collision_flags & INI_MV2S_DETECT) ||
    1647             :                  (po->collision_flags & INI_MS_DETECT)))
    1648           6 :                 po->ret = po->merge_error;
    1649             : 
    1650         238 :             error = po->ret;
    1651             :             break;
    1652             :         }
    1653             : 
    1654      496234 :         error = operations[action](po);
    1655      496234 :         if (error) {
    1656             :             TRACE_ERROR_NUMBER("Failed to perform an action", error);
    1657             :             return error;
    1658             :         }
    1659             : 
    1660             :     }
    1661             : 
    1662             :     TRACE_FLOW_EXIT();
    1663         238 :     return error;
    1664             : }
    1665             : 
    1666             : /* Top level wrapper around the parser */
    1667         246 : int ini_config_parse(struct ini_cfgfile *file_ctx,
    1668             :                      int error_level,
    1669             :                      uint32_t collision_flags,
    1670             :                      uint32_t parse_flags,
    1671             :                      struct ini_cfgobj *ini_config)
    1672             : {
    1673         246 :     int error = EOK;
    1674         246 :     struct parser_obj *po = NULL;
    1675             :     uint32_t fl1, fl2, fl3;
    1676             : 
    1677             :     TRACE_FLOW_ENTRY();
    1678             : 
    1679         246 :     if ((!ini_config) || (!(ini_config->cfg))) {
    1680             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
    1681             :         return EINVAL;
    1682             :     }
    1683             : 
    1684         246 :     if (!file_ctx) {
    1685             :         TRACE_ERROR_NUMBER("Invalid file context", EINVAL);
    1686             :         return EINVAL;
    1687             :     }
    1688             : 
    1689         246 :     if (!valid_collision_flags(collision_flags)) {
    1690             :         TRACE_ERROR_NUMBER("Invalid flags.", EINVAL);
    1691             :         return EINVAL;
    1692             :     }
    1693             : 
    1694         246 :     if ((error_level != INI_STOP_ON_ANY) &&
    1695         246 :         (error_level != INI_STOP_ON_NONE) &&
    1696             :         (error_level != INI_STOP_ON_ERROR)) {
    1697             :         TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
    1698             :         return EINVAL;
    1699             :     }
    1700             : 
    1701         246 :     error = parser_create(ini_config,
    1702             :                           file_ctx->file,
    1703         246 :                           file_ctx->filename,
    1704             :                           error_level,
    1705             :                           collision_flags,
    1706             :                           parse_flags,
    1707             :                           &po);
    1708         246 :     if (error) {
    1709             :         TRACE_ERROR_NUMBER("Failed to perform an action", error);
    1710             :         return error;
    1711             :     }
    1712             : 
    1713         246 :     error = parser_run(po);
    1714         246 :     if (error) {
    1715          32 :         fl1 = collision_flags & INI_MS_MASK;
    1716          32 :         fl2 = collision_flags & INI_MV1S_MASK;
    1717          32 :         fl3 = collision_flags & INI_MV2S_MASK;
    1718          46 :         if ((error == EEXIST) &&
    1719          28 :             (((fl1 == INI_MS_DETECT) &&
    1720          14 :               (fl2 != INI_MV1S_ERROR) &&
    1721           9 :               (fl3 != INI_MV2S_ERROR)) ||
    1722          18 :              ((fl2 == INI_MV1S_DETECT) &&
    1723           9 :               (fl1 != INI_MS_ERROR) &&
    1724           9 :               (fl3 != INI_MV2S_ERROR)) ||
    1725          18 :              ((fl3 == INI_MV2S_DETECT) &&
    1726          10 :               (fl1 != INI_MS_ERROR) &&
    1727             :               (fl2 != INI_MV1S_ERROR)))) {
    1728             :             TRACE_ERROR_NUMBER("No error in detect mode", error);
    1729             :             /* Fall through */
    1730             :         }
    1731             :         else {
    1732             :             TRACE_ERROR_NUMBER("Failed to parse file", error);
    1733             :             TRACE_ERROR_NUMBER("Mode", collision_flags);
    1734          26 :             col_get_collection_count(ini_config->error_list, &(ini_config->count));
    1735          26 :             if(ini_config->count) (ini_config->count)--;
    1736          26 :             parser_destroy(po);
    1737          26 :             return error;
    1738             :         }
    1739             :     }
    1740             : 
    1741             :     /* If should be empty anyways */
    1742         220 :     col_destroy_collection_with_cb(ini_config->cfg, ini_cleanup_cb, NULL);
    1743         220 :     ini_config->cfg = po->top;
    1744         220 :     po->top = NULL;
    1745             : 
    1746         220 :     parser_destroy(po);
    1747             : 
    1748             :     TRACE_FLOW_EXIT();
    1749         220 :     return error;
    1750             : }

Generated by: LCOV version 1.10