LCOV - code coverage report
Current view: top level - src/plugins/ini/inih-r29 - inih.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 143 210 68.1 %
Date: 2019-09-12 12:28:41 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /* inih -- simple .INI file parser
       2             : 
       3             :    inih is released under the New BSD license (see LICENSE.txt). Go to the project
       4             :    home page for more info:
       5             : 
       6             : http://code.google.com/p/inih/
       7             : 
       8             : */
       9             : 
      10             : #include "inih.h"
      11             : #include <ctype.h>
      12             : #include <kdbassert.h>
      13             : #include <kdblogger.h>
      14             : #include <regex.h>
      15             : #include <stdio.h>
      16             : #include <stdlib.h>
      17             : #include <string.h>
      18             : 
      19             : #if !INI_USE_STACK
      20             : #include <stdlib.h>
      21             : #endif
      22             : 
      23             : #define MAX_SECTION 65535
      24             : #define MAX_NAME 65535
      25             : 
      26             : /* Strip whitespace chars off end of given string, in place. Return s. */
      27      256948 : static char * rstrip (char * s)
      28             : {
      29      256948 :         char * p = s + strlen (s);
      30      548308 :         while (p > s && isspace ((unsigned char) (*--p)))
      31             :         {
      32       34412 :                 *p = '\0';
      33             :         }
      34      256948 :         return s;
      35             : }
      36             : 
      37             : /* Return pointer to first non-whitespace char in given string. */
      38      293074 : static char * lskip (const char * s)
      39             : {
      40      691122 :         while (*s && isspace ((unsigned char) (*s)))
      41             :         {
      42      104974 :                 s++;
      43             :         }
      44      293074 :         return (char *) s;
      45             : }
      46             : 
      47             : /* Return pointer to first char c or ';' comment in given string, or pointer to
      48             :    null at end of string if neither found. ';' must be prefixed by a whitespace
      49             :    character to register as a comment. */
      50      128473 : static char * find_char_or_comment (const char * s, char c)
      51             : {
      52      128473 :         int was_whitespace = 0;
      53     1216465 :         while (*s && *s != c && !(was_whitespace && *s == ';'))
      54             :         {
      55      959519 :                 was_whitespace = isspace ((unsigned char) (*s));
      56      959519 :                 s++;
      57             :         }
      58      128473 :         return (char *) s;
      59             : }
      60             : 
      61             : /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
      62             : static char * strncpy0 (char * dest, const char * src, size_t size)
      63             : {
      64             : #if defined(__GNUC__) && __GNUC__ >= 8 && !defined(__clang__)
      65             : #pragma GCC diagnostic push
      66             : #pragma GCC diagnostic ignored "-Wstringop-truncation"
      67             : #endif
      68      150974 :         strncpy (dest, src, size);
      69             : #if defined(__GNUC__) && __GNUC__ >= 8 && !defined(__clang__)
      70             : #pragma GCC diagnostic pop
      71             : #endif
      72      150974 :         dest[size - 1] = '\0';
      73             :         return dest;
      74             : }
      75             : 
      76      164598 : static int isContinuation (const char * line, const struct IniConfig * config)
      77             : {
      78      164598 :         if (!(*line)) return 0;
      79      164598 :         return !strncmp (line, config->continuationString, strlen (config->continuationString));
      80             : }
      81             : 
      82             : static int isSection (const char * line)
      83             : {
      84      153771 :         if (!(*line)) return 0;
      85      153771 :         if (*line == '[') return 1;
      86             :         return 0;
      87             : }
      88             : 
      89             : static int isComment (const char * line)
      90             : {
      91      131275 :         if (!(*line)) return 0;
      92      131275 :         if (*line == '#' || *line == ';') return 1;
      93             :         return 0;
      94             : }
      95             : 
      96             : /* See documentation in header file. */
      97       16999 : int ini_parse_file (FILE * file, const struct IniConfig * config, void * user)
      98             : {
      99             :         /* Uses a fair bit of stack (use heap instead if you need to) */
     100             :         char * line;
     101             : 
     102       16999 :         char section[MAX_SECTION] = "";
     103       16999 :         char prev_name[MAX_NAME] = "";
     104             : 
     105             :         char * start;
     106             :         char * end;
     107             :         char * name;
     108             :         char * value;
     109       16999 :         char delim = config->delim;
     110       16999 :         int lineno = 0;
     111       16999 :         int error = 0;
     112             : 
     113       16999 :         line = (char *) malloc (INI_MAX_LINE);
     114             : 
     115             :         ELEKTRA_LOG_DEBUG ("Allocated memory for line");
     116             : 
     117       16999 :         if (!line)
     118             :         {
     119             :                 return -2;
     120             :         }
     121             : 
     122             :         /* Scan through file line by line */
     123      181607 :         while (fgets (line, INI_MAX_LINE, file) != NULL)
     124             :         {
     125      164610 :                 lineno++;
     126             :                 ELEKTRA_LOG_DEBUG ("Read line %d with content “%s”", lineno, line);
     127             : 
     128      164610 :                 start = line;
     129             : #if INI_ALLOW_BOM
     130      164610 :                 if (lineno == 1 && (unsigned char) start[0] == 0xEF && (unsigned char) start[1] == 0xBB && (unsigned char) start[2] == 0xBF)
     131             :                 {
     132           0 :                         start += 3;
     133           0 :                         config->bomHandler (user, 1);
     134             :                 }
     135             :                 else
     136             :                 {
     137      164610 :                         config->bomHandler (user, 0);
     138             :                 }
     139             : #endif
     140      164610 :                 if (*start == '\n')
     141             :                 {
     142          12 :                         if (!config->commentHandler (user, "") && !error) error = lineno;
     143          12 :                         continue;
     144             :                 }
     145      164598 :                 start = lskip (line);
     146      164598 :                 if (*start == '\0')
     147             :                 {
     148           0 :                         if (!config->commentHandler (user, "") && !error) error = lineno;
     149           0 :                         continue;
     150             :                 }
     151      164598 :                 if (isContinuation (line, config) && config->supportMultiline && *prev_name)
     152             :                 {
     153       10827 :                         start = line + strlen (config->continuationString);
     154       10827 :                         if (*start == '"') ++start;
     155       10827 :                         end = line + (strlen (line) - 1);
     156       32481 :                         while ((*end != '"') && (!isprint (*end)) && (end > start))
     157             :                         {
     158       10827 :                                 if (*end == '\n') *end = '\0';
     159       10827 :                                 --end;
     160             :                         }
     161       10827 :                         if (*end == '"') *end = '\0';
     162             : 
     163       10827 :                         if (!config->keyHandler (user, section, prev_name, start, 1) && !error) error = lineno;
     164             :                 }
     165      307542 :                 else if (isSection (line))
     166             :                 {
     167             :                         ELEKTRA_LOG_DEBUG ("Line contains a section");
     168       22496 :                         end = line + (strlen (line) - 1);
     169       67488 :                         while (end > start)
     170             :                         {
     171       44992 :                                 if (*end == ']') break;
     172       22496 :                                 --end;
     173             :                         }
     174       22496 :                         ++start;
     175       22496 :                         if (*end == ']')
     176             :                         {
     177       22496 :                                 *end = '\0';
     178       22496 :                                 strncpy0 (section, start, sizeof (section));
     179       22496 :                                 *prev_name = '\0';
     180             :                                 ELEKTRA_LOG_DEBUG ("Found section “%s”", section);
     181             : 
     182       22496 :                                 size_t numberBackslashes = 0;
     183       44993 :                                 for (char * endSection = section + strlen (section) - 1; endSection >= section && *endSection == '\\';
     184           1 :                                      endSection--)
     185             :                                 {
     186           1 :                                         numberBackslashes++;
     187             :                                 }
     188       22496 :                                 if (numberBackslashes % 2 != 0)
     189             :                                 {
     190             :                                         ELEKTRA_LOG_WARNING ("Found uneven number of backlashes at end of section");
     191             :                                         error = lineno;
     192             :                                         break;
     193             :                                 }
     194             : 
     195       22495 :                                 if (!config->sectionHandler (user, section) && !error) error = lineno;
     196             :                         }
     197             :                         else
     198             :                         {
     199           0 :                                 end = line + (strlen (line) - 1);
     200           0 :                                 if (*end == '\n')
     201             :                                 {
     202             :                                         strncpy0 (section, start, sizeof (section));
     203           0 :                                         while (fgets (line, INI_MAX_LINE, file))
     204             :                                         {
     205           0 :                                                 end = line + (strlen (line) - 1);
     206           0 :                                                 while ((end > line) && *end != ']')
     207           0 :                                                         --end;
     208           0 :                                                 if (*end == ']')
     209             :                                                 {
     210           0 :                                                         *end = '\0';
     211           0 :                                                         strncpy0 (section + strlen (section), line, sizeof (section) - strlen (section));
     212           0 :                                                         *prev_name = '\0';
     213           0 :                                                         if (!config->sectionHandler (user, section) && !error) error = lineno;
     214             :                                                         break;
     215             :                                                 }
     216             :                                                 else
     217             :                                                 {
     218           0 :                                                         strncpy0 (section + strlen (section), line, sizeof (section) - strlen (section));
     219             :                                                 }
     220             :                                         }
     221             :                                 }
     222             :                                 else
     223             :                                 {
     224             :                                         error = lineno;
     225             :                                 }
     226             :                         }
     227             :                 }
     228      262550 :                 else if (isComment (line))
     229             :                 {
     230        2797 :                         start = line;
     231        2797 :                         end = line + (strlen (line) - 1);
     232        2797 :                         if (*end == '\n') *end = '\0';
     233        2797 :                         if (!config->commentHandler (user, start) && !error) error = lineno;
     234             :                 }
     235             :                 else
     236             :                 {
     237             :                         ELEKTRA_LOG_DEBUG ("Line contains a key");
     238             : 
     239             :                         char * ptr = start;
     240             :                         unsigned int assign = 0;
     241             :                         ELEKTRA_LOG_DEBUG ("Search for delimiter “%c”", delim);
     242     6275606 :                         while (*ptr)
     243             :                         {
     244     6147128 :                                 if (*ptr == delim)
     245             :                                 {
     246      128486 :                                         ++assign;
     247             :                                 }
     248     6147128 :                                 ++ptr;
     249             :                         }
     250             : 
     251      128478 :                         if (assign == 1)
     252             :                         {
     253             :                                 ELEKTRA_LOG_DEBUG ("Found exactly one delimiter");
     254      128464 :                                 name = start;
     255      128464 :                                 end = strchr (start, delim);
     256      128464 :                                 if (*name == '"')
     257             :                                 {
     258             :                                         ELEKTRA_LOG_DEBUG ("Name starts with double quote character");
     259        1821 :                                         ++name;
     260        1821 :                                         if (*(end - 2) == '"')
     261             :                                         {
     262           0 :                                                 *(end - 2) = '\0';
     263             :                                         }
     264        1821 :                                         else if (*(end - 1) == '"')
     265             :                                         {
     266        1820 :                                                 *(end - 1) = '\0';
     267             :                                         }
     268             :                                         else
     269             :                                         {
     270             :                                                 ELEKTRA_LOG_DEBUG ("Did not find closing double quote characters in current line");
     271             :                                                 strncpy0 (prev_name, name, sizeof (prev_name));
     272           2 :                                                 while (fgets (line, INI_MAX_LINE, file))
     273             :                                                 {
     274             :                                                         ELEKTRA_LOG_DEBUG ("Read continuation line with content “%s”", line);
     275           1 :                                                         end = line + (strlen (line) - 1);
     276           5 :                                                         while (end > line && *end != '"')
     277           3 :                                                                 --end;
     278           1 :                                                         if (*end == '"')
     279             :                                                         {
     280             :                                                                 ELEKTRA_LOG_DEBUG ("Found closing double quote character");
     281           0 :                                                                 *(end++) = '\0';
     282           0 :                                                                 strncpy0 (prev_name + strlen (prev_name), line,
     283           0 :                                                                           sizeof (prev_name) - strlen (prev_name));
     284             :                                                                 break;
     285             :                                                         }
     286             :                                                         else
     287             :                                                         {
     288             :                                                                 ELEKTRA_LOG_DEBUG ("Found name continuation");
     289           1 :                                                                 strncpy (prev_name + strlen (prev_name), line,
     290             :                                                                          sizeof (prev_name) - strlen (prev_name));
     291             :                                                         }
     292             :                                                         ELEKTRA_LOG_DEBUG ("New extended name is “%s”", prev_name);
     293             :                                                 }
     294             :                                                 name = prev_name;
     295             :                                                 ELEKTRA_LOG_DEBUG ("Name of key is “%s”", name);
     296             :                                         }
     297             :                                 }
     298      128464 :                                 if (*end != delim)
     299             :                                 {
     300             :                                         ELEKTRA_LOG_DEBUG ("Search for delimiter in “%s”", end);
     301           1 :                                         ptr = lskip (end + 1);
     302           1 :                                         end = strchr (ptr, delim);
     303           1 :                                         if (end && *end == delim)
     304             :                                         {
     305           0 :                                                 *end = '\0';
     306             :                                                 ELEKTRA_LOG_DEBUG ("Found delimiter – New name is “%s”", end);
     307             :                                         }
     308             :                                         else
     309             :                                         {
     310             :                                                 ELEKTRA_LOG_WARNING ("Unable to find delimiter");
     311             :                                                 error = lineno;
     312             :                                                 break;
     313             :                                         }
     314             :                                 }
     315             :                                 else
     316             :                                 {
     317      128463 :                                         *end = '\0';
     318             :                                 }
     319      128463 :                                 if (name != prev_name && end > line)
     320             :                                 {
     321      128463 :                                         rstrip (end - 1);
     322             :                                 }
     323      128463 :                                 value = lskip (end + 1);
     324      128463 :                                 end = find_char_or_comment (value, '\0');
     325      128463 :                                 if (*end == ';') *end = '\0';
     326      128463 :                                 rstrip (value);
     327      128463 :                                 if (*value == '"')
     328             :                                 {
     329         313 :                                         *(value++) = '\0';
     330        1252 :                                         while ((*end != '"') && !isprint (*end) && end > value)
     331         626 :                                                 --end;
     332         313 :                                         if (*end == '"') *end = '\0';
     333             :                                 }
     334      128463 :                                 if (prev_name != name) strncpy0 (prev_name, name, sizeof (prev_name));
     335      128463 :                                 if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno;
     336             :                         }
     337          14 :                         else if (assign == 0)
     338             :                         {
     339             :                                 ELEKTRA_LOG_DEBUG ("Found no delimiter");
     340           4 :                                 if (*start == '"')
     341             :                                 {
     342             :                                         ELEKTRA_LOG_DEBUG ("Found initial double quote character");
     343           0 :                                         ++start;
     344           0 :                                         end = line + (strlen (line) - 1);
     345           0 :                                         while (end > start && *end != '"')
     346           0 :                                                 --end;
     347           0 :                                         if (*end == '"' && end != start)
     348             :                                         {
     349           0 :                                                 *end = '\0';
     350           0 :                                                 if (!config->keyHandler (user, section, start, NULL, 0) && !error) error = lineno;
     351             :                                         }
     352             :                                         else
     353             :                                         {
     354             :                                                 ELEKTRA_LOG_DEBUG ("Did not find closing double quote character");
     355             :                                                 strncpy0 (prev_name, start, sizeof (prev_name));
     356           0 :                                                 while (fgets (line, INI_MAX_LINE, file))
     357             :                                                 {
     358           0 :                                                         end = line + (strlen (line) - 1);
     359             :                                                         ELEKTRA_LOG_DEBUG ("Read continuation line with content “%s”", line);
     360           0 :                                                         while (end > line && *end != '"')
     361           0 :                                                                 --end;
     362           0 :                                                         if (*end == '"')
     363             :                                                         {
     364             :                                                                 ELEKTRA_LOG_DEBUG ("Found closing double quote character");
     365           0 :                                                                 *end = '\0';
     366           0 :                                                                 strncpy0 (prev_name + strlen (prev_name), line,
     367           0 :                                                                           sizeof (prev_name) - strlen (prev_name));
     368             :                                                                 break;
     369             :                                                         }
     370             :                                                         else
     371             :                                                         {
     372             :                                                                 ELEKTRA_LOG_DEBUG ("Found name continuation");
     373           0 :                                                                 strncpy (prev_name + strlen (prev_name), line,
     374             :                                                                          sizeof (prev_name) - strlen (prev_name));
     375             :                                                         }
     376             :                                                         ELEKTRA_LOG_DEBUG ("New extended name is “%s”", prev_name);
     377             :                                                 }
     378           0 :                                                 name = prev_name;
     379           0 :                                                 ptr = end + 1;
     380           0 :                                                 end = strchr (ptr, '=');
     381           0 :                                                 if (!end) end = strchr (ptr, ':');
     382           0 :                                                 if (!end)
     383             :                                                 {
     384           0 :                                                         if (!config->keyHandler (user, section, name, NULL, 0) && !error) error = lineno;
     385             :                                                 }
     386             :                                                 else
     387             :                                                 {
     388           0 :                                                         *end = '\0';
     389           0 :                                                         value = lskip (end + 1);
     390           0 :                                                         if (*value == '"') end = find_char_or_comment (value, '\0');
     391           0 :                                                         if (*end == ';') *end = '\0';
     392           0 :                                                         rstrip (value);
     393           0 :                                                         if (*value == '"' || *(value + 1) == '"')
     394             :                                                         {
     395           0 :                                                                 if (*value == '"')
     396           0 :                                                                         *(value++) = '\0';
     397           0 :                                                                 else if (*(value + 1) == '"')
     398             :                                                                 {
     399           0 :                                                                         *(value + 1) = '\0';
     400           0 :                                                                         value += 2;
     401             :                                                                 }
     402           0 :                                                                 while ((*end != '"') && !isprint (*end) && end > value)
     403           0 :                                                                         --end;
     404           0 :                                                                 if (*end == '"') *end = '\0';
     405             :                                                         }
     406             :                                                         if (prev_name != name) strncpy0 (prev_name, name, sizeof (prev_name));
     407           0 :                                                         if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno;
     408             :                                                 }
     409             :                                         }
     410             :                                 }
     411             :                                 else
     412             :                                 {
     413           4 :                                         name = rstrip (start);
     414           4 :                                         strncpy0 (prev_name, name, sizeof (prev_name));
     415           4 :                                         if (!config->keyHandler (user, section, name, NULL, 0) && !error) error = lineno;
     416             :                                 }
     417             :                         }
     418             :                         else
     419             :                         {
     420             :                                 ELEKTRA_LOG_DEBUG ("Found multiple delimiters");
     421             : 
     422          10 :                                 end = start + 1;
     423          10 :                                 if (*start == '"')
     424             :                                 {
     425             :                                         /* Quoted Name:
     426             :                                                 - The name has to end with a double quote
     427             :                                                 - We do not allow any double quotes inside the name
     428             :                                          */
     429             :                                         name = start + 1;
     430           8 :                                         while (*end && *end != '"')
     431             :                                         {
     432           6 :                                                 end++;
     433             :                                         }
     434           2 :                                         if (!*end)
     435             :                                         {
     436             :                                                 error = lineno;
     437             :                                                 break;
     438             :                                         }
     439           2 :                                         *end = '\0';
     440             : 
     441           2 :                                         value = end + 1;
     442           2 :                                         value = lskip (value);
     443           2 :                                         if (!*value || *value != delim)
     444             :                                         {
     445             :                                                 error = lineno;
     446             :                                                 break;
     447             :                                         }
     448           2 :                                         value++;
     449             :                                 }
     450             :                                 else
     451             :                                 {
     452             :                                         /* Unquoted Name:
     453             :                                                 - The name can not contain a delimiter unless it is the very first character
     454             :                                                 - Trailing whitespace is removed from the name
     455             :                                          */
     456             :                                         name = start;
     457          48 :                                         while (*end && *end != delim)
     458             :                                         {
     459          40 :                                                 end++;
     460             :                                         }
     461           8 :                                         if (!end)
     462             :                                         {
     463             :                                                 error = lineno;
     464             :                                                 break;
     465             :                                         }
     466           8 :                                         *end = '\0';
     467           8 :                                         start = rstrip (start);
     468           8 :                                         value = end + 1;
     469             :                                 }
     470          10 :                                 value = lskip (value);
     471             : 
     472          10 :                                 end = find_char_or_comment (value, '\0');
     473          10 :                                 if (*end == ';') *end = '\0';
     474          10 :                                 rstrip (value);
     475          10 :                                 if (*value == '"' || *(value + 1) == '"')
     476             :                                 {
     477          10 :                                         if (*value == '"')
     478          10 :                                                 *(value++) = '\0';
     479           0 :                                         else if (*(value + 1) == '"')
     480             :                                         {
     481           0 :                                                 *(value + 1) = '\0';
     482           0 :                                                 value += 2;
     483             :                                         }
     484          30 :                                         while ((*end != '"') && !isprint (*end) && end > value)
     485          20 :                                                 --end;
     486          10 :                                         if (*end == '"') *end = '\0';
     487             :                                 }
     488          10 :                                 strncpy0 (prev_name, name, sizeof (prev_name));
     489             : 
     490          10 :                                 if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno;
     491             :                         }
     492             :                 }
     493             : 
     494             : #if INI_STOP_ON_FIRST_ERROR
     495             :                 if (error) break;
     496             : #endif
     497             :         }
     498             : 
     499       16999 :         free (line);
     500       16999 :         return error;
     501             : }
     502             : 
     503             : /* See documentation in header file. */
     504           0 : int ini_parse (const char * filename, const struct IniConfig * config, void * user)
     505             : {
     506             :         FILE * file;
     507             :         int error;
     508             : 
     509           0 :         file = fopen (filename, "r");
     510           0 :         if (!file) return -1;
     511           0 :         error = ini_parse_file (file, config, user);
     512           0 :         fclose (file);
     513           0 :         return error;
     514             : }

Generated by: LCOV version 1.13