Coverage Report

Created: 2026-10-28 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/inih/ini.c
Line
Count
Source
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
168
{
30
168
  char *p = s + strlen(s);
31
288
  while (p > s && isspace((unsigned char)(*--p)))
32
120
    *p = '\0';
33
168
  return s;
34
168
}
35
36
/* Return pointer to first non-whitespace char in given string. */
37
static char *lskip(const char *s)
38
120
{
39
168
  while (*s && isspace((unsigned char)(*s)))
40
48
    s++;
41
120
  return (char *)s;
42
120
}
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
108
{
49
108
#if INI_ALLOW_INLINE_COMMENTS
50
108
  int was_space = 0;
51
1.39k
  while (*s && (!chars || !strchr(chars, *s)) &&
52
1.28k
         !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
53
1.28k
    was_space = isspace((unsigned char)(*s));
54
1.28k
    s++;
55
1.28k
  }
56
#else
57
  while (*s && (!chars || !strchr(chars, *s))) {
58
    s++;
59
  }
60
#endif
61
108
  return (char *)s;
62
108
}
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
60
{
67
60
  strncpy(dest, src, size - 1);
68
60
  dest[size - 1] = '\0';
69
60
  return dest;
70
60
}
71
72
/* See documentation in header file. */
73
int ini_parse_file(FILE *file, ini_handler handler, void *user)
74
12
{
75
  /* Uses a fair bit of stack (use heap instead if you need to) */
76
12
#if INI_USE_STACK
77
12
  char line[INI_MAX_LINE];
78
12
  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
12
  char section[MAX_SECTION] = "";
88
12
  char prev_name[MAX_NAME] = "";
89
90
12
  char *end;
91
12
  char *name;
92
12
  char *value;
93
12
  int lineno = 0;
94
12
  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
96
#define HANDLER(u, s, n, v) handler(u, s, n, v)
107
12
#endif
108
109
  /* Scan through stream line by line */
110
84
  while (fgets(line, max_line, file) != NULL) {
111
72
    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
72
    lineno++;
134
135
72
    start = line;
136
72
#if INI_ALLOW_BOM
137
72
    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
72
#endif
143
72
    start = lskip(rstrip(start));
144
145
72
    if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
146
      /* Start-of-line comment */
147
12
    }
148
60
#if INI_ALLOW_MULTILINE
149
60
    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
60
#endif
156
60
    else if (*start == '[') {
157
      /* A "[section]" line */
158
12
      end = find_chars_or_comment(start + 1, "]");
159
12
      if (*end == ']') {
160
12
        *end = '\0';
161
12
        strncpy0(section, start + 1, sizeof(section));
162
12
        *prev_name = '\0';
163
12
      } else if (!error) {
164
        /* No ']' found on section line */
165
0
        error = lineno;
166
0
      }
167
48
    } else if (*start) {
168
      /* Not a comment, must be a name[=:]value pair */
169
48
      end = find_chars_or_comment(start, "=:");
170
48
      if (*end == '=' || *end == ':') {
171
48
        *end = '\0';
172
48
        name = rstrip(start);
173
48
        value = end + 1;
174
48
#if INI_ALLOW_INLINE_COMMENTS
175
48
        end = find_chars_or_comment(value, NULL);
176
48
        if (*end)
177
0
          *end = '\0';
178
48
#endif
179
48
        value = lskip(value);
180
48
        rstrip(value);
181
182
        /* Valid name[=:]value pair found, call handler */
183
48
        strncpy0(prev_name, name, sizeof(prev_name));
184
48
        if (!HANDLER(user, section, name, value) &&
185
0
            !error)
186
0
          error = lineno;
187
48
      } else if (!error) {
188
        /* No '=' or ':' found on name[=:]value line */
189
0
        error = lineno;
190
0
      }
191
48
    }
192
72
#if INI_STOP_ON_FIRST_ERROR
193
72
    if (error)
194
0
      break;
195
72
#endif
196
72
  }
197
198
#if !INI_USE_STACK
199
  free(line);
200
#endif
201
202
12
  return error;
203
12
}