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 BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
11 #define 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 boost
{ 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("character expected after backslash", "", 0));
41 else if (*b
== Ch('0')) result
+= Ch('\0');
42 else if (*b
== Ch('a')) result
+= Ch('\a');
43 else if (*b
== Ch('b')) result
+= Ch('\b');
44 else if (*b
== Ch('f')) result
+= Ch('\f');
45 else if (*b
== Ch('n')) result
+= Ch('\n');
46 else if (*b
== Ch('r')) result
+= Ch('\r');
47 else if (*b
== Ch('t')) result
+= Ch('\t');
48 else if (*b
== Ch('v')) result
+= Ch('\v');
49 else if (*b
== Ch('"')) result
+= Ch('"');
50 else if (*b
== Ch('\'')) result
+= Ch('\'');
51 else if (*b
== Ch('\\')) result
+= Ch('\\');
53 BOOST_PROPERTY_TREE_THROW(info_parser_error("unknown escape sequence", "", 0));
62 // Advance pointer past whitespace
64 void skip_whitespace(const Ch
*&text
)
67 while (isspace(*text
))
71 // Extract word (whitespace delimited) and advance pointer accordingly
73 std::basic_string
<Ch
> read_word(const Ch
*&text
)
76 skip_whitespace(text
);
77 const Ch
*start
= text
;
78 while (!isspace(*text
) && *text
!= Ch(';') && *text
!= Ch('\0'))
80 return expand_escapes(start
, text
);
83 // Extract line (eol delimited) and advance pointer accordingly
85 std::basic_string
<Ch
> read_line(const Ch
*&text
)
88 skip_whitespace(text
);
89 const Ch
*start
= text
;
90 while (*text
!= Ch('\0') && *text
!= Ch(';'))
92 while (text
> start
&& isspace(*(text
- 1)))
94 return expand_escapes(start
, text
);
97 // Extract string (inside ""), and advance pointer accordingly
98 // Set need_more_lines to true if \ continuator found
100 std::basic_string
<Ch
> read_string(const Ch
*&text
, bool *need_more_lines
)
102 skip_whitespace(text
);
103 if (*text
== Ch('\"'))
109 // Find end of string, but skip escaped "
110 bool escaped
= false;
111 const Ch
*start
= text
;
112 while ((escaped
|| *text
!= Ch('\"')) && *text
!= Ch('\0'))
114 escaped
= (!escaped
&& *text
== Ch('\\'));
118 // If end of string found
119 if (*text
== Ch('\"'))
121 std::basic_string
<Ch
> result
= expand_escapes(start
, text
++);
122 skip_whitespace(text
);
123 if (*text
== Ch('\\'))
125 if (!need_more_lines
)
126 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected \\", "", 0));
128 skip_whitespace(text
);
129 if (*text
== Ch('\0') || *text
== Ch(';'))
130 *need_more_lines
= true;
132 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected end of line after \\", "", 0));
136 *need_more_lines
= false;
140 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected end of line", "", 0));
144 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
149 std::basic_string
<Ch
> read_key(const Ch
*&text
)
151 skip_whitespace(text
);
152 if (*text
== Ch('\"'))
153 return read_string(text
, NULL
);
155 return read_word(text
);
160 std::basic_string
<Ch
> read_data(const Ch
*&text
, bool *need_more_lines
)
162 skip_whitespace(text
);
163 if (*text
== Ch('\"'))
164 return read_string(text
, need_more_lines
);
167 *need_more_lines
= false;
168 return read_word(text
);
172 // Build ptree from info stream
173 template<class Ptree
>
174 void read_info_internal(std::basic_istream
<typename
Ptree::key_type::value_type
> &stream
,
176 const std::string
&filename
,
181 typedef typename
Ptree::key_type::value_type Ch
;
183 // Possible parser states
185 s_key
, // Parser expects key
186 s_data
, // Parser expects data
187 s_data_cont
// Parser expects data continuation
190 unsigned long line_no
= 0;
191 state_t state
= s_key
; // Parser state
192 Ptree
*last
= NULL
; // Pointer to last created ptree
193 std::basic_string
<Ch
> line
; // Define line here to minimize reallocations
195 // Initialize ptree stack (used to handle nesting)
196 std::stack
<Ptree
*> stack
;
197 stack
.push(&pt
); // Push root ptree on stack initially
202 // While there are characters in the stream
203 while (stream
.good())
206 // Read one line from stream
208 std::getline(stream
, line
);
209 if (!stream
.good() && !stream
.eof())
210 BOOST_PROPERTY_TREE_THROW(info_parser_error("read error", "", 0));
211 const Ch
*text
= line
.c_str();
213 // If directive found
214 skip_whitespace(text
);
215 if (*text
== Ch('#'))
218 // Determine directive type
220 std::basic_string
<Ch
> directive
= read_word(text
);
221 if (directive
== convert_chtype
<Ch
, char>("include")) // #include
223 if (include_depth
> 100)
224 BOOST_PROPERTY_TREE_THROW(info_parser_error("include depth too large, probably recursive include", "", 0));
225 std::basic_string
<Ch
> s
= read_string(text
, NULL
);
226 std::string inc_name
= convert_chtype
<char, Ch
>(s
.c_str());
227 std::basic_ifstream
<Ch
> inc_stream(inc_name
.c_str());
228 if (!inc_stream
.good())
229 BOOST_PROPERTY_TREE_THROW(info_parser_error("cannot open include file " + inc_name
, "", 0));
230 read_info_internal(inc_stream
, *stack
.top(), inc_name
, include_depth
+ 1);
232 else // Unknown directive
233 BOOST_PROPERTY_TREE_THROW(info_parser_error("unknown directive", "", 0));
235 // Directive must be followed by end of line
236 skip_whitespace(text
);
237 if (*text
!= Ch('\0'))
238 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected end of line", "", 0));
245 // While there are characters left in line
249 // Stop parsing on end of line or comment
250 skip_whitespace(text
);
251 if (*text
== Ch('\0') || *text
== Ch(';'))
253 if (state
== s_data
) // If there was no data set state to s_key
258 // Process according to current parser state
262 // Parser expects key
266 if (*text
== Ch('{')) // Brace opening found
269 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
274 else if (*text
== Ch('}')) // Brace closing found
276 if (stack
.size() <= 1)
277 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
282 else // Key text found
284 std::basic_string
<Ch
> key
= read_key(text
);
285 last
= &stack
.top()->push_back(std::make_pair(key
, Ptree()))->second
;
291 // Parser expects data
295 // Last ptree must be defined because we are going to add data to it
298 if (*text
== Ch('{')) // Brace opening found
305 else if (*text
== Ch('}')) // Brace closing found
307 if (stack
.size() <= 1)
308 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
314 else // Data text found
316 bool need_more_lines
;
317 std::basic_string
<Ch
> data
= read_data(text
, &need_more_lines
);
319 state
= need_more_lines
? s_data_cont
: s_key
;
325 // Parser expects continuation of data after \ on previous line
329 // Last ptree must be defined because we are going to update its data
332 if (*text
== Ch('\"')) // Continuation must start with "
334 bool need_more_lines
;
335 std::basic_string
<Ch
> data
= read_string(text
, &need_more_lines
);
336 last
->put_value(last
->template get_value
<std::basic_string
<Ch
> >() + data
);
337 state
= need_more_lines
? s_data_cont
: s_key
;
340 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
344 // Should never happen
352 // Check if stack has initial size, otherwise some {'s have not been closed
353 if (stack
.size() != 1)
354 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
357 catch (info_parser_error
&e
)
359 // If line undefined rethrow error with correct filename and line
362 BOOST_PROPERTY_TREE_THROW(info_parser_error(e
.message(), filename
, line_no
));
365 BOOST_PROPERTY_TREE_THROW(e
);