nsnake
Classic snake game for the terminal
Loading...
Searching...
No Matches
INI.cpp
1#include <Config/INI.hpp>
2#include <Misc/Utils.hpp>
3
4void INI::Level::addGroup(std::string name)
5{
6 name = Utils::String::trim(name);
7
8 // When we call `this->sections[name]` we already create
9 // a new one if it doesn't exist.
10 //
11 // The only way we'll know we just made a new one is if
12 // it's parent is NULL.
13 //
14 // Since the only one allowed to be like this is the top
15 // (and the top always exist), this is the way we check
16 // for newly-created group.
17 if (this->sections[name].parent != NULL)
18 return;
19
20 // Setting it's parent as ourselves
21 this->sections[name].parent = this;
22 this->sections[name].depth = this->depth + 1;
23
24 // Registering it on ourselves
25 this->ordered_sections.push_back(this->sections.find(name));
26}
27void INI::Level::addKey(std::string name, std::string value)
28{
29 name = Utils::String::trim(name);
30 value = Utils::String::trim(value);
31
32 std::pair<Level::ValueMap::const_iterator, bool> res =
33 this->values.insert(std::make_pair(name, value));
34
35 if (!res.second)
36 {
37 // Found other key with this name,
38 // let's overwrite its value
39 this->values[name] = value;
40 return;
41 }
42 this->ordered_values.push_back(res.first);
43}
44
45void INI::Parser::raise_error(std::string msg)
46{
47 std::string buffer = ("Error '" +
48 msg +
49 "' on line #" +
50 Utils::String::toString(this->lines));
51
52 throw std::runtime_error(buffer);
53}
54
56 lines(0)
57{
58 this->create();
59}
60
61INI::Parser::Parser(std::string filename) :
62 input_file(filename.c_str()),
63 input(&input_file),
64 lines(0)
65{
66 if (! input)
67 throw std::runtime_error("Failed to open file: " + filename);
68
69 parse(this->top_level);
70}
71
72INI::Parser::Parser(std::istream& stream) :
73 input(&stream),
74 lines(0)
75{
76 parse(this->top_level);
77}
78
80{
81 return this->top_level;
82}
83
84void INI::Parser::dump(std::ostream& stream)
85{
86 dump(stream, top(), "");
87}
88
89void INI::Parser::parseLevelLine(std::string& sname, size_t& depth)
90{
91 depth = 0;
92
93 for (; depth < line_.length(); ++depth)
94 if (line_[depth] != '[') break;
95
96 sname = line_.substr(depth, line_.length() - 2*depth);
97}
98
99void INI::Parser::parse(INI::Level& level)
100{
101 while (std::getline(*input, line_))
102 {
103 ++lines;
104
105 if (line_[0] == '#' || line_[0] == ';') continue;
106
107 line_ = Utils::String::trim(line_);
108
109 if (line_.empty()) continue;
110
111 if (line_[0] == '[')
112 {
113 size_t depth;
114
115 std::string sname;
116
117 parseLevelLine(sname, depth);
118
119 INI::Level* level_current = NULL;
120 INI::Level* parent = &level;
121
122 if (depth > level.depth + 1)
123 raise_error("section with wrong depth");
124
125 if (level.depth == depth - 1)
126 level_current = &level.sections[sname];
127
128 else
129 {
130 level_current = level.parent;
131
132 size_t n = (level.depth - depth);
133
134 for (size_t i = 0; i < n; ++i)
135 level_current = level_current->parent;
136
137 parent = level_current;
138
139 level_current = &level_current->sections[sname];
140
141 }
142
143 if (level_current->depth != 0)
144 raise_error("duplicate section name on the same level");
145
146 if (!level_current->parent)
147 {
148 level_current->depth = depth;
149 level_current->parent = parent;
150 }
151
152 parent->ordered_sections.push_back(parent->sections.find(sname));
153
154 parse(*level_current);
155 }
156 else
157 {
158 // Not a group - found a key-value pair, like:
159 // `something = other_something`
160
161 size_t pos = line_.find('=');
162
163 if (pos == std::string::npos)
164 raise_error("no '=' found");
165
166 std::string key = line_.substr(0, pos);
167 std::string value = line_.substr((pos + 1), (line_.length()-pos-1));
168
169 level.addKey(key, value);
170 }
171 }
172}
173
174void INI::Parser::dump(std::ostream& s, const INI::Level& l, const std::string& sname)
175{
176 if (!sname.empty())
177 s << '\n';
178
179 for (size_t i = 0; i < l.depth; ++i)
180 s << '[';
181
182 if (!sname.empty())
183 s << sname;
184
185 for (size_t i = 0; i < l.depth; ++i)
186 s << ']';
187
188 if (!sname.empty())
189 s << std::endl;
190
191 for (INI::Level::Values::const_iterator it = l.ordered_values.begin();
192 it != l.ordered_values.end();
193 ++it)
194 s << (*it)->first << '=' << (*it)->second << std::endl;
195
196 for (INI::Level::Sections::const_iterator it = l.ordered_sections.begin();
197 it != l.ordered_sections.end();
198 ++it)
199 {
200 assert((*it)->second.depth == l.depth+1);
201
202 dump(s, (*it)->second, (*it)->first);
203 }
204}
205
206void INI::Parser::saveAs(std::string filename)
207{
208 std::ofstream file_out(filename.c_str());
209 if (!file_out)
210 throw std::runtime_error(std::string("Couldn't open '" + filename + "'"));
211
212 this->dump(file_out);
213}
214
216{
217 this->top_level = Level();
218}
219
void dump(std::ostream &stream)
Outputs the contents of the INI file to #stream.
Definition INI.cpp:84
void saveAs(std::string filename)
Save all the internal INI contents on a file with #filename.
Definition INI.cpp:206
Parser()
Creates a blank new INI file.
Definition INI.cpp:55
Level & top()
Returns the top level of this INI file.
Definition INI.cpp:79
void create()
Creates a blank INI registry.
Definition INI.cpp:215
Contains a "scope" of the INI file.
Definition INI.hpp:72
Sections ordered_sections
All Sections in the original order of the INI file.
Definition INI.hpp:126
SectionMap sections
All the Levels inside this Level.
Definition INI.hpp:120
void addKey(std::string name, std::string value)
Creates a new key #name with #value.
Definition INI.cpp:27
Level * parent
The parent Level of this one.
Definition INI.hpp:87
void addGroup(std::string name)
Creates a new child group with #name.
Definition INI.cpp:4
size_t depth
Counter of how many nested levels this one is.
Definition INI.hpp:90
Values ordered_values
All values in the original order of the INI file.
Definition INI.hpp:123