ConfigFileParser.cpp 11 KB

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