| // Read an INI file into easy-to-access name/value pairs. |
| |
| // inih and INIReader are released under the New BSD license (see LICENSE.txt). |
| // Go to the project home page for more info: |
| // |
| // https://github.com/benhoyt/inih |
| /* inih -- simple .INI file parser |
| |
| inih is released under the New BSD license (see LICENSE.txt). Go to the project |
| home page for more info: |
| |
| https://github.com/benhoyt/inih |
| |
| */ |
| |
| #ifndef __INI_H__ |
| #define __INI_H__ |
| |
| /* Make this header file easier to include in C++ code */ |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #include <stdio.h> |
| |
| /* Typedef for prototype of handler function. */ |
| typedef int (*ini_handler)(void* user, const char* section, |
| const char* name, const char* value); |
| |
| /* Typedef for prototype of fgets-style reader function. */ |
| typedef char* (*ini_reader)(char* str, int num, void* stream); |
| |
| /* Parse given INI-style file. May have [section]s, name=value pairs |
| (whitespace stripped), and comments starting with ';' (semicolon). Section |
| is "" if name=value pair parsed before any section heading. name:value |
| pairs are also supported as a concession to Python's configparser. |
| |
| For each name=value pair parsed, call handler function with given user |
| pointer as well as section, name, and value (data only valid for duration |
| of handler call). Handler should return nonzero on success, zero on error. |
| |
| Returns 0 on success, line number of first error on parse error (doesn't |
| stop on first error), -1 on file open error, or -2 on memory allocation |
| error (only when INI_USE_STACK is zero). |
| */ |
| int ini_parse(const char* filename, ini_handler handler, void* user); |
| |
| /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't |
| close the file when it's finished -- the caller must do that. */ |
| int ini_parse_file(FILE* file, ini_handler handler, void* user); |
| |
| /* Same as ini_parse(), but takes an ini_reader function pointer instead of |
| filename. Used for implementing custom or string-based I/O. */ |
| int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, |
| void* user); |
| |
| /* Nonzero to allow multi-line value parsing, in the style of Python's |
| configparser. If allowed, ini_parse() will call the handler with the same |
| name for each subsequent line parsed. */ |
| #ifndef INI_ALLOW_MULTILINE |
| #define INI_ALLOW_MULTILINE 1 |
| #endif |
| |
| /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of |
| the file. See http://code.google.com/p/inih/issues/detail?id=21 */ |
| #ifndef INI_ALLOW_BOM |
| #define INI_ALLOW_BOM 1 |
| #endif |
| |
| /* Nonzero to allow inline comments (with valid inline comment characters |
| specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match |
| Python 3.2+ configparser behaviour. */ |
| #ifndef INI_ALLOW_INLINE_COMMENTS |
| #define INI_ALLOW_INLINE_COMMENTS 1 |
| #endif |
| #ifndef INI_INLINE_COMMENT_PREFIXES |
| #define INI_INLINE_COMMENT_PREFIXES ";" |
| #endif |
| |
| /* Nonzero to use stack, zero to use heap (malloc/free). */ |
| #ifndef INI_USE_STACK |
| #define INI_USE_STACK 1 |
| #endif |
| |
| /* Stop parsing on first error (default is to keep parsing). */ |
| #ifndef INI_STOP_ON_FIRST_ERROR |
| #define INI_STOP_ON_FIRST_ERROR 0 |
| #endif |
| |
| /* Maximum line length for any line in INI file. */ |
| #ifndef INI_MAX_LINE |
| #define INI_MAX_LINE 200 |
| #endif |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /* inih -- simple .INI file parser |
| |
| inih is released under the New BSD license (see LICENSE.txt). Go to the project |
| home page for more info: |
| |
| https://github.com/benhoyt/inih |
| |
| */ |
| |
| #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) |
| #define _CRT_SECURE_NO_WARNINGS |
| #endif |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| |
| #if !INI_USE_STACK |
| #include <stdlib.h> |
| #endif |
| |
| #define MAX_SECTION 50 |
| #define MAX_NAME 50 |
| |
| /* Strip whitespace chars off end of given string, in place. Return s. */ |
| inline static char* rstrip(char* s) |
| { |
| char* p = s + strlen(s); |
| while (p > s && isspace((unsigned char)(*--p))) |
| *p = '\0'; |
| return s; |
| } |
| |
| /* Return pointer to first non-whitespace char in given string. */ |
| inline static char* lskip(const char* s) |
| { |
| while (*s && isspace((unsigned char)(*s))) |
| s++; |
| return (char*)s; |
| } |
| |
| /* Return pointer to first char (of chars) or inline comment in given string, |
| or pointer to null at end of string if neither found. Inline comment must |
| be prefixed by a whitespace character to register as a comment. */ |
| inline static char* find_chars_or_comment(const char* s, const char* chars) |
| { |
| #if INI_ALLOW_INLINE_COMMENTS |
| int was_space = 0; |
| while (*s && (!chars || !strchr(chars, *s)) && |
| !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { |
| was_space = isspace((unsigned char)(*s)); |
| s++; |
| } |
| #else |
| while (*s && (!chars || !strchr(chars, *s))) { |
| s++; |
| } |
| #endif |
| return (char*)s; |
| } |
| |
| /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ |
| inline static char* strncpy0(char* dest, const char* src, size_t size) |
| { |
| strncpy(dest, src, size); |
| dest[size - 1] = '\0'; |
| return dest; |
| } |
| |
| /* See documentation in header file. */ |
| inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, |
| void* user) |
| { |
| /* Uses a fair bit of stack (use heap instead if you need to) */ |
| #if INI_USE_STACK |
| char line[INI_MAX_LINE]; |
| #else |
| char* line; |
| #endif |
| char section[MAX_SECTION] = ""; |
| char prev_name[MAX_NAME] = ""; |
| |
| char* start; |
| char* end; |
| char* name; |
| char* value; |
| int lineno = 0; |
| int error = 0; |
| |
| #if !INI_USE_STACK |
| line = (char*)malloc(INI_MAX_LINE); |
| if (!line) { |
| return -2; |
| } |
| #endif |
| |
| /* Scan through stream line by line */ |
| while (reader(line, INI_MAX_LINE, stream) != NULL) { |
| lineno++; |
| |
| start = line; |
| #if INI_ALLOW_BOM |
| if (lineno == 1 && (unsigned char)start[0] == 0xEF && |
| (unsigned char)start[1] == 0xBB && |
| (unsigned char)start[2] == 0xBF) { |
| start += 3; |
| } |
| #endif |
| start = lskip(rstrip(start)); |
| |
| if (*start == ';' || *start == '#') { |
| /* Per Python configparser, allow both ; and # comments at the |
| start of a line */ |
| } |
| #if INI_ALLOW_MULTILINE |
| else if (*prev_name && *start && start > line) { |
| /* Non-blank line with leading whitespace, treat as continuation |
| of previous name's value (as per Python configparser). */ |
| if (!handler(user, section, prev_name, start) && !error) |
| error = lineno; |
| } |
| #endif |
| else if (*start == '[') { |
| /* A "[section]" line */ |
| end = find_chars_or_comment(start + 1, "]"); |
| if (*end == ']') { |
| *end = '\0'; |
| strncpy0(section, start + 1, sizeof(section)); |
| *prev_name = '\0'; |
| } |
| else if (!error) { |
| /* No ']' found on section line */ |
| error = lineno; |
| } |
| } |
| else if (*start) { |
| /* Not a comment, must be a name[=:]value pair */ |
| end = find_chars_or_comment(start, "=:"); |
| if (*end == '=' || *end == ':') { |
| *end = '\0'; |
| name = rstrip(start); |
| value = lskip(end + 1); |
| #if INI_ALLOW_INLINE_COMMENTS |
| end = find_chars_or_comment(value, NULL); |
| if (*end) |
| *end = '\0'; |
| #endif |
| rstrip(value); |
| |
| /* Valid name[=:]value pair found, call handler */ |
| strncpy0(prev_name, name, sizeof(prev_name)); |
| if (!handler(user, section, name, value) && !error) |
| error = lineno; |
| } |
| else if (!error) { |
| /* No '=' or ':' found on name[=:]value line */ |
| error = lineno; |
| } |
| } |
| |
| #if INI_STOP_ON_FIRST_ERROR |
| if (error) |
| break; |
| #endif |
| } |
| |
| #if !INI_USE_STACK |
| free(line); |
| #endif |
| |
| return error; |
| } |
| |
| /* See documentation in header file. */ |
| inline int ini_parse_file(FILE* file, ini_handler handler, void* user) |
| { |
| return ini_parse_stream((ini_reader)fgets, file, handler, user); |
| } |
| |
| /* See documentation in header file. */ |
| inline int ini_parse(const char* filename, ini_handler handler, void* user) |
| { |
| FILE* file; |
| int error; |
| |
| file = fopen(filename, "r"); |
| if (!file) |
| return -1; |
| error = ini_parse_file(file, handler, user); |
| fclose(file); |
| return error; |
| } |
| |
| #endif /* __INI_H__ */ |
| |
| |
| #ifndef __INIREADER_H__ |
| #define __INIREADER_H__ |
| |
| #include <map> |
| #include <string> |
| |
| // Read an INI file into easy-to-access name/value pairs. (Note that I've gone |
| // for simplicity here rather than speed, but it should be pretty decent.) |
| class INIReader |
| { |
| public: |
| // Construct INIReader and parse given filename. See ini.h for more info |
| // about the parsing. |
| INIReader(std::string filename); |
| |
| // Return the result of ini_parse(), i.e., 0 on success, line number of |
| // first error on parse error, or -1 on file open error. |
| int ParseError(); |
| |
| // Get a string value from INI file, returning default_value if not found. |
| std::string Get(std::string section, std::string name, |
| std::string default_value); |
| |
| // Get an integer (long) value from INI file, returning default_value if |
| // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). |
| long GetInteger(std::string section, std::string name, long default_value); |
| |
| // Get a real (floating point double) value from INI file, returning |
| // default_value if not found or not a valid floating point value |
| // according to strtod(). |
| double GetReal(std::string section, std::string name, double default_value); |
| |
| // Get a boolean value from INI file, returning default_value if not found or if |
| // not a valid true/false value. Valid true values are "true", "yes", "on", "1", |
| // and valid false values are "false", "no", "off", "0" (not case sensitive). |
| bool GetBoolean(std::string section, std::string name, bool default_value); |
| |
| private: |
| int _error; |
| std::map<std::string, std::string> _values; |
| static std::string MakeKey(std::string section, std::string name); |
| static int ValueHandler(void* user, const char* section, const char* name, |
| const char* value); |
| }; |
| |
| #endif // __INIREADER_H__ |
| |
| |
| #ifndef __INIREADER__ |
| #define __INIREADER__ |
| |
| #include <algorithm> |
| #include <cctype> |
| #include <cstdlib> |
| |
| using std::string; |
| |
| inline INIReader::INIReader(string filename) |
| { |
| _error = ini_parse(filename.c_str(), ValueHandler, this); |
| } |
| |
| inline int INIReader::ParseError() |
| { |
| return _error; |
| } |
| |
| inline string INIReader::Get(string section, string name, string default_value) |
| { |
| string key = MakeKey(section, name); |
| return _values.count(key) ? _values[key] : default_value; |
| } |
| |
| inline long INIReader::GetInteger(string section, string name, long default_value) |
| { |
| string valstr = Get(section, name, ""); |
| const char* value = valstr.c_str(); |
| char* end; |
| // This parses "1234" (decimal) and also "0x4D2" (hex) |
| long n = strtol(value, &end, 0); |
| return end > value ? n : default_value; |
| } |
| |
| inline double INIReader::GetReal(string section, string name, double default_value) |
| { |
| string valstr = Get(section, name, ""); |
| const char* value = valstr.c_str(); |
| char* end; |
| double n = strtod(value, &end); |
| return end > value ? n : default_value; |
| } |
| |
| inline bool INIReader::GetBoolean(string section, string name, bool default_value) |
| { |
| string valstr = Get(section, name, ""); |
| // Convert to lower case to make string comparisons case-insensitive |
| std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); |
| if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") |
| return true; |
| else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") |
| return false; |
| else |
| return default_value; |
| } |
| |
| inline string INIReader::MakeKey(string section, string name) |
| { |
| string key = section + "=" + name; |
| // Convert to lower case to make section/name lookups case-insensitive |
| std::transform(key.begin(), key.end(), key.begin(), ::tolower); |
| return key; |
| } |
| |
| inline int INIReader::ValueHandler(void* user, const char* section, const char* name, |
| const char* value) |
| { |
| INIReader* reader = (INIReader*)user; |
| string key = MakeKey(section, name); |
| if (reader->_values[key].size() > 0) |
| reader->_values[key] += "\n"; |
| reader->_values[key] += value; |
| return 1; |
| } |
| |
| #endif // __INIREADER__ |