config_file.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /*!
  2. * \file config_file.cpp
  3. * The file interface to the config class.
  4. */
  5. // Config Class
  6. // Author: Charles Gruenwald III
  7. #include "config_file.hpp"
  8. #include "config_exceptions.hpp"
  9. #include "itostr.h"
  10. #include <iostream>
  11. #include <fstream>
  12. #include <sstream>
  13. #include <errno.h>
  14. #include <boost/algorithm/string.hpp>
  15. #include <boost/version.hpp>
  16. #include <boost/lexical_cast.hpp>
  17. namespace config
  18. {
  19. ConfigFile::ConfigFile(bool case_sensitive)
  20. :
  21. Config(case_sensitive)
  22. {
  23. }
  24. ConfigFile::ConfigFile(const Section & root, bool case_sensitive)
  25. :
  26. Config(root, case_sensitive)
  27. {
  28. }
  29. //load a given filename into a string, taken from boost regexp replace example
  30. void ConfigFile::loadFileToString(String& s, const String& filename)
  31. {
  32. std::ifstream file_in(filename.c_str());
  33. s.erase();
  34. s.reserve(file_in.rdbuf()->in_avail());
  35. char c;
  36. while(file_in.get(c))
  37. {
  38. if(s.capacity() == s.size())
  39. s.reserve(s.capacity() * 3);
  40. s.append(1, c);
  41. }
  42. file_in.close();
  43. }
  44. void ConfigFile::loadConfig()
  45. {
  46. String filename = m_path;
  47. //Make sure the file exists
  48. if(access(filename.c_str(), R_OK) != 0)
  49. {
  50. throw FileNotFound(filename);
  51. }
  52. //Read the data in from the file
  53. String data;
  54. loadFileToString(data, filename);
  55. loadConfigFromString(data);
  56. }
  57. void ConfigFile::loadConfigFromString(const String & cfg)
  58. {
  59. parse(cfg, m_root);
  60. }
  61. //Build the in-memory representation by line-by-line matching boost::regex expressions
  62. void ConfigFile::parse(const String &source, Section & current)
  63. {
  64. //rules
  65. config_parser parser;
  66. parse_info_t info = ast_parse<config_parser::factory_t>(source.c_str(), source.c_str() + source.size(),parser,space_p | comment_p("#"));
  67. if(info.full)
  68. {
  69. // Uncomment the following lines for more verbose output
  70. // showParseTree(info.trees.begin());
  71. evalTree(m_root, info.trees.begin());
  72. }
  73. else
  74. {
  75. String error_str(info.stop);
  76. throw parserError(error_str);
  77. }
  78. }
  79. void ConfigFile::showParseTree(tree_iter_t const& node, int depth)
  80. {
  81. String tabs = "";
  82. for(int i=0;i<depth;i++)
  83. tabs = tabs.append(" ");
  84. RuleID rule = node->value.value().e;
  85. // get the string value from the iter
  86. String value(node->value.begin(), node->value.end() - node->value.begin());
  87. if(rule == sectionID)
  88. value = "s: ";
  89. if(rule == configID)
  90. value = "c: " + value;
  91. else if(rule == keyID)
  92. value = "k: " + value;
  93. else if(rule == keyValueID)
  94. value = "kv: " + value;
  95. else if(rule == keyNameID)
  96. value = "kn: " + value;
  97. else if(rule == sectionNameID)
  98. value = "sn: " + value;
  99. else if(rule == keyValueArrayID)
  100. value = "kva: " + value;
  101. else if(rule == keyValueSpanID)
  102. value = "kvs: " + value;
  103. // if(rule == sectionID || rule == keyID || rule == keyValueID || rule == keyNameID || rule == sectionNameID)
  104. // if(rule > 0)
  105. std::cout << tabs << (int)(rule) << ".[" << value << "]" << std::endl;
  106. for(tree_iter_t chi = node->children.begin(); chi != node->children.end(); chi++)
  107. {
  108. showParseTree(chi, depth + 1);
  109. }
  110. }
  111. void ConfigFile::unEscapeText(const String & source, String & dest)
  112. {
  113. bool backslash = false;
  114. unsigned int start = 0;
  115. unsigned int end = source.size();
  116. //remove surrounding parens if necessary
  117. if(source.size() >= 2 && source.c_str()[0] == '"' && source.c_str()[source.size() - 1] == '"')
  118. {
  119. ++start;
  120. --end;
  121. }
  122. for (unsigned int i = start; i < end; i++) {
  123. char c = source.c_str()[i];
  124. if (backslash)
  125. {
  126. switch (c)
  127. {
  128. case '\\': dest += "\\"; break;
  129. case 'b': dest += "\b"; break;
  130. case 'r': dest += "\r"; break;
  131. case 'f': dest += "\f"; break;
  132. case 't': dest += "\t"; break;
  133. case 'n': dest += "\n"; break;
  134. case '"': dest += "\""; break;
  135. case '\'': dest += "'"; break;
  136. default: dest += c;
  137. }
  138. backslash = false;
  139. }
  140. else
  141. {
  142. if (c == '\\')
  143. backslash = true;
  144. else
  145. dest += c;
  146. }
  147. }
  148. }
  149. String ConfigFile::getNodeValue(tree_iter_t const& node)
  150. {
  151. if(node->value.begin() == node->value.end())
  152. return "";
  153. return String(node->value.begin(), node->value.end());
  154. }
  155. RuleID ConfigFile::getNodeID(tree_iter_t const& node)
  156. {
  157. return node->value.value().e;
  158. }
  159. void ConfigFile::escapeText(const String & source, String & dest)
  160. {
  161. for (unsigned int i = 0; i < source.size(); i++) {
  162. char c = source.c_str()[i];
  163. switch (c)
  164. {
  165. case '\\': dest += "\\\\"; break;
  166. case '\"': dest += "\\\""; break;
  167. default: dest += c;
  168. }
  169. }
  170. }
  171. // This function recursively decends the tree built by the parser and populates the config
  172. // file with the appropriate entries
  173. void ConfigFile::evalTree(Section & current, tree_iter_t const& node, int depth)
  174. {
  175. String tabs = "";
  176. for(int i=0;i<depth;i++)
  177. tabs = tabs.append(" ");
  178. const RuleID rule (getNodeID(node));
  179. const String value(getNodeValue(node));
  180. if(rule == sectionNameID)
  181. {
  182. // Since this is a flat file representation, whenever we get a section we only add it
  183. // to the root node, thus we break out if we are currently nested.
  184. if(!current.isRoot())
  185. return;
  186. //HACK: Strip off the '[' and ']', guaranteed to be matched.
  187. //This should be removed when we upgrade to Boost 1.35, as per config_file_grammar.hpp
  188. //note we should just be able to say section_name = value
  189. String section_name;
  190. section_name = value.substr(1, value.size() - 2);
  191. // std::cout << "Found section: [" << section_name << "]" << std::endl;
  192. // create the section
  193. Section &child = Config::getSection_unsafe(section_name);
  194. // add each of the children nodes to this section
  195. for(tree_iter_t chi_node = node->children.begin(); chi_node != node->children.end(); chi_node++)
  196. {
  197. evalTree(child, chi_node, depth + 1);
  198. }
  199. }
  200. else if(rule == keyID)
  201. {
  202. //assert(node->children.size() == 1 || node->children.size() == 2);
  203. String key_name = "";
  204. String key_value = "";
  205. // Allow for empty (non-existant) values
  206. if (node->children.size() != 1 && node->children.size() != 2)
  207. {
  208. throw parserError("Internal parser error: Expected one or two keyID children, not " + boost::lexical_cast<String>(node->children.size()));
  209. }
  210. if (getNodeID(node->children.begin()) != keyNameID)
  211. {
  212. throw parserError("Internal parser error: Expected the first entry to be a keyNameID");
  213. }
  214. unEscapeText(getNodeValue(node->children.begin()), key_name);
  215. if (node->children.size() == 1)
  216. {
  217. current.addKey(key_name, key_value);
  218. }
  219. else if (node->children.size() == 2)
  220. {
  221. UInt64 index = 0;
  222. tree_iter_t const& datanode = node->children.begin() + 1;
  223. switch(getNodeID(datanode))
  224. {
  225. case keyValueID:
  226. unEscapeText(getNodeValue(datanode), key_value);
  227. current.addKey(key_name, key_value);
  228. break;
  229. case keyValueArrayID:
  230. for(tree_iter_t chi = datanode->children.begin(); chi != datanode->children.end(); chi++)
  231. {
  232. switch(getNodeID(chi))
  233. {
  234. case keySeparatorID:
  235. ++index;
  236. break;
  237. case keyValueID:
  238. key_value = "";
  239. unEscapeText(getNodeValue(chi), key_value);
  240. if (key_value != "")
  241. current.addKey(key_name, key_value, index);
  242. break;
  243. default:
  244. throw parserError("Internal parser error: Expected separator or value");
  245. }
  246. }
  247. break;
  248. // FIXME: Span should properly create a span of numbers
  249. case keyValueSpanID:
  250. default:
  251. throw parserError("Internal parser error: Unexpected node type");
  252. }
  253. }
  254. else
  255. {
  256. throw parserError("Internal parser error: Unexpacted number of keyID children found: " + boost::lexical_cast<String>(node->children.size()));
  257. }
  258. }
  259. else
  260. {
  261. for(tree_iter_t chi = node->children.begin(); chi != node->children.end(); chi++)
  262. {
  263. evalTree(current, chi, depth + 1);
  264. }
  265. }
  266. }
  267. void ConfigFile::saveAs(const String &path)
  268. {
  269. const String tmp_path(path + ".tmp");
  270. std::ofstream output_file;
  271. output_file.exceptions ( std::ofstream::eofbit | std::ofstream::failbit | std::ofstream::badbit );
  272. //Atomic write with error handling
  273. try
  274. {
  275. output_file.open(tmp_path.c_str());
  276. SaveTreeAs(output_file, m_root);
  277. output_file.close();
  278. }
  279. catch(const std::ofstream::failure & e)
  280. {
  281. throw SaveError(e.what());
  282. }
  283. unlink(path.c_str());
  284. int res = rename(tmp_path.c_str(), path.c_str());
  285. if (res == -1)
  286. {
  287. int errsv = errno;
  288. String error_str("Moving " + tmp_path + " to " + path + ": error(" + itostr(errsv) + ") " + strerror(errsv) + ".");
  289. throw SaveError(error_str);
  290. }
  291. }
  292. //Dump the tree in a format that is readable by our parser
  293. void ConfigFile::SaveTreeAs(std::ofstream &out, const Section &current)
  294. {
  295. if(!current.isRoot())
  296. {
  297. out << "[" << current.getFullPath() << "]" << std::endl;
  298. }
  299. // Write out all of the default values
  300. KeyList const & keys = current.getKeys();
  301. for(KeyList::const_iterator i = keys.begin(); i != keys.end();i++)
  302. {
  303. const String & name = i->second->getName();
  304. //Quote the name if it has spaces in it
  305. if(boost::find_first(name, " ") || boost::find_first(name, "\""))
  306. {
  307. String escaped_name;
  308. escapeText(name, escaped_name);
  309. out << "\"" << escaped_name << "\"";
  310. }
  311. else
  312. out << name;
  313. //Quote the value if it is a string
  314. if(i->second->getFloatValid() || i->second->getIntValid())
  315. out << " = " << i->second->getString() << std::endl;
  316. else
  317. {
  318. String escaped_value;
  319. escapeText(i->second->getString(), escaped_value);
  320. out << " = \"" << escaped_value << "\"" << std::endl;
  321. }
  322. }
  323. //Write out the array data that overrides the defaults
  324. KeyArrayList const & arr = current.getArrayKeys();
  325. for(KeyArrayList::const_iterator i = arr.begin(); i != arr.end(); ++i)
  326. {
  327. for (uint64_t index = 0; index < i->second.size(); ++index)
  328. {
  329. // Find a non-default configuration value where we can get the name from
  330. if (i->second[index])
  331. {
  332. const String & name = i->second[index]->getName();
  333. //Quote the name if it has spaces in it
  334. if(boost::find_first(name, " ") || boost::find_first(name, "\""))
  335. {
  336. String escaped_name;
  337. escapeText(name, escaped_name);
  338. out << "\"" << escaped_name << "\"";
  339. }
  340. else
  341. out << name;
  342. break;
  343. }
  344. }
  345. out << "[] = ";
  346. for (uint64_t index = 0; index < i->second.size(); ++index)
  347. {
  348. if (i->second[index])
  349. {
  350. //Quote the value if it is a string
  351. if(i->second[index]->getFloatValid() || i->second[index]->getIntValid())
  352. out << i->second[index]->getString();
  353. else
  354. {
  355. String escaped_value;
  356. escapeText(i->second[index]->getString(), escaped_value);
  357. out << "\"" << escaped_value << "\"";
  358. }
  359. }
  360. // else: default value, print nothing between the ","
  361. out << ",";
  362. }
  363. out << std::endl;
  364. }
  365. out << std::endl;
  366. SectionList const & subsections = current.getSubsections();
  367. for(SectionList::const_iterator i = subsections.begin(); i != subsections.end(); i++)
  368. {
  369. Section const & subsection = *(i->second);
  370. SaveTreeAs(out, subsection);
  371. }
  372. return;
  373. }
  374. //namespaces
  375. }