File indexing completed on 2025-01-18 09:50:16
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
0011 #define BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
0012
0013 #include "boost/property_tree/ptree.hpp"
0014 #include "boost/property_tree/detail/info_parser_error.hpp"
0015 #include "boost/property_tree/detail/info_parser_utils.hpp"
0016 #include <iterator>
0017 #include <string>
0018 #include <stack>
0019 #include <fstream>
0020 #include <cctype>
0021
0022 namespace boost { namespace property_tree { namespace info_parser
0023 {
0024
0025
0026 template<class It>
0027 std::basic_string<typename std::iterator_traits<It>::value_type>
0028 expand_escapes(It b, It e)
0029 {
0030 typedef typename std::iterator_traits<It>::value_type Ch;
0031 std::basic_string<Ch> result;
0032 while (b != e)
0033 {
0034 if (*b == Ch('\\'))
0035 {
0036 ++b;
0037 if (b == e)
0038 {
0039 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0040 "character expected after backslash", "", 0));
0041 }
0042 else if (*b == Ch('0')) result += Ch('\0');
0043 else if (*b == Ch('a')) result += Ch('\a');
0044 else if (*b == Ch('b')) result += Ch('\b');
0045 else if (*b == Ch('f')) result += Ch('\f');
0046 else if (*b == Ch('n')) result += Ch('\n');
0047 else if (*b == Ch('r')) result += Ch('\r');
0048 else if (*b == Ch('t')) result += Ch('\t');
0049 else if (*b == Ch('v')) result += Ch('\v');
0050 else if (*b == Ch('"')) result += Ch('"');
0051 else if (*b == Ch('\'')) result += Ch('\'');
0052 else if (*b == Ch('\\')) result += Ch('\\');
0053 else
0054 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0055 "unknown escape sequence", "", 0));
0056 }
0057 else
0058 result += *b;
0059 ++b;
0060 }
0061 return result;
0062 }
0063
0064
0065 template <class Ch>
0066 bool is_ascii_space(Ch c)
0067 {
0068
0069 unsigned n = c;
0070 if (n > 127)
0071 return false;
0072 return std::isspace(c) != 0;
0073 }
0074
0075
0076 template<class Ch>
0077 void skip_whitespace(const Ch *&text)
0078 {
0079 using namespace std;
0080 while (is_ascii_space(*text))
0081 ++text;
0082 }
0083
0084
0085 template<class Ch>
0086 std::basic_string<Ch> read_word(const Ch *&text)
0087 {
0088 using namespace std;
0089 skip_whitespace(text);
0090 const Ch *start = text;
0091 while (!is_ascii_space(*text) && *text != Ch(';') && *text != Ch('\0'))
0092 ++text;
0093 return expand_escapes(start, text);
0094 }
0095
0096
0097 template<class Ch>
0098 std::basic_string<Ch> read_line(const Ch *&text)
0099 {
0100 using namespace std;
0101 skip_whitespace(text);
0102 const Ch *start = text;
0103 while (*text != Ch('\0') && *text != Ch(';'))
0104 ++text;
0105 while (text > start && is_ascii_space(*(text - 1)))
0106 --text;
0107 return expand_escapes(start, text);
0108 }
0109
0110
0111
0112 template<class Ch>
0113 std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
0114 {
0115 skip_whitespace(text);
0116 if (*text == Ch('\"'))
0117 {
0118
0119
0120 ++text;
0121
0122
0123 bool escaped = false;
0124 const Ch *start = text;
0125 while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
0126 {
0127 escaped = (!escaped && *text == Ch('\\'));
0128 ++text;
0129 }
0130
0131
0132 if (*text == Ch('\"'))
0133 {
0134 std::basic_string<Ch> result = expand_escapes(start, text++);
0135 skip_whitespace(text);
0136 if (*text == Ch('\\'))
0137 {
0138 if (!need_more_lines)
0139 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0140 "unexpected \\", "", 0));
0141 ++text;
0142 skip_whitespace(text);
0143 if (*text == Ch('\0') || *text == Ch(';'))
0144 *need_more_lines = true;
0145 else
0146 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0147 "expected end of line after \\", "", 0));
0148 }
0149 else
0150 if (need_more_lines)
0151 *need_more_lines = false;
0152 return result;
0153 }
0154 else
0155 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0156 "unexpected end of line", "", 0));
0157
0158 }
0159 else
0160 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
0161 }
0162
0163
0164 template<class Ch>
0165 std::basic_string<Ch> read_key(const Ch *&text)
0166 {
0167 skip_whitespace(text);
0168 if (*text == Ch('\"'))
0169 return read_string(text, NULL);
0170 else
0171 return read_word(text);
0172 }
0173
0174
0175 template<class Ch>
0176 std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
0177 {
0178 skip_whitespace(text);
0179 if (*text == Ch('\"'))
0180 return read_string(text, need_more_lines);
0181 else
0182 {
0183 *need_more_lines = false;
0184 return read_word(text);
0185 }
0186 }
0187
0188
0189 template<class Ptree, class Ch>
0190 void read_info_internal(std::basic_istream<Ch> &stream,
0191 Ptree &pt,
0192 const std::string &filename,
0193 int include_depth)
0194 {
0195 typedef std::basic_string<Ch> str_t;
0196
0197 enum state_t {
0198 s_key,
0199 s_data,
0200 s_data_cont
0201 };
0202
0203 unsigned long line_no = 0;
0204 state_t state = s_key;
0205 Ptree *last = NULL;
0206
0207 str_t line;
0208
0209
0210 std::stack<Ptree *> stack;
0211 stack.push(&pt);
0212
0213 try {
0214
0215 while (stream.good()) {
0216
0217 ++line_no;
0218 std::getline(stream, line);
0219 if (!stream.good() && !stream.eof())
0220 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0221 "read error", filename, line_no));
0222 const Ch *text = line.c_str();
0223
0224
0225 skip_whitespace(text);
0226 if (*text == Ch('#')) {
0227
0228 ++text;
0229 std::basic_string<Ch> directive = read_word(text);
0230 if (directive == convert_chtype<Ch, char>("include")) {
0231
0232 if (include_depth > 100) {
0233 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0234 "include depth too large, "
0235 "probably recursive include",
0236 filename, line_no));
0237 }
0238 str_t s = read_string(text, NULL);
0239 std::string inc_name =
0240 convert_chtype<char, Ch>(s.c_str());
0241 std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
0242 if (!inc_stream.good())
0243 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0244 "cannot open include file " + inc_name,
0245 filename, line_no));
0246 read_info_internal(inc_stream, *stack.top(),
0247 inc_name, include_depth + 1);
0248 } else {
0249 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0250 "unknown directive", filename, line_no));
0251 }
0252
0253
0254 skip_whitespace(text);
0255 if (*text != Ch('\0')) {
0256 BOOST_PROPERTY_TREE_THROW(info_parser_error(
0257 "expected end of line", filename, line_no));
0258 }
0259
0260
0261 continue;
0262 }
0263
0264
0265 while (1) {
0266
0267
0268 skip_whitespace(text);
0269 if (*text == Ch('\0') || *text == Ch(';')) {
0270 if (state == s_data)
0271 state = s_key;
0272 break;
0273 }
0274
0275
0276 switch (state)
0277 {
0278
0279
0280 case s_key:
0281 {
0282
0283 if (*text == Ch('{'))
0284 {
0285 if (!last)
0286 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
0287 stack.push(last);
0288 last = NULL;
0289 ++text;
0290 }
0291 else if (*text == Ch('}'))
0292 {
0293 if (stack.size() <= 1)
0294 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
0295 stack.pop();
0296 last = NULL;
0297 ++text;
0298 }
0299 else
0300 {
0301 std::basic_string<Ch> key = read_key(text);
0302 last = &stack.top()->push_back(
0303 std::make_pair(key, Ptree()))->second;
0304 state = s_data;
0305 }
0306
0307 }; break;
0308
0309
0310 case s_data:
0311 {
0312
0313
0314 BOOST_ASSERT(last);
0315
0316 if (*text == Ch('{'))
0317 {
0318 stack.push(last);
0319 last = NULL;
0320 ++text;
0321 state = s_key;
0322 }
0323 else if (*text == Ch('}'))
0324 {
0325 if (stack.size() <= 1)
0326 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
0327 stack.pop();
0328 last = NULL;
0329 ++text;
0330 state = s_key;
0331 }
0332 else
0333 {
0334 bool need_more_lines;
0335 std::basic_string<Ch> data = read_data(text, &need_more_lines);
0336 last->data() = data;
0337 state = need_more_lines ? s_data_cont : s_key;
0338 }
0339
0340
0341 }; break;
0342
0343
0344 case s_data_cont:
0345 {
0346
0347
0348 BOOST_ASSERT(last);
0349
0350 if (*text == Ch('\"'))
0351 {
0352 bool need_more_lines;
0353 std::basic_string<Ch> data = read_string(text, &need_more_lines);
0354 last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
0355 state = need_more_lines ? s_data_cont : s_key;
0356 }
0357 else
0358 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
0359
0360 }; break;
0361
0362
0363 default:
0364 BOOST_ASSERT(0);
0365
0366 }
0367 }
0368 }
0369
0370
0371 if (stack.size() != 1)
0372 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
0373
0374 }
0375 catch (info_parser_error &e)
0376 {
0377
0378 if (e.line() == 0)
0379 {
0380 BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
0381 }
0382 else
0383 BOOST_PROPERTY_TREE_THROW(e);
0384
0385 }
0386
0387 }
0388
0389 } } }
0390
0391 #endif