tunneldiggercpp

git clone git://archive.git.mtrnord.blog/MTRNord/tunneldiggercpp.git
Log | Files | Refs | README

INIReader.h (13094B)


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