The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    ├── assets
    │   └── .condarc
    └── workflows
    │   └── ci.yml
├── .gitignore
├── .pre-commit-config.yaml
├── INIReader.h
├── INIReaderTest.cpp
├── LICENSE.txt
├── README.md
└── test.ini


/.github/assets/.condarc:
--------------------------------------------------------------------------------
1 | channels:
2 |   - conda-forge
3 | 


--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
 1 | name: CI
 2 | on: [push]
 3 | 
 4 | # Automatically stop old builds on the same branch/PR
 5 | concurrency:
 6 |   group: ${{ github.workflow }}-${{ github.ref }}
 7 |   cancel-in-progress: true
 8 | 
 9 | defaults:
10 |   run:
11 |     shell: bash -el {0}
12 | 
13 | jobs:
14 |   pre-commit-checks:
15 |     name: Pre-commit checks
16 |     timeout-minutes: 30
17 |     runs-on: ubuntu-latest
18 |     steps:
19 |       - name: Checkout branch
20 |         uses: actions/checkout@v4
21 |       - name: Run pre-commit-conda
22 |         uses: quantco/pre-commit-conda@v1
23 | 
24 |   tests:
25 |     name: "Tests"
26 |     timeout-minutes: 30
27 |     runs-on: ubuntu-latest
28 |     strategy:
29 |       fail-fast: false
30 |       matrix:
31 |         CXX_COMPILER: ["g++", "clang++"]
32 |     steps:
33 |       - name: Checkout branch
34 |         uses: actions/checkout@v4
35 |         with:
36 |           ref: ${{ github.head_ref }}
37 |           fetch-depth: 0
38 |       - name: Compile and run
39 |         run: |
40 |           ${{ matrix.CXX_COMPILER }} INIReaderTest.cpp -o INIReaderTest.out
41 |           ./INIReaderTest.out
42 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.out
2 | *.o
3 | 


--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
 1 | repos:
 2 |   - repo: https://github.com/Quantco/pre-commit-mirrors-prettier
 3 |     rev: 3.2.5
 4 |     hooks:
 5 |       - id: prettier-conda
 6 |         files: \.(md|yml|yaml)$
 7 |   - repo: https://github.com/Quantco/pre-commit-mirrors-pre-commit-hooks
 8 |     rev: 4.5.0
 9 |     hooks:
10 |       - id: trailing-whitespace-conda
11 |       - id: end-of-file-fixer-conda
12 |       - id: check-merge-conflict-conda
13 |         args: ["--assume-in-merge"]
14 |   - repo: https://github.com/Quantco/pre-commit-mirrors-typos
15 |     rev: 1.19.0
16 |     hooks:
17 |       - id: typos-conda
18 |   - repo: https://github.com/Quantco/pre-commit-mirrors-clang-format
19 |     rev: 16.0.6
20 |     hooks:
21 |       - id: clang-format-conda
22 | 


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


--------------------------------------------------------------------------------
/INIReaderTest.cpp:
--------------------------------------------------------------------------------
 1 | // Example that shows simple usage of the INIReader class
 2 | 
 3 | #include "INIReader.h"
 4 | #include <iostream>
 5 | #include <sstream>
 6 | 
 7 | std::string sections(INIReader &reader) {
 8 |   std::stringstream ss;
 9 |   std::set<std::string> sections = reader.Sections();
10 |   for (std::set<std::string>::iterator it = sections.begin();
11 |        it != sections.end(); ++it)
12 |     ss << *it << ",";
13 |   return ss.str();
14 | }
15 | 
16 | int main() {
17 |   INIReader reader("test.ini");
18 | 
19 |   if (reader.ParseError() < 0) {
20 |     std::cout << "Can't load 'test.ini'\n";
21 |     return 1;
22 |   }
23 |   std::cout << "Config loaded from 'test.ini': found sections="
24 |             << sections(reader)
25 |             << " version=" << reader.GetInteger("protocol", "version", -1)
26 |             << ", name=" << reader.Get("user", "name", "UNKNOWN")
27 |             << ", email=" << reader.Get("user", "email", "UNKNOWN")
28 |             << ", multi=" << reader.Get("user", "multi", "UNKNOWN")
29 |             << ", pi=" << reader.GetReal("user", "pi", -1)
30 |             << ", active=" << reader.GetBoolean("user", "active", true) << "\n";
31 |   return 0;
32 | }
33 | 


--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
 1 | 
 2 | The "inih" library is distributed under the New BSD license:
 3 | 
 4 | Copyright (c) 2009, Ben Hoyt
 5 | All rights reserved.
 6 | 
 7 | Redistribution and use in source and binary forms, with or without
 8 | modification, are permitted provided that the following conditions are met:
 9 |     * Redistributions of source code must retain the above copyright
10 |       notice, this list of conditions and the following disclaimer.
11 |     * Redistributions in binary form must reproduce the above copyright
12 |       notice, this list of conditions and the following disclaimer in the
13 |       documentation and/or other materials provided with the distribution.
14 |     * Neither the name of Ben Hoyt nor the names of its contributors
15 |       may be used to endorse or promote products derived from this software
16 |       without specific prior written permission.
17 | 
18 | THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # inih
 2 | 
 3 | [![CI](https://github.com/jtilly/inih/actions/workflows/ci.yml/badge.svg)](https://github.com/jtilly/inih/actions/workflows/ci.yml)
 4 | 
 5 | This is a header only C++ version of [inih](https://github.com/benhoyt/inih).
 6 | 
 7 | **inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries.
 8 | 
 9 | ## Usage
10 | 
11 | All you need to do is to include `INIReader.h`. Consider the following example (`INIReaderTest.cpp`):
12 | 
13 | ```cpp
14 | #include <iostream>
15 | #include "INIReader.h"
16 | 
17 | int main() {
18 | 
19 |     INIReader reader("test.ini");
20 | 
21 |     if (reader.ParseError() != 0) {
22 |         std::cout << "Can't load 'test.ini'\n";
23 |         return 1;
24 |     }
25 |     std::cout << "Config loaded from 'test.ini': version="
26 |               << reader.GetInteger("protocol", "version", -1) << ", name="
27 |               << reader.Get("user", "name", "UNKNOWN") << ", email="
28 |               << reader.Get("user", "email", "UNKNOWN") << ", pi="
29 |               << reader.GetReal("user", "pi", -1) << ", active="
30 |               << reader.GetBoolean("user", "active", true) << "\n";
31 |     return 0;
32 | 
33 | }
34 | ```
35 | 
36 | To compile and run:
37 | 
38 | ```sh
39 | g++ INIReaderTest.cpp -o INIReaderTest.out
40 | ./INIReaderTest.out
41 | # Config loaded from 'test.ini': version=6, name=Bob Smith, email=bob@smith.com, pi=3.14159, active=1
42 | ```
43 | 


--------------------------------------------------------------------------------
/test.ini:
--------------------------------------------------------------------------------
 1 | ; Test config file for ini_example.c and INIReaderTest.cpp
 2 | 
 3 | [protocol]             ; Protocol configuration
 4 | version=6              ; IPv6
 5 | 
 6 | [user]
 7 | name = Bob Smith       ; Spaces around '=' are stripped
 8 | email = bob@smith.com  ; And comments (like this) ignored
 9 | active = true          ; Test a boolean
10 | pi = 3.14159           ; Test a floating point number
11 | multi = this is a      ; test
12 |         multi-line value ; test
13 | 


--------------------------------------------------------------------------------