24 #include <type_traits>
25 #include <unordered_map>
31 typedef int (*ini_handler)(
void* user,
const char* section,
const char* name,
35 typedef char* (*ini_reader)(
char* str,
int num,
void* stream);
37 #define INI_STOP_ON_FIRST_ERROR 1
38 #define INI_MAX_LINE 2000
39 #define INI_INITIAL_ALLOC 200
40 #define MAX_SECTION 50
42 #define INI_START_COMMENT_PREFIXES ";#"
43 #define INI_INLINE_COMMENT_PREFIXES ";"
46 inline static char* rstrip(
char* s) {
47 char* p = s + strlen(s);
48 while (p > s && isspace((
unsigned char)(*--p))) *p =
'\0';
53 inline static char* lskip(
const char* s) {
54 while (*s && isspace((
unsigned char)(*s))) s++;
61 inline static char* find_chars_or_comment(
const char* s,
const char* chars) {
63 while (*s && (!chars || !strchr(chars, *s)) &&
64 !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
65 was_space = isspace((
unsigned char)(*s));
72 inline static char* strncpy0(
char* dest,
const char* src,
size_t size) {
73 strncpy(dest, src, size - 1);
74 dest[size - 1] =
'\0';
79 inline int ini_parse_stream(ini_reader reader,
void* stream,
80 ini_handler handler,
void* user) {
83 size_t max_line = INI_INITIAL_ALLOC;
86 char section[MAX_SECTION] =
"";
87 char prev_name[MAX_NAME] =
"";
96 line = (
char*)malloc(INI_INITIAL_ALLOC);
101 #if INI_HANDLER_LINENO
102 #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
104 #define HANDLER(u, s, n, v) handler(u, s, n, v)
108 while (reader(line, (
int)max_line, stream) != NULL) {
109 offset = strlen(line);
110 while (offset == max_line - 1 && line[offset - 1] !=
'\n') {
112 if (max_line > INI_MAX_LINE) max_line = INI_MAX_LINE;
113 new_line = (
char*)realloc(line, max_line);
119 if (reader(line + offset, (
int)(max_line - offset), stream) == NULL)
121 if (max_line >= INI_MAX_LINE)
break;
122 offset += strlen(line + offset);
128 if (lineno == 1 && (
unsigned char)start[0] == 0xEF &&
129 (
unsigned char)start[1] == 0xBB &&
130 (
unsigned char)start[2] == 0xBF) {
133 start = lskip(rstrip(start));
135 if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
137 }
else if (*start ==
'[') {
139 end = find_chars_or_comment(start + 1,
"]");
142 strncpy0(section, start + 1,
sizeof(section));
150 end = find_chars_or_comment(start,
"=:");
151 if (*end ==
'=' || *end ==
':') {
153 name = rstrip(start);
155 end = find_chars_or_comment(value, NULL);
156 if (*end) *end =
'\0';
157 value = lskip(value);
161 strncpy0(prev_name, name,
sizeof(prev_name));
162 if (!HANDLER(user, section, name, value) && !error)
178 inline int ini_parse_file(FILE* file, ini_handler handler,
void* user) {
179 return ini_parse_stream((ini_reader)fgets, file, handler, user);
182 inline int ini_parse(
const char* filename, ini_handler handler,
void* user) {
186 file = fopen(filename,
"r");
187 if (!file)
return -1;
188 error = ini_parse_file(file, handler, user);
195 #ifndef __INIREADER_H__
196 #define __INIREADER_H__
215 int ParseError()
const;
218 const std::set<std::string>
Sections()
const;
221 const std::set<std::string>
Keys(std::string section)
const;
223 const std::unordered_map<std::string, std::string>
Get(
224 std::string section)
const;
226 template <
typename T = std::
string>
227 T
Get(
const std::string& section,
const std::string& name)
const;
229 template <
typename T>
230 T
Get(
const std::string& section,
const std::string& name,
231 T&& default_v)
const;
233 template <
typename T = std::
string>
234 std::vector<T>
GetVector(
const std::string& section,
235 const std::string& name)
const;
237 template <
typename T>
238 std::vector<T>
GetVector(
const std::string& section,
239 const std::string& name,
240 const std::vector<T>& default_v)
const;
242 template <
typename T = std::
string>
243 void InsertEntry(
const std::string& section,
const std::string& name,
246 template <
typename T = std::
string>
247 void InsertEntry(
const std::string& section,
const std::string& name,
248 const std::vector<T>& vs);
250 template <
typename T = std::
string>
251 void UpdateEntry(
const std::string& section,
const std::string& name,
254 template <
typename T = std::
string>
255 void UpdateEntry(
const std::string& section,
const std::string& name,
256 const std::vector<T>& vs);
260 std::unordered_map<std::string,
261 std::unordered_map<std::string, std::string>>
263 static int ValueHandler(
void* user,
const char* section,
const char* name,
266 template <
typename T>
267 T Converter(
const std::string& s)
const;
269 const bool BoolConverter(std::string s)
const;
271 template <
typename T>
272 std::string V2String(
const T& v)
const;
274 template <
typename T>
275 std::string Vec2String(
const std::vector<T>& v)
const;
280 #ifndef __INIREADER__
281 #define __INIREADER__
288 inline INIReader::INIReader(std::string filename) {
289 _error = ini_parse(filename.c_str(), ValueHandler,
this);
298 inline INIReader::INIReader(FILE* file) {
299 _error = ini_parse_file(file, ValueHandler,
this);
303 inline int INIReader::ParseError()
const {
308 throw std::runtime_error(
"ini file not found.");
310 throw std::runtime_error(
"memory alloc error");
312 throw std::runtime_error(
"parse error on line no: " +
313 std::to_string(_error));
323 std::set<std::string> retval;
324 for (
auto const& element : _values) {
325 retval.insert(element.first);
336 auto const _section =
Get(section);
337 std::set<std::string> retval;
338 for (
auto const& element : _section) {
339 retval.insert(element.first);
351 std::string section)
const {
352 auto const _section = _values.find(section);
353 if (_section == _values.end()) {
354 throw std::runtime_error(
"section '" + section +
"' not found.");
356 return _section->second;
365 template <
typename T>
367 const std::string& name)
const {
368 auto const _section =
Get(section);
369 auto const _value = _section.find(name);
371 if (_value == _section.end()) {
372 throw std::runtime_error(
"key '" + name +
"' not found in section '" +
376 std::string value = _value->second;
378 if constexpr (std::is_same<T, std::string>()) {
380 }
else if constexpr (std::is_same<T, bool>()) {
381 return BoolConverter(value);
383 return Converter<T>(value);
396 template <
typename T>
398 T&& default_v)
const {
400 return Get<T>(section, name);
401 }
catch (std::runtime_error& e) {
422 template <
typename T>
424 const std::string& name)
const {
425 std::string value =
Get(section, name);
427 std::istringstream out{value};
428 const std::vector<std::string> strs{std::istream_iterator<std::string>{out},
429 std::istream_iterator<std::string>()};
432 for (
const std::string& s : strs) {
433 vs.emplace_back(Converter<T>(s));
436 }
catch (std::exception& e) {
437 throw std::runtime_error(
"cannot parse value " + value +
453 template <
typename T>
455 const std::string& section,
const std::string& name,
456 const std::vector<T>& default_v)
const {
458 return GetVector<T>(section, name);
459 }
catch (std::runtime_error& e) {
471 template <
typename T>
473 const std::string& name,
const T& v) {
474 if (_values[section][name].size() > 0) {
475 throw std::runtime_error(
"duplicate key '" + std::string(name) +
476 "' in section '" + section +
"'.");
478 _values[section][name] = V2String(v);
488 template <
typename T>
490 const std::string& name,
491 const std::vector<T>& vs) {
492 if (_values[section][name].size() > 0) {
493 throw std::runtime_error(
"duplicate key '" + std::string(name) +
494 "' in section '" + section +
"'.");
496 _values[section][name] = Vec2String(vs);
506 template <
typename T>
508 const std::string& name,
const T& v) {
509 if (!_values[section][name].size()) {
510 throw std::runtime_error(
"key '" + std::string(name) +
511 "' not exist in section '" + section +
"'.");
513 _values[section][name] = V2String(v);
523 template <
typename T>
525 const std::string& name,
526 const std::vector<T>& vs) {
527 if (!_values[section][name].size()) {
528 throw std::runtime_error(
"key '" + std::string(name) +
529 "' not exist in section '" + section +
"'.");
531 _values[section][name] = Vec2String(vs);
534 template <
typename T>
535 inline std::string INIReader::V2String(
const T& v)
const {
536 std::stringstream ss;
541 template <
typename T>
542 inline std::string INIReader::Vec2String(
const std::vector<T>& v)
const {
546 std::ostringstream oss;
547 std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(oss,
" "));
553 template <
typename T>
554 inline T INIReader::Converter(
const std::string& s)
const {
557 std::istringstream _{s};
558 _.exceptions(std::ios::failbit);
561 }
catch (std::exception& e) {
562 throw std::runtime_error(
"cannot parse value '" + s +
"' to type<T>.");
566 inline const bool INIReader::BoolConverter(std::string s)
const {
567 std::transform(s.begin(), s.end(), s.begin(), ::tolower);
568 static const std::unordered_map<std::string, bool> s2b{
569 {
"1",
true}, {
"true",
true}, {
"yes",
true}, {
"on",
true},
570 {
"0",
false}, {
"false",
false}, {
"no",
false}, {
"off",
false},
572 auto const value = s2b.find(s);
573 if (value == s2b.end()) {
574 throw std::runtime_error(
"'" + s +
"' is not a valid boolean value.");
576 return value->second;
579 inline int INIReader::ValueHandler(
void* user,
const char* section,
580 const char* name,
const char* value) {
581 INIReader* reader = (INIReader*)user;
582 if (reader->_values[section][name].size() > 0) {
583 throw std::runtime_error(
"duplicate key '" + std::string(name) +
584 "' in section '" + section +
"'.");
586 reader->_values[section][name] = value;
591 #ifndef __INIWRITER_H__
592 #define __INIWRITER_H__
604 inline static void write(
const std::string& filepath,
606 if (
struct stat buf; stat(filepath.c_str(), &buf) == 0) {
607 throw std::runtime_error(
"file: " + filepath +
" already exist.");
611 if (!out.is_open()) {
612 throw std::runtime_error(
"cannot open output file: " + filepath);
614 for (
const auto& section : reader.
Sections()) {
615 out <<
"[" << section <<
"]\n";
616 for (
const auto& key : reader.
Keys(section)) {
617 out << key <<
"=" << reader.
Get(section, key) <<
"\n";
const std::unordered_map< std::string, std::string > Get(std::string section) const
Get the map representing the values in a section of the INI file.
Definition: ini.h:350
const std::set< std::string > Sections() const
Return the list of sections found in ini file.
Definition: ini.h:322
void InsertEntry(const std::string §ion, const std::string &name, const T &v)
Insert a key-value pair into the INI file.
Definition: ini.h:472
void UpdateEntry(const std::string §ion, const std::string &name, const T &v)
Update a key-value pair in the INI file.
Definition: ini.h:507
std::vector< T > GetVector(const std::string §ion, const std::string &name) const
Return the value array of the given key in the given section.
Definition: ini.h:423
const std::set< std::string > Keys(std::string section) const
Return the list of keys in the given section.
Definition: ini.h:335
static void write(const std::string &filepath, const INIReader &reader)
Write the contents of an INI file to a new file.
Definition: ini.h:604
Yet another .ini parser for modern c++ (made for cpp17), inspired and extend from @benhoyt's inih.
Definition: ini.h:28