1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
// Read an INI file into easy-to-access name/value pairs.
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (C) 2009-2025, Ben Hoyt
// 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
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include "../ini.h"
#include "INIReader.h"
using std::string;
INIReader::INIReader(const string& filename)
{
_error = ini_parse(filename.c_str(), ValueHandler, this);
}
INIReader::INIReader(const char *buffer, size_t buffer_size)
{
_error = ini_parse_string_length(buffer, buffer_size, ValueHandler, this);
}
int INIReader::ParseError() const
{
return _error;
}
string INIReader::ParseErrorMessage() const
{
// If _error is positive it means it is the line number on which a parse
// error occurred. This could be an overlong line, that ValueHandler
// indicated a user defined error, an unterminated section name, or a name
// without a value.
if (_error > 0) {
return "parse error on line " + std::to_string(_error) + "; missing ']' or '='?";
}
// If _error is negative it is a system type error, and 0 means success.
switch (_error) {
case -2:
return "unable to allocate memory";
case -1:
return "unable to open file";
case 0:
return "";
}
// This should never be reached. It probably means a new error code was
// added to the C API without updating this method.
return "unknown error " + std::to_string(_error);
}
string INIReader::Get(const string& section, const string& name, const string& default_value) const
{
string key = MakeKey(section, name);
// Use _values.find() here instead of _values.at() to support pre C++11 compilers
return _values.count(key) ? _values.find(key)->second : default_value;
}
string INIReader::GetString(const string& section, const string& name, const string& default_value) const
{
const string str = Get(section, name, "");
return str.empty() ? default_value : str;
}
long INIReader::GetInteger(const string& section, const string& name, long default_value) const
{
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;
}
INI_API int64_t INIReader::GetInteger64(const string& section, const string& name, int64_t default_value) const
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
// This parses "1234" (decimal) and also "0x4D2" (hex)
int64_t n = strtoll(value, &end, 0);
return end > value ? n : default_value;
}
unsigned long INIReader::GetUnsigned(const string& section, const string& name, unsigned long default_value) const
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
// This parses "1234" (decimal) and also "0x4D2" (hex)
unsigned long n = strtoul(value, &end, 0);
return end > value ? n : default_value;
}
INI_API uint64_t INIReader::GetUnsigned64(const string& section, const string& name, uint64_t default_value) const
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
// This parses "1234" (decimal) and also "0x4D2" (hex)
uint64_t n = strtoull(value, &end, 0);
return end > value ? n : default_value;
}
double INIReader::GetReal(const string& section, const string& name, double default_value) const
{
string valstr = Get(section, name, "");
const char* value = valstr.c_str();
char* end;
double n = strtod(value, &end);
return end > value ? n : default_value;
}
bool INIReader::GetBoolean(const string& section, const string& name, bool default_value) const
{
string valstr = Get(section, name, "");
// Convert to lower case to make string comparisons case-insensitive
std::transform(valstr.begin(), valstr.end(), valstr.begin(),
[](const unsigned char& ch) { return static_cast<unsigned char>(::tolower(ch)); });
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;
}
std::vector<string> INIReader::Sections() const
{
std::set<string> sectionSet;
for (std::map<string, string>::const_iterator it = _values.begin(); it != _values.end(); ++it) {
size_t pos = it->first.find('=');
if (pos != string::npos) {
sectionSet.insert(it->first.substr(0, pos));
}
}
return std::vector<string>(sectionSet.begin(), sectionSet.end());
}
std::vector<string> INIReader::Keys(const string& section) const
{
std::vector<string> keys;
string keyPrefix = MakeKey(section, "");
for (std::map<string, string>::const_iterator it = _values.begin(); it != _values.end(); ++it) {
if (it->first.compare(0, keyPrefix.length(), keyPrefix) == 0) {
keys.push_back(it->first.substr(keyPrefix.length()));
}
}
return keys;
}
bool INIReader::HasSection(const string& section) const
{
const string key = MakeKey(section, "");
std::map<string, string>::const_iterator pos = _values.lower_bound(key);
if (pos == _values.end())
return false;
// Does the key at the lower_bound pos start with "section"?
return pos->first.compare(0, key.length(), key) == 0;
}
bool INIReader::HasValue(const string& section, const string& name) const
{
string key = MakeKey(section, name);
return _values.count(key);
}
string INIReader::MakeKey(const string& section, const 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(),
[](const unsigned char& ch) { return static_cast<unsigned char>(::tolower(ch)); });
return key;
}
int INIReader::ValueHandler(void* user, const char* section, const char* name,
const char* value)
{
if (!name) // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled
return 1;
INIReader* reader = static_cast<INIReader*>(user);
string key = MakeKey(section, name);
if (reader->_values[key].size() > 0)
reader->_values[key] += "\n";
reader->_values[key] += value ? value : "";
return 1;
}
|