info_parser_read.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. // Detect whitespace in a not very smart way.
  61. template <class Ch>
  62. bool is_ascii_space(Ch c)
  63. {
  64. // Everything outside ASCII is not space.
  65. unsigned n = c;
  66. if (n > 127)
  67. return false;
  68. return std::isspace(c) != 0;
  69. }
  70. // Advance pointer past whitespace
  71. template<class Ch>
  72. void skip_whitespace(const Ch *&text)
  73. {
  74. using namespace std;
  75. while (is_ascii_space(*text))
  76. ++text;
  77. }
  78. // Extract word (whitespace delimited) and advance pointer accordingly
  79. template<class Ch>
  80. std::basic_string<Ch> read_word(const Ch *&text)
  81. {
  82. using namespace std;
  83. skip_whitespace(text);
  84. const Ch *start = text;
  85. while (!is_ascii_space(*text) && *text != Ch(';') && *text != Ch('\0'))
  86. ++text;
  87. return expand_escapes(start, text);
  88. }
  89. // Extract line (eol delimited) and advance pointer accordingly
  90. template<class Ch>
  91. std::basic_string<Ch> read_line(const Ch *&text)
  92. {
  93. using namespace std;
  94. skip_whitespace(text);
  95. const Ch *start = text;
  96. while (*text != Ch('\0') && *text != Ch(';'))
  97. ++text;
  98. while (text > start && is_ascii_space(*(text - 1)))
  99. --text;
  100. return expand_escapes(start, text);
  101. }
  102. // Extract string (inside ""), and advance pointer accordingly
  103. // Set need_more_lines to true if \ continuator found
  104. template<class Ch>
  105. std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
  106. {
  107. skip_whitespace(text);
  108. if (*text == Ch('\"'))
  109. {
  110. // Skip "
  111. ++text;
  112. // Find end of string, but skip escaped "
  113. bool escaped = false;
  114. const Ch *start = text;
  115. while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
  116. {
  117. escaped = (!escaped && *text == Ch('\\'));
  118. ++text;
  119. }
  120. // If end of string found
  121. if (*text == Ch('\"'))
  122. {
  123. std::basic_string<Ch> result = expand_escapes(start, text++);
  124. skip_whitespace(text);
  125. if (*text == Ch('\\'))
  126. {
  127. if (!need_more_lines)
  128. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  129. "unexpected \\", "", 0));
  130. ++text;
  131. skip_whitespace(text);
  132. if (*text == Ch('\0') || *text == Ch(';'))
  133. *need_more_lines = true;
  134. else
  135. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  136. "expected end of line after \\", "", 0));
  137. }
  138. else
  139. if (need_more_lines)
  140. *need_more_lines = false;
  141. return result;
  142. }
  143. else
  144. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  145. "unexpected end of line", "", 0));
  146. }
  147. else
  148. BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
  149. }
  150. // Extract key
  151. template<class Ch>
  152. std::basic_string<Ch> read_key(const Ch *&text)
  153. {
  154. skip_whitespace(text);
  155. if (*text == Ch('\"'))
  156. return read_string(text, NULL);
  157. else
  158. return read_word(text);
  159. }
  160. // Extract data
  161. template<class Ch>
  162. std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
  163. {
  164. skip_whitespace(text);
  165. if (*text == Ch('\"'))
  166. return read_string(text, need_more_lines);
  167. else
  168. {
  169. *need_more_lines = false;
  170. return read_word(text);
  171. }
  172. }
  173. // Build ptree from info stream
  174. template<class Ptree, class Ch>
  175. void read_info_internal(std::basic_istream<Ch> &stream,
  176. Ptree &pt,
  177. const std::string &filename,
  178. int include_depth)
  179. {
  180. typedef std::basic_string<Ch> str_t;
  181. // Possible parser states
  182. enum state_t {
  183. s_key, // Parser expects key
  184. s_data, // Parser expects data
  185. s_data_cont // Parser expects data continuation
  186. };
  187. unsigned long line_no = 0;
  188. state_t state = s_key; // Parser state
  189. Ptree *last = NULL; // Pointer to last created ptree
  190. // Define line here to minimize reallocations
  191. str_t line;
  192. // Initialize ptree stack (used to handle nesting)
  193. std::stack<Ptree *> stack;
  194. stack.push(&pt); // Push root ptree on stack initially
  195. try {
  196. // While there are characters in the stream
  197. while (stream.good()) {
  198. // Read one line from stream
  199. ++line_no;
  200. std::getline(stream, line);
  201. if (!stream.good() && !stream.eof())
  202. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  203. "read error", filename, line_no));
  204. const Ch *text = line.c_str();
  205. // If directive found
  206. skip_whitespace(text);
  207. if (*text == Ch('#')) {
  208. // Determine directive type
  209. ++text; // skip #
  210. std::basic_string<Ch> directive = read_word(text);
  211. if (directive == convert_chtype<Ch, char>("include")) {
  212. // #include
  213. if (include_depth > 100) {
  214. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  215. "include depth too large, "
  216. "probably recursive include",
  217. filename, line_no));
  218. }
  219. str_t s = read_string(text, NULL);
  220. std::string inc_name =
  221. convert_chtype<char, Ch>(s.c_str());
  222. std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
  223. if (!inc_stream.good())
  224. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  225. "cannot open include file " + inc_name,
  226. filename, line_no));
  227. read_info_internal(inc_stream, *stack.top(),
  228. inc_name, include_depth + 1);
  229. } else { // Unknown directive
  230. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  231. "unknown directive", filename, line_no));
  232. }
  233. // Directive must be followed by end of line
  234. skip_whitespace(text);
  235. if (*text != Ch('\0')) {
  236. BOOST_PROPERTY_TREE_THROW(info_parser_error(
  237. "expected end of line", filename, line_no));
  238. }
  239. // Go to next line
  240. continue;
  241. }
  242. // While there are characters left in line
  243. while (1) {
  244. // Stop parsing on end of line or comment
  245. skip_whitespace(text);
  246. if (*text == Ch('\0') || *text == Ch(';')) {
  247. if (state == s_data) // If there was no data set state to s_key
  248. state = s_key;
  249. break;
  250. }
  251. // Process according to current parser state
  252. switch (state)
  253. {
  254. // Parser expects key
  255. case s_key:
  256. {
  257. if (*text == Ch('{')) // Brace opening found
  258. {
  259. if (!last)
  260. BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
  261. stack.push(last);
  262. last = NULL;
  263. ++text;
  264. }
  265. else if (*text == Ch('}')) // Brace closing found
  266. {
  267. if (stack.size() <= 1)
  268. BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
  269. stack.pop();
  270. last = NULL;
  271. ++text;
  272. }
  273. else // Key text found
  274. {
  275. std::basic_string<Ch> key = read_key(text);
  276. last = &stack.top()->push_back(
  277. std::make_pair(key, Ptree()))->second;
  278. state = s_data;
  279. }
  280. }; break;
  281. // Parser expects data
  282. case s_data:
  283. {
  284. // Last ptree must be defined because we are going to add data to it
  285. BOOST_ASSERT(last);
  286. if (*text == Ch('{')) // Brace opening found
  287. {
  288. stack.push(last);
  289. last = NULL;
  290. ++text;
  291. state = s_key;
  292. }
  293. else if (*text == Ch('}')) // Brace closing found
  294. {
  295. if (stack.size() <= 1)
  296. BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
  297. stack.pop();
  298. last = NULL;
  299. ++text;
  300. state = s_key;
  301. }
  302. else // Data text found
  303. {
  304. bool need_more_lines;
  305. std::basic_string<Ch> data = read_data(text, &need_more_lines);
  306. last->data() = data;
  307. state = need_more_lines ? s_data_cont : s_key;
  308. }
  309. }; break;
  310. // Parser expects continuation of data after \ on previous line
  311. case s_data_cont:
  312. {
  313. // Last ptree must be defined because we are going to update its data
  314. BOOST_ASSERT(last);
  315. if (*text == Ch('\"')) // Continuation must start with "
  316. {
  317. bool need_more_lines;
  318. std::basic_string<Ch> data = read_string(text, &need_more_lines);
  319. last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
  320. state = need_more_lines ? s_data_cont : s_key;
  321. }
  322. else
  323. BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
  324. }; break;
  325. // Should never happen
  326. default:
  327. BOOST_ASSERT(0);
  328. }
  329. }
  330. }
  331. // Check if stack has initial size, otherwise some {'s have not been closed
  332. if (stack.size() != 1)
  333. BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
  334. }
  335. catch (info_parser_error &e)
  336. {
  337. // If line undefined rethrow error with correct filename and line
  338. if (e.line() == 0)
  339. {
  340. BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
  341. }
  342. else
  343. BOOST_PROPERTY_TREE_THROW(e);
  344. }
  345. }
  346. } } }
  347. #endif