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