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__