Coverage Report

Created: 2025-03-06 06:58

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