summaryrefslogtreecommitdiff
path: root/libs/log/src/format_parser.cpp
blob: 4d3223fcfe52301c7cd72892c328a8d982eb68b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 *          Copyright Andrey Semashev 2007 - 2014.
 * 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>