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