liblcf
Loading...
Searching...
No Matches
inireader.cpp
Go to the documentation of this file.
1// Read an INI file into easy-to-access name/value pairs.
2
3// inih and INIReader are released under the New BSD license:
4//
5// Copyright (c) 2009, Ben Hoyt
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are met:
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above copyright
13// notice, this list of conditions and the following disclaimer in the
14// documentation and/or other materials provided with the distribution.
15// * Neither the name of Ben Hoyt nor the names of its contributors
16// may be used to endorse or promote products derived from this software
17// without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22// DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY
23// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30// Go to the project home page for more info: https://github.com/benhoyt/inih
31
32#include <algorithm>
33#include <cctype>
34#include <cstdlib>
35#include <cstring>
36#include <istream>
37#include <charconv>
38#include <ini.h>
39#include "lcf/inireader.h"
40
41namespace lcf {
42
43INIReader::INIReader(std::string filename)
44{
45 _error = ini_parse(filename.c_str(), ValueHandler, this);
46}
47
48INIReader::INIReader(std::istream& filestream)
49{
50 _error = ini_parse_stream([](char* str, int num, void* stream) {
51 std::istream* is = reinterpret_cast<std::istream*>(stream);
52 if (num > 0) {
53 // via https://stackoverflow.com/questions/6089231/
54 std::string out;
55
56 std::istream::sentry se(*is, true);
57 std::streambuf* sb = is->rdbuf();
58
59 bool loop = true;
60
61 do {
62 int c = sb->sbumpc();
63 switch (c) {
64 case '\n':
65 loop = false;
66 break;
67 case '\r':
68 if (sb->sgetc() == '\n') {
69 sb->sbumpc();
70 }
71 loop = false;
72 break;
73 case EOF:
74 // Also handle the case when the last line has no line ending
75 if (out.empty()) {
76 is->setstate(std::ios::eofbit);
77 }
78 loop = false;
79 break;
80 default:
81 out += (char)c;
82 }
83 } while (loop);
84
85 if (out.empty() && (is->fail() || is->eof())) {
86 return (char*)nullptr;
87 }
88
89 strncpy(str, out.c_str(), num);
90 str[num - 1] = '\0';
91
92 return str;
93 }
94
95 return (char*)nullptr;
96 }, &filestream, ValueHandler, this);
97}
98
99int INIReader::ParseError() const
100{
101 return _error;
102}
103
104std::string_view INIReader::Get(std::string_view section, std::string_view name, std::string_view default_value) const
105{
106 std::string key = MakeKey(section, name);
107
108 auto it = _values.find(key);
109 if (it == _values.end()) {
110 return default_value;
111 }
112 return it->second;
113}
114
115std::string_view INIReader::GetString(std::string_view section, std::string_view name, std::string_view default_value) const
116{
117 auto str = Get(section, name, "");
118 return str.empty() ? default_value : str;
119}
120
121long INIReader::GetInteger(std::string_view section, std::string_view name, long default_value) const
122{
123 std::string_view valstr = Get(section, name, "");
124 long n;
125 auto ec = std::from_chars(valstr.data(), valstr.data() + valstr.size(), n).ec;
126 return ec == std::errc() ? n : default_value;
127}
128
129double INIReader::GetReal(std::string_view section, std::string_view name, double default_value) const
130{
131 std::string valstr = std::string(Get(section, name, ""));
132 const char* value = valstr.c_str();
133 char* end;
134 double n = strtod(value, &end);
135 return end > value ? n : default_value;
136/*
137 // FIXME: std::from_chars<double> not supported by clang and old g++ versions
138 std::string_view valstr = Get(section, name, "");
139 double n;
140 auto ec = std::from_chars(valstr.data(), valstr.data() + valstr.size(), n).ec;
141 return ec == std::errc() ? n : default_value;
142*/
143}
144
145bool INIReader::GetBoolean(std::string_view section, std::string_view name, bool default_value) const
146{
147 auto valstr = std::string(Get(section, name, ""));
148 // Convert to lower case to make string comparisons case-insensitive
149 std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
150 if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
151 return true;
152 else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
153 return false;
154 else
155 return default_value;
156}
157
158bool INIReader::HasValue(std::string_view section, std::string_view name) const
159{
160 std::string key = MakeKey(section, name);
161 return _values.find(key) != _values.end();
162}
163
164std::string INIReader::MakeKey(std::string_view section, std::string_view name)
165{
166 std::string key = std::string(section) + "=" + std::string(name);
167 // Convert to lower case to make section/name lookups case-insensitive
168 std::transform(key.begin(), key.end(), key.begin(), ::tolower);
169 return key;
170}
171
172int INIReader::ValueHandler(void* user, const char* section, const char* name,
173 const char* value)
174{
175 INIReader* reader = static_cast<INIReader*>(user);
176 std::string key = MakeKey(section, name);
177
178 auto [it, inserted] = reader->_values.try_emplace(key, value);
179
180 if (!inserted) {
181 // Key is duplicated
182 it->second += "\n";
183 it->second += value;
184 }
185
186 return 1;
187}
188
189} //namespace lcf