/Src/Dependencies/Boost/boost/property_tree/detail/info_parser_read.hpp

http://hadesmem.googlecode.com/ · C++ Header · 380 lines · 295 code · 43 blank · 42 comment · 115 complexity · 3a32103ca8b54fa783a9d04e789494c4 MD5 · raw file

  1. // ----------------------------------------------------------------------------
  2. // Copyright (C) 2002-2006 Marcin Kalicinski
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. // For more information, see www.boost.org
  9. // ----------------------------------------------------------------------------
  10. #ifndef BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
  11. #define BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
  12. #include "boost/property_tree/ptree.hpp"
  13. #include "boost/property_tree/detail/info_parser_error.hpp"
  14. #include "boost/property_tree/detail/info_parser_utils.hpp"
  15. #include <iterator>
  16. #include <string>
  17. #include <stack>
  18. #include <fstream>
  19. #include <cctype>
  20. namespace boost { namespace property_tree { namespace info_parser
  21. {
  22. // Expand known escape sequences
  23. template<class It>
  24. std::basic_string<typename std::iterator_traits<It>::value_type>
  25. expand_escapes(It b, It e)
  26. {
  27. typedef typename std::iterator_traits<It>::value_type Ch;
  28. std::basic_string<Ch> result;
  29. while (b != e)
  30. {
  31. if (*b == Ch('\\'))
  32. {
  33. ++b;
  34. if (b == e)
  35. {
  36. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  37. "character expected after backslash", "", 0));
  38. }
  39. else if (*b == Ch('0')) result += Ch('\0');
  40. else if (*b == Ch('a')) result += Ch('\a');
  41. else if (*b == Ch('b')) result += Ch('\b');
  42. else if (*b == Ch('f')) result += Ch('\f');
  43. else if (*b == Ch('n')) result += Ch('\n');
  44. else if (*b == Ch('r')) result += Ch('\r');
  45. else if (*b == Ch('t')) result += Ch('\t');
  46. else if (*b == Ch('v')) result += Ch('\v');
  47. else if (*b == Ch('"')) result += Ch('"');
  48. else if (*b == Ch('\'')) result += Ch('\'');
  49. else if (*b == Ch('\\')) result += Ch('\\');
  50. else
  51. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  52. "unknown escape sequence", "", 0));
  53. }
  54. else
  55. result += *b;
  56. ++b;
  57. }
  58. return result;
  59. }
  60. // Advance pointer past whitespace
  61. template<class Ch>
  62. void skip_whitespace(const Ch *&text)
  63. {
  64. using namespace std;
  65. while (isspace(*text))
  66. ++text;
  67. }
  68. // Extract word (whitespace delimited) and advance pointer accordingly
  69. template<class Ch>
  70. std::basic_string<Ch> read_word(const Ch *&text)
  71. {
  72. using namespace std;
  73. skip_whitespace(text);
  74. const Ch *start = text;
  75. while (!isspace(*text) && *text != Ch(';') && *text != Ch('\0'))
  76. ++text;
  77. return expand_escapes(start, text);
  78. }
  79. // Extract line (eol delimited) and advance pointer accordingly
  80. template<class Ch>
  81. std::basic_string<Ch> read_line(const Ch *&text)
  82. {
  83. using namespace std;
  84. skip_whitespace(text);
  85. const Ch *start = text;
  86. while (*text != Ch('\0') && *text != Ch(';'))
  87. ++text;
  88. while (text > start && isspace(*(text - 1)))
  89. --text;
  90. return expand_escapes(start, text);
  91. }
  92. // Extract string (inside ""), and advance pointer accordingly
  93. // Set need_more_lines to true if \ continuator found
  94. template<class Ch>
  95. std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
  96. {
  97. skip_whitespace(text);
  98. if (*text == Ch('\"'))
  99. {
  100. // Skip "
  101. ++text;
  102. // Find end of string, but skip escaped "
  103. bool escaped = false;
  104. const Ch *start = text;
  105. while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
  106. {
  107. escaped = (!escaped && *text == Ch('\\'));
  108. ++text;
  109. }
  110. // If end of string found
  111. if (*text == Ch('\"'))
  112. {
  113. std::basic_string<Ch> result = expand_escapes(start, text++);
  114. skip_whitespace(text);
  115. if (*text == Ch('\\'))
  116. {
  117. if (!need_more_lines)
  118. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  119. "unexpected \\", "", 0));
  120. ++text;
  121. skip_whitespace(text);
  122. if (*text == Ch('\0') || *text == Ch(';'))
  123. *need_more_lines = true;
  124. else
  125. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  126. "expected end of line after \\", "", 0));
  127. }
  128. else
  129. if (need_more_lines)
  130. *need_more_lines = false;
  131. return result;
  132. }
  133. else
  134. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  135. "unexpected end of line", "", 0));
  136. }
  137. else
  138. BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
  139. }
  140. // Extract key
  141. template<class Ch>
  142. std::basic_string<Ch> read_key(const Ch *&text)
  143. {
  144. skip_whitespace(text);
  145. if (*text == Ch('\"'))
  146. return read_string(text, NULL);
  147. else
  148. return read_word(text);
  149. }
  150. // Extract data
  151. template<class Ch>
  152. std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
  153. {
  154. skip_whitespace(text);
  155. if (*text == Ch('\"'))
  156. return read_string(text, need_more_lines);
  157. else
  158. {
  159. *need_more_lines = false;
  160. return read_word(text);
  161. }
  162. }
  163. // Build ptree from info stream
  164. template<class Ptree, class Ch>
  165. void read_info_internal(std::basic_istream<Ch> &stream,
  166. Ptree &pt,
  167. const std::string &filename,
  168. int include_depth)
  169. {
  170. typedef std::basic_string<Ch> str_t;
  171. // Possible parser states
  172. enum state_t {
  173. s_key, // Parser expects key
  174. s_data, // Parser expects data
  175. s_data_cont // Parser expects data continuation
  176. };
  177. unsigned long line_no = 0;
  178. state_t state = s_key; // Parser state
  179. Ptree *last = NULL; // Pointer to last created ptree
  180. // Define line here to minimize reallocations
  181. str_t line;
  182. // Initialize ptree stack (used to handle nesting)
  183. std::stack<Ptree *> stack;
  184. stack.push(&pt); // Push root ptree on stack initially
  185. try {
  186. // While there are characters in the stream
  187. while (stream.good()) {
  188. // Read one line from stream
  189. ++line_no;
  190. std::getline(stream, line);
  191. if (!stream.good() && !stream.eof())
  192. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  193. "read error", filename, line_no));
  194. const Ch *text = line.c_str();
  195. // If directive found
  196. skip_whitespace(text);
  197. if (*text == Ch('#')) {
  198. // Determine directive type
  199. ++text; // skip #
  200. std::basic_string<Ch> directive = read_word(text);
  201. if (directive == convert_chtype<Ch, char>("include")) {
  202. // #include
  203. if (include_depth > 100) {
  204. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  205. "include depth too large, "
  206. "probably recursive include",
  207. filename, line_no));
  208. }
  209. str_t s = read_string(text, NULL);
  210. std::string inc_name =
  211. convert_chtype<char, Ch>(s.c_str());
  212. std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
  213. if (!inc_stream.good())
  214. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  215. "cannot open include file " + inc_name,
  216. filename, line_no));
  217. read_info_internal(inc_stream, *stack.top(),
  218. inc_name, include_depth + 1);
  219. } else { // Unknown directive
  220. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  221. "unknown directive", filename, line_no));
  222. }
  223. // Directive must be followed by end of line
  224. skip_whitespace(text);
  225. if (*text != Ch('\0')) {
  226. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  227. "expected end of line", filename, line_no));
  228. }
  229. // Go to next line
  230. continue;
  231. }
  232. // While there are characters left in line
  233. while (1) {
  234. // Stop parsing on end of line or comment
  235. skip_whitespace(text);
  236. if (*text == Ch('\0') || *text == Ch(';')) {
  237. if (state == s_data) // If there was no data set state to s_key
  238. state = s_key;
  239. break;
  240. }
  241. // Process according to current parser state
  242. switch (state)
  243. {
  244. // Parser expects key
  245. case s_key:
  246. {
  247. if (*text == Ch('{')) // Brace opening found
  248. {
  249. if (!last)
  250. BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
  251. stack.push(last);
  252. last = NULL;
  253. ++text;
  254. }
  255. else if (*text == Ch('}')) // Brace closing found
  256. {
  257. if (stack.size() <= 1)
  258. BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
  259. stack.pop();
  260. last = NULL;
  261. ++text;
  262. }
  263. else // Key text found
  264. {
  265. std::basic_string<Ch> key = read_key(text);
  266. last = &stack.top()->push_back(
  267. std::make_pair(key, Ptree()))->second;
  268. state = s_data;
  269. }
  270. }; break;
  271. // Parser expects data
  272. case s_data:
  273. {
  274. // Last ptree must be defined because we are going to add data to it
  275. BOOST_ASSERT(last);
  276. if (*text == Ch('{')) // Brace opening found
  277. {
  278. stack.push(last);
  279. last = NULL;
  280. ++text;
  281. state = s_key;
  282. }
  283. else if (*text == Ch('}')) // Brace closing found
  284. {
  285. if (stack.size() <= 1)
  286. BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
  287. stack.pop();
  288. last = NULL;
  289. ++text;
  290. state = s_key;
  291. }
  292. else // Data text found
  293. {
  294. bool need_more_lines;
  295. std::basic_string<Ch> data = read_data(text, &need_more_lines);
  296. last->data() = data;
  297. state = need_more_lines ? s_data_cont : s_key;
  298. }
  299. }; break;
  300. // Parser expects continuation of data after \ on previous line
  301. case s_data_cont:
  302. {
  303. // Last ptree must be defined because we are going to update its data
  304. BOOST_ASSERT(last);
  305. if (*text == Ch('\"')) // Continuation must start with "
  306. {
  307. bool need_more_lines;
  308. std::basic_string<Ch> data = read_string(text, &need_more_lines);
  309. last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
  310. state = need_more_lines ? s_data_cont : s_key;
  311. }
  312. else
  313. BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
  314. }; break;
  315. // Should never happen
  316. default:
  317. BOOST_ASSERT(0);
  318. }
  319. }
  320. }
  321. // Check if stack has initial size, otherwise some {'s have not been closed
  322. if (stack.size() != 1)
  323. BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
  324. }
  325. catch (info_parser_error &e)
  326. {
  327. // If line undefined rethrow error with correct filename and line
  328. if (e.line() == 0)
  329. {
  330. BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
  331. }
  332. else
  333. BOOST_PROPERTY_TREE_THROW(e);
  334. }
  335. }
  336. } } }
  337. #endif