Explorar el Código

initial commit

Patrick Brosi hace 9 años
commit
6b107d1c5e
Se han modificado 3 ficheros con 411 adiciones y 0 borrados
  1. 2 0
      CMakeLists.txt
  2. 324 0
      ConfigFileParser.cpp
  3. 85 0
      ConfigFileParser.h

+ 2 - 0
CMakeLists.txt

@@ -0,0 +1,2 @@
1
+file(GLOB_RECURSE cfgparser_SRC *.cpp)
2
+add_library(configfileparser ${cfgparser_SRC})

+ 324 - 0
ConfigFileParser.cpp

@@ -0,0 +1,324 @@
1
+// Copyright 2015 Patrick Brosi
2
+// info@patrickbrosi.de
3
+
4
+#include <algorithm>
5
+#include <fstream>
6
+#include <iostream>
7
+#include <limits>
8
+#include <set>
9
+#include <sstream>
10
+#include <string>
11
+#include "./ConfigFileParser.h"
12
+
13
+using namespace config;
14
+
15
+// _____________________________________________________________________________
16
+ConfigFileParser::ConfigFileParser() {}
17
+
18
+// _____________________________________________________________________________
19
+void ConfigFileParser::parse(const std::string& path) {
20
+  State s = NONE;
21
+  std::set<std::string> headers;
22
+  KeyVals curKV;
23
+  std::string tmp, tmp2;
24
+  std::fstream is(path);
25
+  char c;
26
+  size_t l = 1, pos = 0, valLine = 0, valPos = 0;
27
+
28
+  while (is.get(c)) {
29
+    pos++;
30
+    // skip comments
31
+    while (c == '#') {
32
+      is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
33
+      c = '\n';
34
+    }
35
+    if (c == '\n') {
36
+      l++;
37
+      pos = 1;
38
+    }
39
+    switch (s) {
40
+      case NONE:
41
+        if (std::isspace(c)) continue;
42
+        if (c == '[') {
43
+          if (tmp.size()) curKV[tmp] = Val{trim(tmp2), valLine, valPos};
44
+          tmp.clear();
45
+          tmp2.clear();
46
+          for (auto h : headers) {
47
+            if (_secs.find(h) != _secs.end()) {
48
+              _kvs[_secs[h]].insert(curKV.begin(), curKV.end());
49
+            } else {
50
+              _secs[h] = _kvs.size();
51
+              _kvs.push_back(curKV);
52
+            }
53
+          }
54
+          headers.clear();
55
+          curKV.clear();
56
+          s = IN_HEAD;
57
+          continue;
58
+        }
59
+        if (isKeyChar(c)) {
60
+          if (!headers.size()) headers.insert("");
61
+          tmp.push_back(c);
62
+          s = IN_KEY_VAL_KEY;
63
+          continue;
64
+        }
65
+        throw ParseExc(l, pos, "header or key", std::string("'") + c + "'",
66
+                       path);
67
+      case IN_HEAD:
68
+        if (std::isspace(c)) continue;
69
+        if (isKeyChar(c)) {
70
+          s = IN_HEAD_KEY;
71
+          tmp.push_back(c);
72
+          continue;
73
+        }
74
+        throw ParseExc(l, pos, "header name", std::string("'") + c + "'", path);
75
+
76
+      case IN_HEAD_KEY:
77
+        if (isKeyChar(c)) {
78
+          tmp.push_back(c);
79
+          continue;
80
+        }
81
+
82
+        if (std::isspace(c)) {
83
+          s = IN_HEAD_AW_COM_OR_END;
84
+          continue;
85
+        }
86
+      // fall through here
87
+      case IN_HEAD_AW_COM_OR_END:
88
+        if (std::isspace(c)) continue;
89
+        if (c == ',') {
90
+          headers.insert(tmp);
91
+          tmp.clear();
92
+          s = IN_HEAD;
93
+          continue;
94
+        }
95
+        if (c == ']') {
96
+          s = NONE;
97
+          headers.insert(tmp);
98
+          tmp.clear();
99
+          continue;
100
+        }
101
+        throw ParseExc(l, pos, "next header oder header end",
102
+                       std::string("'") + c + "'", path);
103
+      case IN_KEY_VAL_KEY:
104
+        if (isKeyChar(c)) {
105
+          tmp.push_back(c);
106
+          continue;
107
+        }
108
+
109
+        if (c == ':' || c == '=') {
110
+          valLine = l;
111
+          valPos = pos;
112
+          s = AW_KEY_VAL_VAL;
113
+          continue;
114
+        }
115
+
116
+        throw ParseExc(l, pos, "key/value", std::string("'") + c + "'", path);
117
+
118
+      case AW_KEY_VAL_VAL:
119
+        if (c == '\r' || c == '\n') {
120
+          s = IN_KEY_VAL_VAL_HANG;
121
+          continue;
122
+        }
123
+        if (std::isspace(c)) continue;
124
+      case IN_KEY_VAL_VAL:
125
+        s = IN_KEY_VAL_VAL;
126
+        if (c == '\r' || c == '\n') {
127
+          s = IN_KEY_VAL_VAL_HANG;
128
+          continue;
129
+        }
130
+        tmp2.push_back(c);
131
+        continue;
132
+      case IN_KEY_VAL_VAL_HANG:
133
+        if (c == '\r' || c == '\n') {
134
+          continue;
135
+        }
136
+        if (c == '[') {
137
+          curKV[tmp] = Val{trim(tmp2), valLine, valPos};
138
+          tmp.clear();
139
+          tmp2.clear();
140
+          for (auto h : headers) {
141
+            if (_secs.find(h) != _secs.end()) {
142
+              _kvs[_secs[h]].insert(curKV.begin(), curKV.end());
143
+            } else {
144
+              _secs[h] = _kvs.size();
145
+              _kvs.push_back(curKV);
146
+            }
147
+          }
148
+          headers.clear();
149
+          curKV.clear();
150
+          s = IN_HEAD;
151
+          continue;
152
+        }
153
+        if (isKeyChar(c)) {
154
+          curKV[tmp] = Val{trim(tmp2), valLine, valPos};
155
+          tmp.clear();
156
+          tmp2.clear();
157
+          tmp.push_back(c);
158
+          s = IN_KEY_VAL_KEY;
159
+          continue;
160
+        }
161
+        if (std::isspace(c)) {
162
+          s = IN_KEY_VAL_VAL_HANG_END;
163
+          continue;
164
+        }
165
+
166
+      case IN_KEY_VAL_VAL_HANG_END:
167
+        if (c == '\r' || c == '\n') {
168
+          s = IN_KEY_VAL_VAL_HANG;
169
+          continue;
170
+        }
171
+        if (std::isspace(c)) continue;
172
+        if (!tmp2.empty()) tmp2.push_back(' ');
173
+        tmp2.push_back(c);
174
+        s = IN_KEY_VAL_VAL;
175
+        continue;
176
+    }
177
+  }
178
+
179
+  if (s != IN_KEY_VAL_VAL && s != NONE && s != IN_KEY_VAL_VAL_HANG &&
180
+      s != IN_KEY_VAL_VAL_HANG_END) {
181
+    throw ParseExc(l, pos, "character", "<EOF>", path);
182
+  }
183
+
184
+  if (tmp.size() && tmp2.size()) curKV[tmp] = Val{tmp2, valLine, valPos};
185
+  tmp.clear();
186
+  tmp2.clear();
187
+  for (auto h : headers) {
188
+    if (_secs.find(h) != _secs.end()) {
189
+      _kvs[_secs[h]].insert(curKV.begin(), curKV.end());
190
+    } else {
191
+      _secs[h] = _kvs.size();
192
+      _kvs.push_back(curKV);
193
+    }
194
+  }
195
+}
196
+
197
+// _____________________________________________________________________________
198
+const std::string& ConfigFileParser::getStr(Sec sec, const Key& key) const {
199
+  return _kvs[_secs.find(sec)->second].find(key)->second.val;
200
+}
201
+// _____________________________________________________________________________
202
+int ConfigFileParser::getInt(Sec sec, const Key& key) const {
203
+  return atoi(_kvs[_secs.find(sec)->second].find(key)->second.val.c_str());
204
+}
205
+// _____________________________________________________________________________
206
+double ConfigFileParser::getDouble(Sec sec, const Key& key) const {
207
+  return atof(_kvs[_secs.find(sec)->second].find(key)->second.val.c_str());
208
+}
209
+// _____________________________________________________________________________
210
+bool ConfigFileParser::getBool(Sec sec, const Key& key) const {
211
+  return toBool(getStr(sec, key));
212
+}
213
+
214
+// _____________________________________________________________________________
215
+bool ConfigFileParser::toBool(std::string v) const {
216
+  std::transform(v.begin(), v.end(), v.begin(), ::tolower);
217
+
218
+  if (v == "true") return true;
219
+  if (v == "false") return false;
220
+  if (v == "yes") return true;
221
+  if (v == "no") return false;
222
+  if (v == "enable") return true;
223
+  if (v == "disable") return false;
224
+  if (v == "on") return true;
225
+  if (v == "off") return false;
226
+  if (v == "+") return true;
227
+  if (v == "-") return false;
228
+  if (v == "t") return true;
229
+  if (v == "f") return false;
230
+
231
+  return atoi(v.c_str());
232
+}
233
+
234
+// _____________________________________________________________________________
235
+std::vector<std::string> ConfigFileParser::getStrArr(Sec sec, const Key& key,
236
+                                                     char del) const {
237
+  size_t p, l = 0;
238
+  std::string s = getStr(sec, key) + del;
239
+  std::vector<std::string> ret;
240
+  while ((p = s.find(del, l)) != std::string::npos) {
241
+    std::string tk = trim(s.substr(l, p - l));
242
+    if (tk.size()) ret.push_back(tk);
243
+    l = p + 1;
244
+  }
245
+
246
+  return ret;
247
+}
248
+// _____________________________________________________________________________
249
+std::vector<int> ConfigFileParser::getIntArr(Sec sec, const Key& key,
250
+                                             char del) const {
251
+  std::vector<int> ret;
252
+  for (const std::string& s : getStrArr(sec, key, del)) {
253
+    ret.push_back(atoi(s.c_str()));
254
+  }
255
+
256
+  return ret;
257
+}
258
+// _____________________________________________________________________________
259
+std::vector<double> ConfigFileParser::getDoubleArr(Sec sec, const Key& key,
260
+                                                   char del) const {
261
+  std::vector<double> ret;
262
+  for (const std::string& s : getStrArr(sec, key, del)) {
263
+    ret.push_back(atof(s.c_str()));
264
+  }
265
+
266
+  return ret;
267
+}
268
+// _____________________________________________________________________________
269
+std::vector<bool> ConfigFileParser::getBoolArr(Sec sec, const Key& key,
270
+                                               char del) const {
271
+  std::vector<bool> ret;
272
+  for (const std::string& s : getStrArr(sec, key, del)) {
273
+    ret.push_back(toBool(s.c_str()));
274
+  }
275
+
276
+  return ret;
277
+}
278
+
279
+// _____________________________________________________________________________
280
+bool ConfigFileParser::isKeyChar(char t) const {
281
+  return std::isalnum(t) || t == '_' || t == '-' || t == '.';
282
+}
283
+
284
+// _____________________________________________________________________________
285
+std::string ConfigFileParser::toString() const {
286
+  std::stringstream ss;
287
+  for (auto i : _secs) {
288
+    ss << std::endl << "[" << i.first << "]" << std::endl << std::endl;
289
+    for (auto kv : _kvs[i.second]) {
290
+      ss << "  " << kv.first << " = " << kv.second.val << std::endl;
291
+    }
292
+  }
293
+
294
+  return ss.str();
295
+}
296
+
297
+// _____________________________________________________________________________
298
+std::string ConfigFileParser::trim(const std::string& str) const {
299
+  size_t s = str.find_first_not_of(' ');
300
+  size_t e = str.find_last_not_of(' ');
301
+  if (s == std::string::npos) return "";
302
+  return str.substr(s, e - s + 1);
303
+}
304
+
305
+// _____________________________________________________________________________
306
+const std::unordered_map<std::string, size_t> ConfigFileParser::getSecs()
307
+    const {
308
+  return _secs;
309
+}
310
+
311
+// _____________________________________________________________________________
312
+const Val& ConfigFileParser::getVal(Sec section, Key key) const {
313
+  return _kvs[_secs.find(section)->second].find(key)->second;
314
+}
315
+
316
+// _____________________________________________________________________________
317
+bool ConfigFileParser::hasKey(Sec section, Key key) const {
318
+  if (_secs.find(section) == _secs.end()) return false;
319
+  if (_kvs[_secs.find(section)->second].find(key) ==
320
+      _kvs[_secs.find(section)->second].end())
321
+    return false;
322
+
323
+  return true;
324
+}

+ 85 - 0
ConfigFileParser.h

@@ -0,0 +1,85 @@
1
+// Copyright 2015 Patrick Brosi
2
+// info@patrickbrosi.de
3
+
4
+#pragma once
5
+
6
+#include <exception>
7
+#include <fstream>
8
+#include <sstream>
9
+#include <string>
10
+#include <unordered_map>
11
+#include <vector>
12
+
13
+namespace configparser {
14
+
15
+struct Val {
16
+  std::string val;
17
+  size_t line;
18
+  size_t pos;
19
+};
20
+
21
+typedef std::string Key;
22
+typedef std::string Sec;
23
+typedef std::unordered_map<std::string, Val> KeyVals;
24
+
25
+enum State {
26
+  NONE,
27
+  IN_HEAD,
28
+  IN_HEAD_KEY,
29
+  IN_HEAD_AW_COM_OR_END,
30
+  IN_KEY_VAL_KEY,
31
+  IN_KEY_VAL_VAL,
32
+  AW_KEY_VAL_VAL,
33
+  IN_KEY_VAL_VAL_HANG,
34
+  IN_KEY_VAL_VAL_HANG_END
35
+};
36
+
37
+class ParseExc : public std::exception {
38
+ public:
39
+  ParseExc(size_t l, size_t p, std::string exc, std::string f, std::string file)
40
+      : _l(l), _p(p), _exc(exc), _f(f), _file(file), _msg() {
41
+    std::stringstream ss;
42
+    ss << _file << ":" << _l << ", at pos " << _p << ": Expected " << _exc
43
+       << ", found " << _f;
44
+    _msg = ss.str();
45
+  };
46
+  virtual const char* what() const throw() { return _msg.c_str(); }
47
+
48
+ private:
49
+  size_t _l;
50
+  size_t _p;
51
+  std::string _exc, _f, _file, _msg;
52
+};
53
+
54
+class ConfigFileParser {
55
+ public:
56
+  ConfigFileParser();
57
+  void parse(const std::string& path);
58
+
59
+  const std::string& getStr(Sec sec, const Key& key) const;
60
+  int getInt(Sec sec, const Key& key) const;
61
+  double getDouble(Sec sec, const Key& key) const;
62
+  bool getBool(Sec sec, const Key& key) const;
63
+
64
+  std::vector<std::string> getStrArr(Sec sec, const Key& key, char del) const;
65
+  std::vector<int> getIntArr(Sec sec, const Key& key, char del) const;
66
+  std::vector<double> getDoubleArr(Sec sec, const Key& key, char del) const;
67
+  std::vector<bool> getBoolArr(Sec sec, const Key& key, char del) const;
68
+
69
+  std::string toString() const;
70
+
71
+  const std::unordered_map<std::string, size_t> getSecs() const;
72
+
73
+  bool hasKey(Sec section, Key key) const;
74
+
75
+  const Val& getVal(Sec section, Key key) const;
76
+
77
+ private:
78
+  std::unordered_map<std::string, size_t> _secs;
79
+  std::vector<KeyVals> _kvs;
80
+
81
+  bool isKeyChar(char t) const;
82
+  std::string trim(const std::string& str) const;
83
+  bool toBool(std::string str) const;
84
+};
85
+}