1
// ----------------------------------------------------------------------------
2
// Copyright (C) 2002-2006 Marcin Kalicinski
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)
8
// For more information, see www.boost.org
9
// ----------------------------------------------------------------------------
10
#ifndef LIBLAS_BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
11
#define LIBLAS_BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
13
#include "boost/property_tree/ptree.hpp"
14
#include "boost/property_tree/detail/info_parser_error.hpp"
15
#include "boost/property_tree/detail/info_parser_utils.hpp"
22
namespace liblas { namespace property_tree { namespace info_parser
25
// Expand known escape sequences
27
std::basic_string<typename std::iterator_traits<It>::value_type>
28
expand_escapes(It b, It e)
30
typedef typename std::iterator_traits<It>::value_type Ch;
31
std::basic_string<Ch> result;
39
BOOST_PROPERTY_TREE_THROW(info_parser_error(
40
"character expected after backslash", "", 0));
42
else if (*b == Ch('0')) result += Ch('\0');
43
else if (*b == Ch('a')) result += Ch('\a');
44
else if (*b == Ch('b')) result += Ch('\b');
45
else if (*b == Ch('f')) result += Ch('\f');
46
else if (*b == Ch('n')) result += Ch('\n');
47
else if (*b == Ch('r')) result += Ch('\r');
48
else if (*b == Ch('t')) result += Ch('\t');
49
else if (*b == Ch('v')) result += Ch('\v');
50
else if (*b == Ch('"')) result += Ch('"');
51
else if (*b == Ch('\'')) result += Ch('\'');
52
else if (*b == Ch('\\')) result += Ch('\\');
54
BOOST_PROPERTY_TREE_THROW(info_parser_error(
55
"unknown escape sequence", "", 0));
64
// Advance pointer past whitespace
66
void skip_whitespace(const Ch *&text)
69
while (isspace(*text))
73
// Extract word (whitespace delimited) and advance pointer accordingly
75
std::basic_string<Ch> read_word(const Ch *&text)
78
skip_whitespace(text);
79
const Ch *start = text;
80
while (!isspace(*text) && *text != Ch(';') && *text != Ch('\0'))
82
return expand_escapes(start, text);
85
// Extract line (eol delimited) and advance pointer accordingly
87
std::basic_string<Ch> read_line(const Ch *&text)
90
skip_whitespace(text);
91
const Ch *start = text;
92
while (*text != Ch('\0') && *text != Ch(';'))
94
while (text > start && isspace(*(text - 1)))
96
return expand_escapes(start, text);
99
// Extract string (inside ""), and advance pointer accordingly
100
// Set need_more_lines to true if \ continuator found
102
std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
104
skip_whitespace(text);
105
if (*text == Ch('\"'))
111
// Find end of string, but skip escaped "
112
bool escaped = false;
113
const Ch *start = text;
114
while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
116
escaped = (!escaped && *text == Ch('\\'));
120
// If end of string found
121
if (*text == Ch('\"'))
123
std::basic_string<Ch> result = expand_escapes(start, text++);
124
skip_whitespace(text);
125
if (*text == Ch('\\'))
127
if (!need_more_lines)
128
BOOST_PROPERTY_TREE_THROW(info_parser_error(
129
"unexpected \\", "", 0));
131
skip_whitespace(text);
132
if (*text == Ch('\0') || *text == Ch(';'))
133
*need_more_lines = true;
135
BOOST_PROPERTY_TREE_THROW(info_parser_error(
136
"expected end of line after \\", "", 0));
140
*need_more_lines = false;
144
BOOST_PROPERTY_TREE_THROW(info_parser_error(
145
"unexpected end of line", "", 0));
149
BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
154
std::basic_string<Ch> read_key(const Ch *&text)
156
skip_whitespace(text);
157
if (*text == Ch('\"'))
158
return read_string(text, NULL);
160
return read_word(text);
165
std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
167
skip_whitespace(text);
168
if (*text == Ch('\"'))
169
return read_string(text, need_more_lines);
172
*need_more_lines = false;
173
return read_word(text);
177
// Build ptree from info stream
178
template<class Ptree, class Ch>
179
void read_info_internal(std::basic_istream<Ch> &stream,
181
const std::string &filename,
184
typedef std::basic_string<Ch> str_t;
185
// Possible parser states
187
s_key, // Parser expects key
188
s_data, // Parser expects data
189
s_data_cont // Parser expects data continuation
192
unsigned long line_no = 0;
193
state_t state = s_key; // Parser state
194
Ptree *last = NULL; // Pointer to last created ptree
195
// Define line here to minimize reallocations
198
// Initialize ptree stack (used to handle nesting)
199
std::stack<Ptree *> stack;
200
stack.push(&pt); // Push root ptree on stack initially
203
// While there are characters in the stream
204
while (stream.good()) {
205
// Read one line from stream
207
std::getline(stream, line);
208
if (!stream.good() && !stream.eof())
209
BOOST_PROPERTY_TREE_THROW(info_parser_error(
210
"read error", filename, line_no));
211
const Ch *text = line.c_str();
213
// If directive found
214
skip_whitespace(text);
215
if (*text == Ch('#')) {
216
// Determine directive type
218
std::basic_string<Ch> directive = read_word(text);
219
if (directive == convert_chtype<Ch, char>("include")) {
221
if (include_depth > 100) {
222
BOOST_PROPERTY_TREE_THROW(info_parser_error(
223
"include depth too large, "
224
"probably recursive include",
227
str_t s = read_string(text, NULL);
228
std::string inc_name =
229
convert_chtype<char, Ch>(s.c_str());
230
std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
231
if (!inc_stream.good())
232
BOOST_PROPERTY_TREE_THROW(info_parser_error(
233
"cannot open include file " + inc_name,
235
read_info_internal(inc_stream, *stack.top(),
236
inc_name, include_depth + 1);
237
} else { // Unknown directive
238
BOOST_PROPERTY_TREE_THROW(info_parser_error(
239
"unknown directive", filename, line_no));
242
// Directive must be followed by end of line
243
skip_whitespace(text);
244
if (*text != Ch('\0')) {
245
BOOST_PROPERTY_TREE_THROW(info_parser_error(
246
"expected end of line", filename, line_no));
253
// While there are characters left in line
256
// Stop parsing on end of line or comment
257
skip_whitespace(text);
258
if (*text == Ch('\0') || *text == Ch(';')) {
259
if (state == s_data) // If there was no data set state to s_key
264
// Process according to current parser state
268
// Parser expects key
272
if (*text == Ch('{')) // Brace opening found
275
BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
280
else if (*text == Ch('}')) // Brace closing found
282
if (stack.size() <= 1)
283
BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
288
else // Key text found
290
std::basic_string<Ch> key = read_key(text);
291
last = &stack.top()->push_back(
292
std::make_pair(key, Ptree()))->second;
298
// Parser expects data
302
// Last ptree must be defined because we are going to add data to it
305
if (*text == Ch('{')) // Brace opening found
312
else if (*text == Ch('}')) // Brace closing found
314
if (stack.size() <= 1)
315
BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
321
else // Data text found
323
bool need_more_lines;
324
std::basic_string<Ch> data = read_data(text, &need_more_lines);
326
state = need_more_lines ? s_data_cont : s_key;
332
// Parser expects continuation of data after \ on previous line
336
// Last ptree must be defined because we are going to update its data
339
if (*text == Ch('\"')) // Continuation must start with "
341
bool need_more_lines;
342
std::basic_string<Ch> data = read_string(text, &need_more_lines);
343
last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
344
state = need_more_lines ? s_data_cont : s_key;
347
BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
351
// Should never happen
359
// Check if stack has initial size, otherwise some {'s have not been closed
360
if (stack.size() != 1)
361
BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
364
catch (info_parser_error &e)
366
// If line undefined rethrow error with correct filename and line
369
BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
372
BOOST_PROPERTY_TREE_THROW(e);