Coverage Report

Created: 2023-03-26 07:33

/src/gnutls/lib/inih/ini.c
Line
Count
Source (jump to first uncovered line)
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
https://github.com/benhoyt/inih
7
8
*/
9
10
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
11
# define _CRT_SECURE_NO_WARNINGS
12
#endif
13
14
#include <stdio.h>
15
#include <ctype.h>
16
#include <string.h>
17
18
#include "ini.h"
19
20
#if !INI_USE_STACK
21
# include <stdlib.h>
22
#endif
23
24
#define MAX_SECTION 50
25
#define MAX_NAME 50
26
27
/* Strip whitespace chars off end of given string, in place. Return s. */
28
static char *rstrip(char *s)
29
0
{
30
0
  char *p = s + strlen(s);
31
0
  while (p > s && isspace((unsigned char)(*--p)))
32
0
    *p = '\0';
33
0
  return s;
34
0
}
35
36
/* Return pointer to first non-whitespace char in given string. */
37
static char *lskip(const char *s)
38
0
{
39
0
  while (*s && isspace((unsigned char)(*s)))
40
0
    s++;
41
0
  return (char *)s;
42
0
}
43
44
/* Return pointer to first char (of chars) or inline comment in given string,
45
   or pointer to null at end of string if neither found. Inline comment must
46
   be prefixed by a whitespace character to register as a comment. */
47
static char *find_chars_or_comment(const char *s, const char *chars)
48
0
{
49
0
#if INI_ALLOW_INLINE_COMMENTS
50
0
  int was_space = 0;
51
0
  while (*s && (!chars || !strchr(chars, *s)) &&
52
0
         !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
53
0
    was_space = isspace((unsigned char)(*s));
54
0
    s++;
55
0
  }
56
#else
57
  while (*s && (!chars || !strchr(chars, *s))) {
58
    s++;
59
  }
60
#endif
61
0
  return (char *)s;
62
0
}
63
64
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
65
static char *strncpy0(char *dest, const char *src, size_t size)
66
0
{
67
0
  strncpy(dest, src, size - 1);
68
0
  dest[size - 1] = '\0';
69
0
  return dest;
70
0
}
71
72
/* See documentation in header file. */
73
int ini_parse_file(FILE * file, ini_handler handler, void *user)
74
0
{
75
  /* Uses a fair bit of stack (use heap instead if you need to) */
76
0
#if INI_USE_STACK
77
0
  char line[INI_MAX_LINE];
78
0
  int max_line = INI_MAX_LINE;
79
#else
80
  char *line;
81
  int max_line = INI_INITIAL_ALLOC;
82
#endif
83
#if INI_ALLOW_REALLOC && !INI_USE_STACK
84
  char *new_line;
85
  int offset;
86
#endif
87
0
  char section[MAX_SECTION] = "";
88
0
  char prev_name[MAX_NAME] = "";
89
90
0
  char *end;
91
0
  char *name;
92
0
  char *value;
93
0
  int lineno = 0;
94
0
  int error = 0;
95
96
#if !INI_USE_STACK
97
  line = (char *)malloc(INI_INITIAL_ALLOC);
98
  if (!line) {
99
    return -2;
100
  }
101
#endif
102
103
#if INI_HANDLER_LINENO
104
# define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
105
#else
106
0
# define HANDLER(u, s, n, v) handler(u, s, n, v)
107
0
#endif
108
109
  /* Scan through stream line by line */
110
0
  while (fgets(line, max_line, file) != NULL) {
111
0
    char *start;
112
#if INI_ALLOW_REALLOC && !INI_USE_STACK
113
    offset = strlen(line);
114
    while (offset == max_line - 1 && line[offset - 1] != '\n') {
115
      max_line *= 2;
116
      if (max_line > INI_MAX_LINE)
117
        max_line = INI_MAX_LINE;
118
      new_line = realloc(line, max_line);
119
      if (!new_line) {
120
        free(line);
121
        return -2;
122
      }
123
      line = new_line;
124
      if (fgets(line + offset, max_line - offset, file) ==
125
          NULL)
126
        break;
127
      if (max_line >= INI_MAX_LINE)
128
        break;
129
      offset += strlen(line + offset);
130
    }
131
#endif
132
133
0
    lineno++;
134
135
0
    start = line;
136
0
#if INI_ALLOW_BOM
137
0
    if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
138
0
        (unsigned char)start[1] == 0xBB &&
139
0
        (unsigned char)start[2] == 0xBF) {
140
0
      start += 3;
141
0
    }
142
0
#endif
143
0
    start = lskip(rstrip(start));
144
145
0
    if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
146
      /* Start-of-line comment */
147
0
    }
148
0
#if INI_ALLOW_MULTILINE
149
0
    else if (*prev_name && *start && start > line) {
150
      /* Non-blank line with leading whitespace, treat as continuation
151
         of previous name's value (as per Python configparser). */
152
0
      if (!HANDLER(user, section, prev_name, start) && !error)
153
0
        error = lineno;
154
0
    }
155
0
#endif
156
0
    else if (*start == '[') {
157
      /* A "[section]" line */
158
0
      end = find_chars_or_comment(start + 1, "]");
159
0
      if (*end == ']') {
160
0
        *end = '\0';
161
0
        strncpy0(section, start + 1, sizeof(section));
162
0
        *prev_name = '\0';
163
0
      } else if (!error) {
164
        /* No ']' found on section line */
165
0
        error = lineno;
166
0
      }
167
0
    } else if (*start) {
168
      /* Not a comment, must be a name[=:]value pair */
169
0
      end = find_chars_or_comment(start, "=:");
170
0
      if (*end == '=' || *end == ':') {
171
0
        *end = '\0';
172
0
        name = rstrip(start);
173
0
        value = end + 1;
174
0
#if INI_ALLOW_INLINE_COMMENTS
175
0
        end = find_chars_or_comment(value, NULL);
176
0
        if (*end)
177
0
          *end = '\0';
178
0
#endif
179
0
        value = lskip(value);
180
0
        rstrip(value);
181
182
        /* Valid name[=:]value pair found, call handler */
183
0
        strncpy0(prev_name, name, sizeof(prev_name));
184
0
        if (!HANDLER(user, section, name, value)
185
0
            && !error)
186
0
          error = lineno;
187
0
      } else if (!error) {
188
        /* No '=' or ':' found on name[=:]value line */
189
0
        error = lineno;
190
0
      }
191
0
    }
192
0
#if INI_STOP_ON_FIRST_ERROR
193
0
    if (error)
194
0
      break;
195
0
#endif
196
0
  }
197
198
#if !INI_USE_STACK
199
  free(line);
200
#endif
201
202
0
  return error;
203
0
}