ConfigFileParser.cpp 11 KB

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