blob: d1375ba6fd4b9ba11beccf52798d0e7db936bb42 [file] [log] [blame]
/*
* Copyright Andrey Semashev 2007 - 2015.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
/*!
* \file format_parser.cpp
* \author Andrey Semashev
* \date 16.11.2012
*
* \brief This header is the Boost.Log library implementation, see the library documentation
* at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
*/
#include <string>
#include <algorithm>
#include <boost/throw_exception.hpp>
#include <boost/exception/exception.hpp>
#include <boost/move/core.hpp>
#include <boost/move/utility.hpp>
#include <boost/spirit/include/qi_uint.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/log/detail/format.hpp>
#include <boost/log/exceptions.hpp>
#include <boost/log/support/exception.hpp>
#include "spirit_encoding.hpp"
#include <boost/log/detail/header.hpp>
namespace qi = boost::spirit::qi;
namespace boost {
BOOST_LOG_OPEN_NAMESPACE
namespace aux {
template< typename CharT >
format_description< CharT > parse_format(const CharT* begin, const CharT* end)
{
typedef CharT char_type;
typedef format_description< char_type > description;
typedef typename encoding< char_type >::type traits;
const char_type* original_begin = begin;
description descr;
unsigned int literal_start_pos = 0;
while (begin != end)
{
const char_type* p = std::find(begin, end, static_cast< char_type >('%'));
descr.literal_chars.append(begin, p);
if ((end - p) >= 2)
{
// Check for a percent placeholder
char_type c = p[1];
if (c == static_cast< char_type >('%'))
{
descr.literal_chars.push_back(static_cast< char_type >('%'));
begin = p + 2;
continue;
}
// From here on, no more literals are possible. Append the literal element.
{
const unsigned int literal_chars_size = static_cast< unsigned int >(descr.literal_chars.size());
if (literal_start_pos < literal_chars_size)
{
descr.format_elements.push_back(format_element::literal(literal_start_pos, literal_chars_size - literal_start_pos));
literal_start_pos = literal_chars_size;
}
}
// Check if this is a positional argument
if (traits::isdigit(c))
{
if (c != static_cast< char_type >('0'))
{
// Positional argument in the form "%N%"
unsigned int n = 0;
const char_type* pp = p + 1;
qi::parse(pp, end, qi::uint_, n);
if (n == 0 || pp == end || *pp != static_cast< char_type >('%'))
{
boost::throw_exception(boost::enable_error_info(parse_error("Invalid positional format placeholder")) << boost::throw_file(__FILE__) << boost::throw_line(__LINE__)
<< boost::log::position_info(static_cast< unsigned int >(p - original_begin))
);
}
// Safety check against ridiculously large argument numbers which would lead to excessive memory consumption.
// This could be useful if the format string is gathered from an external source (e.g. a config file).
if (n > 1000)
{
boost::throw_exception(boost::enable_error_info(limitation_error("Positional format placeholder too big")) << boost::throw_file(__FILE__) << boost::throw_line(__LINE__)
<< boost::log::position_info(static_cast< unsigned int >(p - original_begin))
);
}
// We count positional arguments from 0, not from 1 as in format strings
descr.format_elements.push_back(format_element::positional_argument(n - 1));
begin = pp + 1; // skip the closing '%'
continue;
}
else
{
// This must be the filler character, not supported yet
}
}
// This must be something else, not supported yet
boost::throw_exception(boost::enable_error_info(parse_error("Unsupported format placeholder")) << boost::throw_file(__FILE__) << boost::throw_line(__LINE__)
<< boost::log::position_info(static_cast< unsigned int >(p - original_begin))
);
}
else
{
if (p != end)
descr.literal_chars.push_back(static_cast< char_type >('%')); // a single '%' character at the end of the string
begin = end;
}
}
const unsigned int literal_chars_size = static_cast< unsigned int >(descr.literal_chars.size());
if (literal_start_pos < literal_chars_size)
descr.format_elements.push_back(format_element::literal(literal_start_pos, literal_chars_size - literal_start_pos));
return boost::move(descr);
}
#ifdef BOOST_LOG_USE_CHAR
template BOOST_LOG_API
format_description< char > parse_format(const char* begin, const char* end);
#endif // BOOST_LOG_USE_CHAR
#ifdef BOOST_LOG_USE_WCHAR_T
template BOOST_LOG_API
format_description< wchar_t > parse_format(const wchar_t* begin, const wchar_t* end);
#endif // BOOST_LOG_USE_WCHAR_T
} // namespace aux
BOOST_LOG_CLOSE_NAMESPACE // namespace log
} // namespace boost
#include <boost/log/detail/footer.hpp>