nsnake
Classic snake game for the terminal
INI.cpp
1 #include <Config/INI.hpp>
2 #include <Misc/Utils.hpp>
3 
4 void 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 }
27 void 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 
45 void 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 
61 INI::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 
72 INI::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 
84 void INI::Parser::dump(std::ostream& stream)
85 {
86  dump(stream, top(), "");
87 }
88 
89 void 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 
99 void 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 
174 void 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 
206 void 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 
Level & top()
Returns the top level of this INI file.
Definition: INI.cpp:79
Contains a "scope" of the INI file.
Definition: INI.hpp:71
void saveAs(std::string filename)
Save all the internal INI contents on a file with #filename.
Definition: INI.cpp:206
size_t depth
Counter of how many nested levels this one is.
Definition: INI.hpp:90
void addGroup(std::string name)
Creates a new child group with #name.
Definition: INI.cpp:4
SectionMap sections
All the Levels inside this Level.
Definition: INI.hpp:120
Values ordered_values
All values in the original order of the INI file.
Definition: INI.hpp:123
void dump(std::ostream &stream)
Outputs the contents of the INI file to #stream.
Definition: INI.cpp:84
void addKey(std::string name, std::string value)
Creates a new key #name with #value.
Definition: INI.cpp:27
ValueMap values
All the key values inside this Level.
Definition: INI.hpp:107
Sections ordered_sections
All Sections in the original order of the INI file.
Definition: INI.hpp:126
void create()
Creates a blank INI registry.
Definition: INI.cpp:215
Level * parent
The parent Level of this one.
Definition: INI.hpp:87
Parser()
Creates a blank new INI file.
Definition: INI.cpp:55