123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- // ----------------------------------------------------------------------------
- // Copyright (C) 2002-2006 Marcin Kalicinski
- //
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // For more information, see www.boost.org
- // ----------------------------------------------------------------------------
- #ifndef BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
- #define BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
- #include "boost/property_tree/ptree.hpp"
- #include "boost/property_tree/detail/info_parser_error.hpp"
- #include "boost/property_tree/detail/info_parser_utils.hpp"
- #include <iterator>
- #include <string>
- #include <stack>
- #include <fstream>
- #include <cctype>
- namespace boost { namespace property_tree { namespace info_parser
- {
- // Expand known escape sequences
- template<class It>
- std::basic_string<typename std::iterator_traits<It>::value_type>
- expand_escapes(It b, It e)
- {
- typedef typename std::iterator_traits<It>::value_type Ch;
- std::basic_string<Ch> result;
- while (b != e)
- {
- if (*b == Ch('\\'))
- {
- ++b;
- if (b == e)
- {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "character expected after backslash", "", 0));
- }
- else if (*b == Ch('0')) result += Ch('\0');
- else if (*b == Ch('a')) result += Ch('\a');
- else if (*b == Ch('b')) result += Ch('\b');
- else if (*b == Ch('f')) result += Ch('\f');
- else if (*b == Ch('n')) result += Ch('\n');
- else if (*b == Ch('r')) result += Ch('\r');
- else if (*b == Ch('t')) result += Ch('\t');
- else if (*b == Ch('v')) result += Ch('\v');
- else if (*b == Ch('"')) result += Ch('"');
- else if (*b == Ch('\'')) result += Ch('\'');
- else if (*b == Ch('\\')) result += Ch('\\');
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unknown escape sequence", "", 0));
- }
- else
- result += *b;
- ++b;
- }
- return result;
- }
-
- // Detect whitespace in a not very smart way.
- template <class Ch>
- bool is_ascii_space(Ch c)
- {
- // Everything outside ASCII is not space.
- unsigned n = c;
- if (n > 127)
- return false;
- return std::isspace(c) != 0;
- }
- // Advance pointer past whitespace
- template<class Ch>
- void skip_whitespace(const Ch *&text)
- {
- using namespace std;
- while (is_ascii_space(*text))
- ++text;
- }
- // Extract word (whitespace delimited) and advance pointer accordingly
- template<class Ch>
- std::basic_string<Ch> read_word(const Ch *&text)
- {
- using namespace std;
- skip_whitespace(text);
- const Ch *start = text;
- while (!is_ascii_space(*text) && *text != Ch(';') && *text != Ch('\0'))
- ++text;
- return expand_escapes(start, text);
- }
- // Extract line (eol delimited) and advance pointer accordingly
- template<class Ch>
- std::basic_string<Ch> read_line(const Ch *&text)
- {
- using namespace std;
- skip_whitespace(text);
- const Ch *start = text;
- while (*text != Ch('\0') && *text != Ch(';'))
- ++text;
- while (text > start && is_ascii_space(*(text - 1)))
- --text;
- return expand_escapes(start, text);
- }
- // Extract string (inside ""), and advance pointer accordingly
- // Set need_more_lines to true if \ continuator found
- template<class Ch>
- std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
- {
- skip_whitespace(text);
- if (*text == Ch('\"'))
- {
- // Skip "
- ++text;
- // Find end of string, but skip escaped "
- bool escaped = false;
- const Ch *start = text;
- while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
- {
- escaped = (!escaped && *text == Ch('\\'));
- ++text;
- }
- // If end of string found
- if (*text == Ch('\"'))
- {
- std::basic_string<Ch> result = expand_escapes(start, text++);
- skip_whitespace(text);
- if (*text == Ch('\\'))
- {
- if (!need_more_lines)
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unexpected \\", "", 0));
- ++text;
- skip_whitespace(text);
- if (*text == Ch('\0') || *text == Ch(';'))
- *need_more_lines = true;
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "expected end of line after \\", "", 0));
- }
- else
- if (need_more_lines)
- *need_more_lines = false;
- return result;
- }
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unexpected end of line", "", 0));
- }
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
- }
- // Extract key
- template<class Ch>
- std::basic_string<Ch> read_key(const Ch *&text)
- {
- skip_whitespace(text);
- if (*text == Ch('\"'))
- return read_string(text, NULL);
- else
- return read_word(text);
- }
- // Extract data
- template<class Ch>
- std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
- {
- skip_whitespace(text);
- if (*text == Ch('\"'))
- return read_string(text, need_more_lines);
- else
- {
- *need_more_lines = false;
- return read_word(text);
- }
- }
- // Build ptree from info stream
- template<class Ptree, class Ch>
- void read_info_internal(std::basic_istream<Ch> &stream,
- Ptree &pt,
- const std::string &filename,
- int include_depth)
- {
- typedef std::basic_string<Ch> str_t;
- // Possible parser states
- enum state_t {
- s_key, // Parser expects key
- s_data, // Parser expects data
- s_data_cont // Parser expects data continuation
- };
- unsigned long line_no = 0;
- state_t state = s_key; // Parser state
- Ptree *last = NULL; // Pointer to last created ptree
- // Define line here to minimize reallocations
- str_t line;
- // Initialize ptree stack (used to handle nesting)
- std::stack<Ptree *> stack;
- stack.push(&pt); // Push root ptree on stack initially
- try {
- // While there are characters in the stream
- while (stream.good()) {
- // Read one line from stream
- ++line_no;
- std::getline(stream, line);
- if (!stream.good() && !stream.eof())
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "read error", filename, line_no));
- const Ch *text = line.c_str();
- // If directive found
- skip_whitespace(text);
- if (*text == Ch('#')) {
- // Determine directive type
- ++text; // skip #
- std::basic_string<Ch> directive = read_word(text);
- if (directive == convert_chtype<Ch, char>("include")) {
- // #include
- if (include_depth > 100) {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "include depth too large, "
- "probably recursive include",
- filename, line_no));
- }
- str_t s = read_string(text, NULL);
- std::string inc_name =
- convert_chtype<char, Ch>(s.c_str());
- std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
- if (!inc_stream.good())
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "cannot open include file " + inc_name,
- filename, line_no));
- read_info_internal(inc_stream, *stack.top(),
- inc_name, include_depth + 1);
- } else { // Unknown directive
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unknown directive", filename, line_no));
- }
- // Directive must be followed by end of line
- skip_whitespace(text);
- if (*text != Ch('\0')) {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "expected end of line", filename, line_no));
- }
- // Go to next line
- continue;
- }
- // While there are characters left in line
- while (1) {
- // Stop parsing on end of line or comment
- skip_whitespace(text);
- if (*text == Ch('\0') || *text == Ch(';')) {
- if (state == s_data) // If there was no data set state to s_key
- state = s_key;
- break;
- }
- // Process according to current parser state
- switch (state)
- {
- // Parser expects key
- case s_key:
- {
- if (*text == Ch('{')) // Brace opening found
- {
- if (!last)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
- stack.push(last);
- last = NULL;
- ++text;
- }
- else if (*text == Ch('}')) // Brace closing found
- {
- if (stack.size() <= 1)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
- stack.pop();
- last = NULL;
- ++text;
- }
- else // Key text found
- {
- std::basic_string<Ch> key = read_key(text);
- last = &stack.top()->push_back(
- std::make_pair(key, Ptree()))->second;
- state = s_data;
- }
- }; break;
- // Parser expects data
- case s_data:
- {
-
- // Last ptree must be defined because we are going to add data to it
- BOOST_ASSERT(last);
-
- if (*text == Ch('{')) // Brace opening found
- {
- stack.push(last);
- last = NULL;
- ++text;
- state = s_key;
- }
- else if (*text == Ch('}')) // Brace closing found
- {
- if (stack.size() <= 1)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
- stack.pop();
- last = NULL;
- ++text;
- state = s_key;
- }
- else // Data text found
- {
- bool need_more_lines;
- std::basic_string<Ch> data = read_data(text, &need_more_lines);
- last->data() = data;
- state = need_more_lines ? s_data_cont : s_key;
- }
- }; break;
- // Parser expects continuation of data after \ on previous line
- case s_data_cont:
- {
-
- // Last ptree must be defined because we are going to update its data
- BOOST_ASSERT(last);
-
- if (*text == Ch('\"')) // Continuation must start with "
- {
- bool need_more_lines;
- std::basic_string<Ch> data = read_string(text, &need_more_lines);
- last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
- state = need_more_lines ? s_data_cont : s_key;
- }
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
- }; break;
- // Should never happen
- default:
- BOOST_ASSERT(0);
- }
- }
- }
- // Check if stack has initial size, otherwise some {'s have not been closed
- if (stack.size() != 1)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
- }
- catch (info_parser_error &e)
- {
- // If line undefined rethrow error with correct filename and line
- if (e.line() == 0)
- {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
- }
- else
- BOOST_PROPERTY_TREE_THROW(e);
- }
- }
- } } }
- #endif
|