ConfigFileParser.cpp 9.7 KB

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