-
Notifications
You must be signed in to change notification settings - Fork 17
/
ini.h
126 lines (94 loc) · 2.87 KB
/
ini.h
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
#ifndef INI_H_INCLUDED
#define INI_H_INCLUDED
#include <ios>
#include <string>
#include <fstream>
#include <stdexcept>
#include <algorithm>
#include <string_view>
// A VERY rudimentary ini lexer.
enum class ini_token_type {
Eof,
Section,
KeyValue,
};
struct ini_token {
ini_token_type Type;
std::string_view Key;
std::string_view Value;
};
template <typename ItT>
inline ItT TrimLeft(ItT Begin,ItT End){
auto IsSpace = [](auto c){
return '\0' <= c && c <= ' ';
};
return std::find_if_not(Begin,End,IsSpace);
}
template <typename ItT>
inline ItT TrimRight(ItT Begin,ItT End){
return TrimLeft(std::make_reverse_iterator(End),std::make_reverse_iterator(Begin)).base();
}
template <typename ItT>
inline std::basic_string_view<typename ItT::value_type> StringView(ItT Begin,ItT End){
return {&*Begin,static_cast<std::size_t>(End-Begin)};
}
class ini_lexer {
public:
explicit ini_lexer(const char* Filename)
:File(Filename,std::ios::binary),Line(),LineNum(0),Filename(Filename) {
if(!File){
throw std::runtime_error(std::string("Unable to open configuration file: ")+Filename);
}
}
ini_token Next(){
ini_token r = {};
while(std::getline(File,Line)){
LineNum += 1;
auto Begin = TrimLeft(Line.begin(),Line.end());
auto End = TrimRight(Begin,std::find(Begin,Line.end(),';'));
if(Begin == End){
continue;
}
if(Begin[0] == '['){
if(End[-1] != ']'){
Error("Invalid section name. Starts with '[' but does not end with ']'.");
}
r.Type = ini_token_type::Section;
++Begin;
--End;
}else{
auto Middle = std::find(Begin,End,'=');
if(Middle == End){
Error("Expected '=' followed by a value but found nothing.");
}
r.Type = ini_token_type::KeyValue;
r.Value = StringView(TrimLeft(Middle+1,End),End);
if(r.Value.empty()){
Error("Empty value string.");
}
End = TrimRight(Begin,Middle);
}
std::for_each(Begin,End,[](auto& c){
c = ('A' <= c && c <= 'z')?c|32:c;
});
r.Key = StringView(Begin,End);
if(r.Key.empty()){
Error("Empty key string.");
}
break;
}
return r;
}
std::string Name() const {
return Filename+(":"+std::to_string(LineNum));
}
void Error(const char* Message) const {
throw std::runtime_error(Name()+": "+Message);
}
private:
std::ifstream File;
std::string Line;
std::intmax_t LineNum;
const char* Filename;
};
#endif // INI_H_INCLUDED