Antares Xpansion
Investment simulations for Antares studies
Loading...
Searching...
No Matches
INIReader.h
1// Read an INI file into easy-to-access name/value pairs.
2
3// inih and INIReader are released under the New BSD license (see LICENSE.txt).
4// Go to the project home page for more info:
5//
6// https://github.com/benhoyt/inih
7/* inih -- simple .INI file parser
8
9inih is released under the New BSD license (see LICENSE.txt). Go to the project
10home page for more info:
11
12https://github.com/benhoyt/inih
13
14*/
15
16#ifndef __INI_H__
17#define __INI_H__
18
19/* Make this header file easier to include in C++ code */
20#ifdef __cplusplus
21extern "C"
22{
23#endif
24
25#include <stdio.h>
26
27/* Typedef for prototype of handler function. */
28typedef int (*ini_handler)(void* user, const char* section, const char* name, const char* value);
29
30/* Typedef for prototype of fgets-style reader function. */
31typedef char* (*ini_reader)(char* str, int num, void* stream);
32
33/* Parse given INI-style file. May have [section]s, name=value pairs
34 (whitespace stripped), and comments starting with ';' (semicolon). Section
35 is "" if name=value pair parsed before any section heading. name:value
36 pairs are also supported as a concession to Python's configparser.
37
38 For each name=value pair parsed, call handler function with given user
39 pointer as well as section, name, and value (data only valid for duration
40 of handler call). Handler should return nonzero on success, zero on error.
41
42 Returns 0 on success, line number of first error on parse error (doesn't
43 stop on first error), -1 on file open error, or -2 on memory allocation
44 error (only when INI_USE_STACK is zero).
45*/
46int ini_parse(const char* filename, ini_handler handler, void* user);
47
48/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
49 close the file when it's finished -- the caller must do that. */
50int ini_parse_file(FILE* file, ini_handler handler, void* user);
51
52/* Same as ini_parse(), but takes an ini_reader function pointer instead of
53 filename. Used for implementing custom or string-based I/O. */
54int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user);
55
56/* Nonzero to allow multi-line value parsing, in the style of Python's
57 configparser. If allowed, ini_parse() will call the handler with the same
58 name for each subsequent line parsed. */
59#ifndef INI_ALLOW_MULTILINE
60#define INI_ALLOW_MULTILINE 1
61#endif
62
63/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
64 the file. See http://code.google.com/p/inih/issues/detail?id=21 */
65#ifndef INI_ALLOW_BOM
66#define INI_ALLOW_BOM 1
67#endif
68
69/* Nonzero to allow inline comments (with valid inline comment characters
70 specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
71 Python 3.2+ configparser behaviour. */
72#ifndef INI_ALLOW_INLINE_COMMENTS
73#define INI_ALLOW_INLINE_COMMENTS 1
74#endif
75#ifndef INI_INLINE_COMMENT_PREFIXES
76#define INI_INLINE_COMMENT_PREFIXES ";"
77#endif
78
79/* Nonzero to use stack, zero to use heap (malloc/free). */
80#ifndef INI_USE_STACK
81#define INI_USE_STACK 1
82#endif
83
84/* Stop parsing on first error (default is to keep parsing). */
85#ifndef INI_STOP_ON_FIRST_ERROR
86#define INI_STOP_ON_FIRST_ERROR 0
87#endif
88
89/* Maximum line length for any line in INI file. */
90#ifndef INI_MAX_LINE
91#define INI_MAX_LINE 200
92#endif
93
94#ifdef __cplusplus
95}
96#endif
97
98/* inih -- simple .INI file parser
99
100inih is released under the New BSD license (see LICENSE.txt). Go to the project
101home page for more info:
102
103https://github.com/benhoyt/inih
104
105*/
106
107#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
108#define _CRT_SECURE_NO_WARNINGS
109#endif
110
111#include <ctype.h>
112#include <stdio.h>
113#include <string.h>
114
115#if !INI_USE_STACK
116#include <stdlib.h>
117#endif
118
119#define MAX_SECTION 50
120#define MAX_NAME 50
121
122/* Strip whitespace chars off end of given string, in place. Return s. */
123inline static char* rstrip(char* s)
124{
125 char* p = s + strlen(s);
126 while (p > s && isspace((unsigned char)(*--p)))
127 {
128 *p = '\0';
129 }
130 return s;
131}
132
133/* Return pointer to first non-whitespace char in given string. */
134inline static char* lskip(const char* s)
135{
136 while (*s && isspace((unsigned char)(*s)))
137 {
138 s++;
139 }
140 return (char*)s;
141}
142
143/* Return pointer to first char (of chars) or inline comment in given string,
144 or pointer to null at end of string if neither found. Inline comment must
145 be prefixed by a whitespace character to register as a comment. */
146inline static char* find_chars_or_comment(const char* s, const char* chars)
147{
148#if INI_ALLOW_INLINE_COMMENTS
149 int was_space = 0;
150 while (*s && (!chars || !strchr(chars, *s))
151 && !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s)))
152 {
153 was_space = isspace((unsigned char)(*s));
154 s++;
155 }
156#else
157 while (*s && (!chars || !strchr(chars, *s)))
158 {
159 s++;
160 }
161#endif
162 return (char*)s;
163}
164
165/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
166inline static char* strncpy0(char* dest, const char* src, size_t size)
167{
168 strncpy(dest, src, size);
169 dest[size - 1] = '\0';
170 return dest;
171}
172
173/* See documentation in header file. */
174inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user)
175{
176 /* Uses a fair bit of stack (use heap instead if you need to) */
177#if INI_USE_STACK
178 char line[INI_MAX_LINE];
179#else
180 char* line;
181#endif
182 char section[MAX_SECTION] = "";
183 char prev_name[MAX_NAME] = "";
184
185 char* start;
186 char* end;
187 char* name;
188 char* value;
189 int lineno = 0;
190 int error = 0;
191
192#if !INI_USE_STACK
193 line = (char*)malloc(INI_MAX_LINE);
194 if (!line)
195 {
196 return -2;
197 }
198#endif
199
200 /* Scan through stream line by line */
201 while (reader(line, INI_MAX_LINE, stream) != NULL)
202 {
203 lineno++;
204
205 start = line;
206#if INI_ALLOW_BOM
207 if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB
208 && (unsigned char)start[2] == 0xBF)
209 {
210 start += 3;
211 }
212#endif
213 start = lskip(rstrip(start));
214
215 if (*start == ';' || *start == '#')
216 {
217 /* Per Python configparser, allow both ; and # comments at the
218 start of a line */
219 }
220#if INI_ALLOW_MULTILINE
221 else if (*prev_name && *start && start > line)
222 {
223#if INI_ALLOW_INLINE_COMMENTS
224 end = find_chars_or_comment(start, NULL);
225 if (*end)
226 {
227 *end = '\0';
228 }
229 rstrip(start);
230#endif
231
232 /* Non-blank line with leading whitespace, treat as continuation
233 of previous name's value (as per Python configparser). */
234 if (!handler(user, section, prev_name, start) && !error)
235 {
236 error = lineno;
237 }
238 }
239#endif
240 else if (*start == '[')
241 {
242 /* A "[section]" line */
243 end = find_chars_or_comment(start + 1, "]");
244 if (*end == ']')
245 {
246 *end = '\0';
247 strncpy0(section, start + 1, sizeof(section));
248 *prev_name = '\0';
249 }
250 else if (!error)
251 {
252 /* No ']' found on section line */
253 error = lineno;
254 }
255 }
256 else if (*start)
257 {
258 /* Not a comment, must be a name[=:]value pair */
259 end = find_chars_or_comment(start, "=:");
260 if (*end == '=' || *end == ':')
261 {
262 *end = '\0';
263 name = rstrip(start);
264 value = lskip(end + 1);
265#if INI_ALLOW_INLINE_COMMENTS
266 end = find_chars_or_comment(value, NULL);
267 if (*end)
268 {
269 *end = '\0';
270 }
271#endif
272 rstrip(value);
273
274 /* Valid name[=:]value pair found, call handler */
275 strncpy0(prev_name, name, sizeof(prev_name));
276 if (!handler(user, section, name, value) && !error)
277 {
278 error = lineno;
279 }
280 }
281 else if (!error)
282 {
283 /* No '=' or ':' found on name[=:]value line */
284 error = lineno;
285 }
286 }
287
288#if INI_STOP_ON_FIRST_ERROR
289 if (error)
290 {
291 break;
292 }
293#endif
294 }
295
296#if !INI_USE_STACK
297 free(line);
298#endif
299
300 return error;
301}
302
303/* See documentation in header file. */
304inline int ini_parse_file(FILE* file, ini_handler handler, void* user)
305{
306 return ini_parse_stream((ini_reader)fgets, file, handler, user);
307}
308
309/* See documentation in header file. */
310inline int ini_parse(const char* filename, ini_handler handler, void* user)
311{
312 FILE* file;
313 int error;
314
315 file = fopen(filename, "r");
316 if (!file)
317 {
318 return -1;
319 }
320 error = ini_parse_file(file, handler, user);
321 fclose(file);
322 return error;
323}
324
325#endif /* __INI_H__ */
326
327#ifndef __INIREADER_H__
328#define __INIREADER_H__
329
330#include <map>
331#include <set>
332#include <string>
333
334// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
335// for simplicity here rather than speed, but it should be pretty decent.)
337{
338public:
339 // Empty Constructor
340 INIReader(){};
341
342 // Construct INIReader and parse given filename. See ini.h for more info
343 // about the parsing.
344 INIReader(std::string filename);
345
346 // Construct INIReader and parse given file. See ini.h for more info
347 // about the parsing.
348 INIReader(FILE* file);
349
350 // Return the result of ini_parse(), i.e., 0 on success, line number of
351 // first error on parse error, or -1 on file open error.
352 int ParseError() const;
353
354 // Return the list of sections found in ini file
355 const std::set<std::string>& Sections() const;
356
357 // Get a string value from INI file, returning default_value if not found.
358 std::string Get(std::string section, std::string name, std::string default_value) const;
359
360 // Get an integer (long) value from INI file, returning default_value if
361 // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
362 long GetInteger(std::string section, std::string name, long default_value) const;
363
364 // Get a real (floating point double) value from INI file, returning
365 // default_value if not found or not a valid floating point value
366 // according to strtod().
367 double GetReal(std::string section, std::string name, double default_value) const;
368
369 // Get a boolean value from INI file, returning default_value if not found or
370 // if not a valid true/false value. Valid true values are "true", "yes", "on",
371 // "1", and valid false values are "false", "no", "off", "0" (not case
372 // sensitive).
373 bool GetBoolean(std::string section, std::string name, bool default_value) const;
374
375protected:
376 int _error;
377 std::map<std::string, std::string> _values;
378 std::set<std::string> _sections;
379 static std::string MakeKey(std::string section, std::string name);
380 static int ValueHandler(void* user, const char* section, const char* name, const char* value);
381};
382
383#endif // __INIREADER_H__
384
385#ifndef __INIREADER__
386#define __INIREADER__
387
388#include <algorithm>
389#include <cctype>
390#include <cstdlib>
391
392inline INIReader::INIReader(std::string filename)
393{
394 _error = ini_parse(filename.c_str(), ValueHandler, this);
395}
396
397inline INIReader::INIReader(FILE* file)
398{
399 _error = ini_parse_file(file, ValueHandler, this);
400}
401
402inline int INIReader::ParseError() const
403{
404 return _error;
405}
406
407inline const std::set<std::string>& INIReader::Sections() const
408{
409 return _sections;
410}
411
412inline std::string INIReader::Get(std::string section,
413 std::string name,
414 std::string default_value) const
415{
416 std::string key = MakeKey(section, name);
417 return _values.count(key) ? _values.at(key) : default_value;
418}
419
420inline long INIReader::GetInteger(std::string section, std::string name, long default_value) const
421{
422 std::string valstr = Get(section, name, "");
423 const char* value = valstr.c_str();
424 char* end;
425 // This parses "1234" (decimal) and also "0x4D2" (hex)
426 long n = strtol(value, &end, 0);
427 return end > value ? n : default_value;
428}
429
430inline double INIReader::GetReal(std::string section, std::string name, double default_value) const
431{
432 std::string valstr = Get(section, name, "");
433 const char* value = valstr.c_str();
434 char* end;
435 double n = strtod(value, &end);
436 return end > value ? n : default_value;
437}
438
439inline bool INIReader::GetBoolean(std::string section, std::string name, bool default_value) const
440{
441 std::string valstr = Get(section, name, "");
442 // Convert to lower case to make string comparisons case-insensitive
443 std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
444 if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
445 {
446 return true;
447 }
448 else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
449 {
450 return false;
451 }
452 else
453 {
454 return default_value;
455 }
456}
457
458inline std::string INIReader::MakeKey(std::string section, std::string name)
459{
460 std::string key = section + "=" + name;
461 // Convert to lower case to make section/name lookups case-insensitive
462 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
463 return key;
464}
465
466inline int INIReader::ValueHandler(void* user,
467 const char* section,
468 const char* name,
469 const char* value)
470{
471 INIReader* reader = (INIReader*)user;
472 std::string key = MakeKey(section, name);
473 if (reader->_values[key].size() > 0)
474 {
475 reader->_values[key] += "\n";
476 }
477 reader->_values[key] += value;
478 reader->_sections.insert(section);
479 return 1;
480}
481
482#endif // __INIREADER__
Definition INIReader.h:337