ConfigFileParser.cpp 9.6 KB

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