ConfigFileParser.cpp 12 KB

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