1 | /* |
2 | * Copyright Andrey Semashev 2007 - 2015. |
3 | * Distributed under the Boost Software License, Version 1.0. |
4 | * (See accompanying file LICENSE_1_0.txt or copy at |
5 | * http://www.boost.org/LICENSE_1_0.txt) |
6 | */ |
7 | /*! |
8 | * \file settings_parser.cpp |
9 | * \author Andrey Semashev |
10 | * \date 20.07.2012 |
11 | * |
12 | * \brief This header is the Boost.Log library implementation, see the library documentation |
13 | * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. |
14 | */ |
15 | |
16 | #include <boost/log/detail/setup_config.hpp> |
17 | |
18 | #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS |
19 | |
20 | #include <string> |
21 | #include <locale> |
22 | #include <iostream> |
23 | #include <stdexcept> |
24 | #include <algorithm> |
25 | #include <boost/throw_exception.hpp> |
26 | #include <boost/exception/exception.hpp> |
27 | #include <boost/exception/info.hpp> |
28 | #include <boost/exception/errinfo_at_line.hpp> |
29 | #include <boost/io/ios_state.hpp> |
30 | #include <boost/move/core.hpp> |
31 | #include <boost/move/utility_core.hpp> |
32 | #include <boost/log/detail/code_conversion.hpp> |
33 | #include <boost/log/utility/setup/settings_parser.hpp> |
34 | #include <boost/log/exceptions.hpp> |
35 | #include "parser_utils.hpp" |
36 | #include "spirit_encoding.hpp" |
37 | #include <boost/log/detail/header.hpp> |
38 | |
39 | namespace boost { |
40 | |
41 | BOOST_LOG_OPEN_NAMESPACE |
42 | |
43 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
44 | |
45 | //! Settings parser |
46 | template< typename CharT > |
47 | class settings_parser |
48 | { |
49 | private: |
50 | typedef CharT char_type; |
51 | typedef const char_type* iterator_type; |
52 | typedef typename log::aux::encoding< char_type >::type encoding; |
53 | typedef settings_parser< char_type > this_type; |
54 | |
55 | typedef std::basic_string< char_type > string_type; |
56 | typedef log::aux::char_constants< char_type > constants; |
57 | typedef basic_settings< char_type > settings_type; |
58 | |
59 | private: |
60 | //! Current section name |
61 | std::string m_SectionName; |
62 | //! Current parameter name |
63 | std::string m_ParameterName; |
64 | //! Settings instance |
65 | settings_type& m_Settings; |
66 | //! Locale from the source stream |
67 | std::locale m_Locale; |
68 | //! Current line number |
69 | unsigned int& m_LineCounter; |
70 | |
71 | public: |
72 | //! Constructor |
73 | explicit settings_parser(settings_type& setts, unsigned int& line_counter, std::locale const& loc) : |
74 | m_Settings(setts), |
75 | m_Locale(loc), |
76 | m_LineCounter(line_counter) |
77 | { |
78 | } |
79 | |
80 | //! Parses a line of the input |
81 | void parse_line(iterator_type& begin, iterator_type end) |
82 | { |
83 | iterator_type p = begin; |
84 | p = constants::trim_spaces_left(p, end); |
85 | if (p != end) |
86 | { |
87 | char_type c = *p; |
88 | if (c == constants::char_section_bracket_left) |
89 | { |
90 | // We have a section name |
91 | iterator_type start = ++p; |
92 | start = constants::trim_spaces_left(start, end); |
93 | iterator_type stop = std::find(start, end, constants::char_section_bracket_right); |
94 | if (stop == end) |
95 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section header is invalid" , (m_LineCounter)); |
96 | |
97 | p = stop + 1; |
98 | stop = constants::trim_spaces_right(start, stop); |
99 | |
100 | set_section_name(begin: start, end: stop); |
101 | } |
102 | else if (c != constants::char_comment) |
103 | { |
104 | // We have a parameter |
105 | iterator_type eq = std::find(p, end, constants::char_equal); |
106 | if (eq == end) |
107 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter description is invalid" , (m_LineCounter)); |
108 | |
109 | // Parameter name |
110 | set_parameter_name(begin: p, end: constants::trim_spaces_right(p, eq)); |
111 | |
112 | // Parameter value |
113 | p = constants::trim_spaces_left(eq + 1, end); |
114 | if (p == end || *p == constants::char_comment) |
115 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter value is not specified" , (m_LineCounter)); |
116 | |
117 | try |
118 | { |
119 | string_type value; |
120 | p = constants::parse_operand(p, end, value); |
121 | set_parameter_value(value); |
122 | } |
123 | catch (parse_error& e) |
124 | { |
125 | throw boost::enable_error_info(x: e) << boost::errinfo_at_line(m_LineCounter); |
126 | } |
127 | } |
128 | |
129 | // In the end of the line we may have a comment |
130 | p = constants::trim_spaces_left(p, end); |
131 | if (p != end) |
132 | { |
133 | c = *p; |
134 | if (c == constants::char_comment) |
135 | { |
136 | // The comment spans until the end of the line |
137 | p = end; |
138 | } |
139 | else |
140 | { |
141 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Unexpected characters in the end of the line" , (m_LineCounter)); |
142 | } |
143 | } |
144 | } |
145 | |
146 | begin = p; |
147 | } |
148 | |
149 | private: |
150 | //! The method sets the parsed section name |
151 | void set_section_name(iterator_type begin, iterator_type end) |
152 | { |
153 | // Check that the section name is valid |
154 | if (begin == end) |
155 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section name is empty" , (m_LineCounter)); |
156 | |
157 | for (iterator_type p = begin; p != end; ++p) |
158 | { |
159 | char_type c = *p; |
160 | if (c != constants::char_dot && !encoding::isalnum(c)) |
161 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section name is invalid" , (m_LineCounter)); |
162 | } |
163 | |
164 | m_SectionName = log::aux::to_narrow(string_type(begin, end), m_Locale); |
165 | |
166 | // For compatibility with Boost.Log v1, we replace the "Sink:" prefix with "Sinks." |
167 | // so that all sink parameters are placed in the common Sinks section. |
168 | if (m_SectionName.compare(pos: 0, n1: 5, s: "Sink:" ) == 0) |
169 | m_SectionName = "Sinks." + m_SectionName.substr(pos: 5); |
170 | } |
171 | |
172 | //! The method sets the parsed parameter name |
173 | void set_parameter_name(iterator_type begin, iterator_type end) |
174 | { |
175 | if (m_SectionName.empty()) |
176 | { |
177 | // The parameter encountered before any section starter |
178 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameters are only allowed within sections" , (m_LineCounter)); |
179 | } |
180 | |
181 | // Check that the parameter name is valid |
182 | if (begin == end) |
183 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is empty" , (m_LineCounter)); |
184 | |
185 | iterator_type p = begin; |
186 | if (!encoding::isalpha(*p)) |
187 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is invalid" , (m_LineCounter)); |
188 | for (++p; p != end; ++p) |
189 | { |
190 | char_type c = *p; |
191 | if (!encoding::isgraph(c)) |
192 | BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is invalid" , (m_LineCounter)); |
193 | } |
194 | |
195 | m_ParameterName = log::aux::to_narrow(string_type(begin, end), m_Locale); |
196 | } |
197 | |
198 | //! The method sets the parsed parameter value (non-quoted) |
199 | void set_parameter_value(string_type const& value) |
200 | { |
201 | m_Settings[m_SectionName][m_ParameterName] = value; |
202 | m_ParameterName.clear(); |
203 | } |
204 | |
205 | // Assignment and copying are prohibited |
206 | BOOST_DELETED_FUNCTION(settings_parser(settings_parser const&)) |
207 | BOOST_DELETED_FUNCTION(settings_parser& operator= (settings_parser const&)) |
208 | }; |
209 | |
210 | } // namespace |
211 | |
212 | //! The function parses library settings from an input stream |
213 | template< typename CharT > |
214 | BOOST_LOG_SETUP_API basic_settings< CharT > parse_settings(std::basic_istream< CharT >& strm) |
215 | { |
216 | typedef CharT char_type; |
217 | typedef std::basic_string< char_type > string_type; |
218 | typedef settings_parser< char_type > settings_parser_type; |
219 | typedef basic_settings< char_type > settings_type; |
220 | |
221 | if (!strm.good()) |
222 | BOOST_THROW_EXCEPTION(std::invalid_argument("The input stream for parsing settings is not valid" )); |
223 | |
224 | io::basic_ios_exception_saver< char_type > exceptions_guard(strm, std::ios_base::badbit); |
225 | |
226 | // Engage parsing |
227 | settings_type settings; |
228 | unsigned int line_number = 1; |
229 | std::locale loc = strm.getloc(); |
230 | settings_parser_type parser(settings, line_number, loc); |
231 | |
232 | string_type line; |
233 | while (!strm.eof()) |
234 | { |
235 | std::getline(strm, line); |
236 | |
237 | const char_type* p = line.c_str(); |
238 | parser.parse_line(p, p + line.size()); |
239 | |
240 | line.clear(); |
241 | ++line_number; |
242 | } |
243 | |
244 | return BOOST_LOG_NRVO_RESULT(settings); |
245 | } |
246 | |
247 | |
248 | #ifdef BOOST_LOG_USE_CHAR |
249 | template BOOST_LOG_SETUP_API basic_settings< char > parse_settings< char >(std::basic_istream< char >& strm); |
250 | #endif |
251 | #ifdef BOOST_LOG_USE_WCHAR_T |
252 | template BOOST_LOG_SETUP_API basic_settings< wchar_t > parse_settings< wchar_t >(std::basic_istream< wchar_t >& strm); |
253 | #endif |
254 | |
255 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
256 | |
257 | } // namespace boost |
258 | |
259 | #include <boost/log/detail/footer.hpp> |
260 | |
261 | #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS |
262 | |