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