liblcf
Loading...
Searching...
No Matches
ini.cpp
Go to the documentation of this file.
1/* inih -- simple .INI file parser
2
3The "inih" library is distributed under the New BSD license:
4
5Copyright (c) 2009, Ben Hoyt
6All rights reserved.
7
8Redistribution and use in source and binary forms, with or without
9modification, are permitted provided that the following conditions are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 * Neither the name of Ben Hoyt nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
18
19THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
20EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
23DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30Go to the project home page for more info: https://github.com/benhoyt/inih
31
32*/
33
34#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
35#define _CRT_SECURE_NO_WARNINGS
36#endif
37
38#include <cstdio>
39#include <cctype>
40#include <cstring>
41
42#include "lcf/ini.h"
43
44#if !INI_USE_STACK
45#include <cstdlib>
46#endif
47
48#define MAX_SECTION 50
49#define MAX_NAME 50
50
51/* Used by ini_parse_string() to keep track of string parsing state. */
52typedef struct {
53 const char* ptr;
54 size_t num_left;
56
57/* Strip whitespace chars off end of given string, in place. Return s. */
58static char* rstrip(char* s)
59{
60 char* p = s + strlen(s);
61 while (p > s && isspace((unsigned char)(*--p)))
62 *p = '\0';
63 return s;
64}
65
66/* Return pointer to first non-whitespace char in given string. */
67static char* lskip(const char* s)
68{
69 while (*s && isspace((unsigned char)(*s)))
70 s++;
71 return (char*)s;
72}
73
74/* Return pointer to first char (of chars) or inline comment in given string,
75 or pointer to null at end of string if neither found. Inline comment must
76 be prefixed by a whitespace character to register as a comment. */
77static char* find_chars_or_comment(const char* s, const char* chars)
78{
79#if INI_ALLOW_INLINE_COMMENTS
80 int was_space = 0;
81 while (*s && (!chars || !strchr(chars, *s)) &&
82 !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
83 was_space = isspace((unsigned char)(*s));
84 s++;
85 }
86#else
87 while (*s && (!chars || !strchr(chars, *s))) {
88 s++;
89 }
90#endif
91 return (char*)s;
92}
93
94/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
95static char* strncpy0(char* dest, const char* src, size_t size)
96{
97 strncpy(dest, src, size - 1);
98 dest[size - 1] = '\0';
99 return dest;
100}
101
102/* See documentation in header file. */
103int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
104 void* user)
105{
106 /* Uses a fair bit of stack (use heap instead if you need to) */
107#if INI_USE_STACK
108 char line[INI_MAX_LINE];
109 int max_line = INI_MAX_LINE;
110#else
111 char* line;
112 int max_line = INI_INITIAL_ALLOC;
113#endif
114#if INI_ALLOW_REALLOC && !INI_USE_STACK
115 char* new_line;
116 int offset;
117#endif
118 char section[MAX_SECTION] = "";
119 char prev_name[MAX_NAME] = "";
120
121 char* start;
122 char* end;
123 char* name;
124 char* value;
125 int lineno = 0;
126 int error = 0;
127
128#if !INI_USE_STACK
129 line = (char*)malloc(INI_INITIAL_ALLOC);
130 if (!line) {
131 return -2;
132 }
133#endif
134
135#if INI_HANDLER_LINENO
136#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
137#else
138#define HANDLER(u, s, n, v) handler(u, s, n, v)
139#endif
140
141 /* Scan through stream line by line */
142 while (reader(line, max_line, stream) != NULL) {
143#if INI_ALLOW_REALLOC && !INI_USE_STACK
144 offset = strlen(line);
145 while (offset == max_line - 1 && line[offset - 1] != '\n') {
146 max_line *= 2;
147 if (max_line > INI_MAX_LINE)
148 max_line = INI_MAX_LINE;
149 new_line = realloc(line, max_line);
150 if (!new_line) {
151 free(line);
152 return -2;
153 }
154 line = new_line;
155 if (reader(line + offset, max_line - offset, stream) == NULL)
156 break;
157 if (max_line >= INI_MAX_LINE)
158 break;
159 offset += strlen(line + offset);
160 }
161#endif
162
163 lineno++;
164
165 start = line;
166#if INI_ALLOW_BOM
167 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
168 (unsigned char)start[1] == 0xBB &&
169 (unsigned char)start[2] == 0xBF) {
170 start += 3;
171 }
172#endif
173 start = lskip(rstrip(start));
174
175 if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
176 /* Start-of-line comment */
177 }
178#if INI_ALLOW_MULTILINE
179 else if (*prev_name && *start && start > line) {
180 /* Non-blank line with leading whitespace, treat as continuation
181 of previous name's value (as per Python configparser). */
182 if (!HANDLER(user, section, prev_name, start) && !error)
183 error = lineno;
184 }
185#endif
186 else if (*start == '[') {
187 /* A "[section]" line */
188 end = find_chars_or_comment(start + 1, "]");
189 if (*end == ']') {
190 *end = '\0';
191 strncpy0(section, start + 1, sizeof(section));
192 *prev_name = '\0';
193 }
194 else if (!error) {
195 /* No ']' found on section line */
196 error = lineno;
197 }
198 }
199 else if (*start) {
200 /* Not a comment, must be a name[=:]value pair */
201 end = find_chars_or_comment(start, "=:");
202 if (*end == '=' || *end == ':') {
203 *end = '\0';
204 name = rstrip(start);
205 value = end + 1;
206#if INI_ALLOW_INLINE_COMMENTS
207 end = find_chars_or_comment(value, NULL);
208 if (*end)
209 *end = '\0';
210#endif
211 value = lskip(value);
212 rstrip(value);
213
214 /* Valid name[=:]value pair found, call handler */
215 strncpy0(prev_name, name, sizeof(prev_name));
216 if (!HANDLER(user, section, name, value) && !error)
217 error = lineno;
218 }
219 else if (!error) {
220 /* No '=' or ':' found on name[=:]value line */
221 error = lineno;
222 }
223 }
224
225#if INI_STOP_ON_FIRST_ERROR
226 if (error)
227 break;
228#endif
229 }
230
231#if !INI_USE_STACK
232 free(line);
233#endif
234
235 return error;
236}
237
238/* See documentation in header file. */
239int ini_parse_file(FILE* file, ini_handler handler, void* user)
240{
241 return ini_parse_stream((ini_reader)fgets, file, handler, user);
242}
243
244/* See documentation in header file. */
245int ini_parse(const char* filename, ini_handler handler, void* user)
246{
247 FILE* file;
248 int error;
249
250 file = fopen(filename, "r");
251 if (!file)
252 return -1;
253 error = ini_parse_file(file, handler, user);
254 fclose(file);
255 return error;
256}
257
258/* An ini_reader function to read the next line from a string buffer. This
259 is the fgets() equivalent used by ini_parse_string(). */
260static char* ini_reader_string(char* str, int num, void* stream) {
262 const char* ctx_ptr = ctx->ptr;
263 size_t ctx_num_left = ctx->num_left;
264 char* strp = str;
265 char c;
266
267 if (ctx_num_left == 0 || num < 2)
268 return NULL;
269
270 while (num > 1 && ctx_num_left != 0) {
271 c = *ctx_ptr++;
272 ctx_num_left--;
273 *strp++ = c;
274 if (c == '\n')
275 break;
276 num--;
277 }
278
279 *strp = '\0';
280 ctx->ptr = ctx_ptr;
281 ctx->num_left = ctx_num_left;
282 return str;
283}
284
285/* See documentation in header file. */
286int ini_parse_string(const char* string, ini_handler handler, void* user) {
288
289 ctx.ptr = string;
290 ctx.num_left = strlen(string);
291 return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
292 user);
293}
int ini_parse(const char *filename, ini_handler handler, void *user)
Definition: ini.cpp:245
static char * lskip(const char *s)
Definition: ini.cpp:67
#define HANDLER(u, s, n, v)
static char * find_chars_or_comment(const char *s, const char *chars)
Definition: ini.cpp:77
static char * ini_reader_string(char *str, int num, void *stream)
Definition: ini.cpp:260
static char * strncpy0(char *dest, const char *src, size_t size)
Definition: ini.cpp:95
int ini_parse_file(FILE *file, ini_handler handler, void *user)
Definition: ini.cpp:239
static char * rstrip(char *s)
Definition: ini.cpp:58
#define MAX_SECTION
Definition: ini.cpp:48
#define MAX_NAME
Definition: ini.cpp:49
int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user)
Definition: ini.cpp:103
int ini_parse_string(const char *string, ini_handler handler, void *user)
Definition: ini.cpp:286
size_t num_left
Definition: ini.cpp:54
const char * ptr
Definition: ini.cpp:53