Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/SharedDependencies/Sources/inih/ini.c
2 views
1
/* inih -- simple .INI file parser
2
3
SPDX-License-Identifier: BSD-3-Clause
4
5
Copyright (C) 2009-2025, Ben Hoyt
6
7
inih is released under the New BSD license (see LICENSE.txt). Go to the project
8
home page for more info:
9
10
https://github.com/benhoyt/inih
11
12
*/
13
14
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
15
#define _CRT_SECURE_NO_WARNINGS
16
#endif
17
18
#include <stdio.h>
19
#include <ctype.h>
20
#include <string.h>
21
22
#include "ini.h"
23
24
#if !INI_USE_STACK
25
#if INI_CUSTOM_ALLOCATOR
26
#include <stddef.h>
27
void* ini_malloc(size_t size);
28
void ini_free(void* ptr);
29
void* ini_realloc(void* ptr, size_t size);
30
#else
31
#include <stdlib.h>
32
#define ini_malloc malloc
33
#define ini_free free
34
#define ini_realloc realloc
35
#endif
36
#endif
37
38
#define MAX_SECTION 50
39
#define MAX_NAME 50
40
41
/* Used by ini_parse_string() to keep track of string parsing state. */
42
typedef struct {
43
const char* ptr;
44
size_t num_left;
45
} ini_parse_string_ctx;
46
47
/* Strip whitespace chars off end of given string, in place. end must be a
48
pointer to the NUL terminator at the end of the string. Return s. */
49
static char* ini_rstrip(char* s, char* end)
50
{
51
while (end > s && isspace((unsigned char)(*--end)))
52
*end = '\0';
53
return s;
54
}
55
56
/* Return pointer to first non-whitespace char in given string. */
57
static char* ini_lskip(const char* s)
58
{
59
while (*s && isspace((unsigned char)(*s)))
60
s++;
61
return (char*)s;
62
}
63
64
/* Return pointer to first char (of chars) or inline comment in given string,
65
or pointer to NUL at end of string if neither found. Inline comment must
66
be prefixed by a whitespace character to register as a comment. */
67
static char* ini_find_chars_or_comment(const char* s, const char* chars)
68
{
69
#if INI_ALLOW_INLINE_COMMENTS
70
int was_space = 0;
71
while (*s && (!chars || !strchr(chars, *s)) &&
72
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
73
was_space = isspace((unsigned char)(*s));
74
s++;
75
}
76
#else
77
while (*s && (!chars || !strchr(chars, *s))) {
78
s++;
79
}
80
#endif
81
return (char*)s;
82
}
83
84
/* Similar to strncpy, but ensures dest (size bytes) is
85
NUL-terminated, and doesn't pad with NULs. */
86
static char* ini_strncpy0(char* dest, const char* src, size_t size)
87
{
88
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
89
size_t i;
90
for (i = 0; i < size - 1 && src[i]; i++)
91
dest[i] = src[i];
92
dest[i] = '\0';
93
return dest;
94
}
95
96
/* See documentation in header file. */
97
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
98
void* user)
99
{
100
/* Uses a fair bit of stack (use heap instead if you need to) */
101
#if INI_USE_STACK
102
char line[INI_MAX_LINE];
103
size_t max_line = INI_MAX_LINE;
104
#else
105
char* line;
106
size_t max_line = INI_INITIAL_ALLOC;
107
#endif
108
#if INI_ALLOW_REALLOC && !INI_USE_STACK
109
char* new_line;
110
#endif
111
char section[MAX_SECTION] = "";
112
#if INI_ALLOW_MULTILINE
113
char prev_name[MAX_NAME] = "";
114
#endif
115
116
size_t offset;
117
char* start;
118
char* end;
119
char* name;
120
char* value;
121
int lineno = 0;
122
int error = 0;
123
char abyss[16]; /* Used to consume input when a line is too long. */
124
125
#if !INI_USE_STACK
126
line = (char*)ini_malloc(INI_INITIAL_ALLOC);
127
if (!line) {
128
return -2;
129
}
130
#endif
131
132
#if INI_HANDLER_LINENO
133
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
134
#else
135
#define HANDLER(u, s, n, v) handler(u, s, n, v)
136
#endif
137
138
/* Scan through stream line by line */
139
while (reader(line, (int)max_line, stream) != NULL) {
140
offset = strlen(line);
141
142
#if INI_ALLOW_REALLOC && !INI_USE_STACK
143
while (max_line < INI_MAX_LINE &&
144
offset == max_line - 1 && line[offset - 1] != '\n') {
145
max_line *= 2;
146
if (max_line > INI_MAX_LINE)
147
max_line = INI_MAX_LINE;
148
new_line = ini_realloc(line, max_line);
149
if (!new_line) {
150
ini_free(line);
151
return -2;
152
}
153
line = new_line;
154
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
155
break;
156
offset += strlen(line + offset);
157
}
158
#endif
159
160
lineno++;
161
162
/* If line exceeded INI_MAX_LINE bytes, discard till end of line. */
163
if (offset == max_line - 1 && line[offset - 1] != '\n') {
164
while (reader(abyss, sizeof(abyss), stream) != NULL) {
165
if (!error)
166
error = lineno;
167
if (abyss[strlen(abyss) - 1] == '\n')
168
break;
169
}
170
}
171
172
start = line;
173
#if INI_ALLOW_BOM
174
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
175
(unsigned char)start[1] == 0xBB &&
176
(unsigned char)start[2] == 0xBF) {
177
start += 3;
178
}
179
#endif
180
start = ini_rstrip(ini_lskip(start), line + offset);
181
182
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
183
/* Start-of-line comment */
184
}
185
#if INI_ALLOW_MULTILINE
186
else if (*prev_name && *start && start > line) {
187
#if INI_ALLOW_INLINE_COMMENTS
188
end = ini_find_chars_or_comment(start, NULL);
189
*end = '\0';
190
ini_rstrip(start, end);
191
#endif
192
/* Non-blank line with leading whitespace, treat as continuation
193
of previous name's value (as per Python configparser). */
194
if (!HANDLER(user, section, prev_name, start) && !error)
195
error = lineno;
196
}
197
#endif
198
else if (*start == '[') {
199
/* A "[section]" line */
200
end = ini_find_chars_or_comment(start + 1, "]");
201
if (*end == ']') {
202
*end = '\0';
203
ini_strncpy0(section, start + 1, sizeof(section));
204
#if INI_ALLOW_MULTILINE
205
*prev_name = '\0';
206
#endif
207
#if INI_CALL_HANDLER_ON_NEW_SECTION
208
if (!HANDLER(user, section, NULL, NULL) && !error)
209
error = lineno;
210
#endif
211
}
212
else if (!error) {
213
/* No ']' found on section line */
214
error = lineno;
215
}
216
}
217
else if (*start) {
218
/* Not a comment, must be a name[=:]value pair */
219
end = ini_find_chars_or_comment(start, "=:");
220
if (*end == '=' || *end == ':') {
221
*end = '\0';
222
name = ini_rstrip(start, end);
223
value = end + 1;
224
#if INI_ALLOW_INLINE_COMMENTS
225
end = ini_find_chars_or_comment(value, NULL);
226
*end = '\0';
227
#endif
228
value = ini_lskip(value);
229
ini_rstrip(value, end);
230
231
#if INI_ALLOW_MULTILINE
232
ini_strncpy0(prev_name, name, sizeof(prev_name));
233
#endif
234
/* Valid name[=:]value pair found, call handler */
235
if (!HANDLER(user, section, name, value) && !error)
236
error = lineno;
237
}
238
else {
239
/* No '=' or ':' found on name[=:]value line */
240
#if INI_ALLOW_NO_VALUE
241
*end = '\0';
242
name = ini_rstrip(start, end);
243
if (!HANDLER(user, section, name, NULL) && !error)
244
error = lineno;
245
#else
246
if (!error)
247
error = lineno;
248
#endif
249
}
250
}
251
252
#if INI_STOP_ON_FIRST_ERROR
253
if (error)
254
break;
255
#endif
256
}
257
258
#if !INI_USE_STACK
259
ini_free(line);
260
#endif
261
262
return error;
263
}
264
265
/* See documentation in header file. */
266
int ini_parse_file(FILE* file, ini_handler handler, void* user)
267
{
268
return ini_parse_stream((ini_reader)fgets, file, handler, user);
269
}
270
271
/* See documentation in header file. */
272
int ini_parse(const char* filename, ini_handler handler, void* user)
273
{
274
FILE* file;
275
int error;
276
277
file = fopen(filename, "r");
278
if (!file)
279
return -1;
280
error = ini_parse_file(file, handler, user);
281
fclose(file);
282
return error;
283
}
284
285
/* An ini_reader function to read the next line from a string buffer. This
286
is the fgets() equivalent used by ini_parse_string(). */
287
static char* ini_reader_string(char* str, int num, void* stream) {
288
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
289
const char* ctx_ptr = ctx->ptr;
290
size_t ctx_num_left = ctx->num_left;
291
char* strp = str;
292
char c;
293
294
if (ctx_num_left == 0 || num < 2)
295
return NULL;
296
297
while (num > 1 && ctx_num_left != 0) {
298
c = *ctx_ptr++;
299
ctx_num_left--;
300
*strp++ = c;
301
if (c == '\n')
302
break;
303
num--;
304
}
305
306
*strp = '\0';
307
ctx->ptr = ctx_ptr;
308
ctx->num_left = ctx_num_left;
309
return str;
310
}
311
312
/* See documentation in header file. */
313
int ini_parse_string(const char* string, ini_handler handler, void* user) {
314
return ini_parse_string_length(string, strlen(string), handler, user);
315
}
316
317
/* See documentation in header file. */
318
int ini_parse_string_length(const char* string, size_t length,
319
ini_handler handler, void* user) {
320
ini_parse_string_ctx ctx;
321
322
ctx.ptr = string;
323
ctx.num_left = length;
324
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
325
user);
326
}
327
328