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   date_time_format_parser.cpp
9  * \author Andrey Semashev
10  * \date   30.09.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 <cstring>
17 #include <string>
18 #include <algorithm>
19 #include <boost/spirit/include/karma_uint.hpp>
20 #include <boost/spirit/include/karma_generate.hpp>
21 #include <boost/range/iterator_range_core.hpp>
22 #include <boost/log/detail/date_time_format_parser.hpp>
23 #include <boost/log/detail/header.hpp>
24 
25 namespace karma = boost::spirit::karma;
26 
27 namespace boost {
28 
29 BOOST_LOG_OPEN_NAMESPACE
30 
31 namespace aux {
32 
33 BOOST_LOG_ANONYMOUS_NAMESPACE {
34 
35 template< typename CharT >
36 struct string_constants;
37 
38 #ifdef BOOST_LOG_USE_CHAR
39 
40 template< >
41 struct string_constants< char >
42 {
43     typedef char char_type;
44 
45     static const char_type* iso_date_format() { return "%Y%m%d"; }
46     static const char_type* extended_iso_date_format() { return "%Y-%m-%d"; }
47 
48     static const char_type* iso_time_format() { return "%H%M%S"; }
49     static const char_type* extended_iso_time_format() { return "%H:%M:%S"; }
50     static const char_type* default_time_format() { return "%H:%M:%S.%f"; }
51 };
52 
53 #endif // BOOST_LOG_USE_CHAR
54 
55 #ifdef BOOST_LOG_USE_WCHAR_T
56 
57 template< >
58 struct string_constants< wchar_t >
59 {
60     typedef wchar_t char_type;
61 
62     static const char_type* iso_date_format() { return L"%Y%m%d"; }
63     static const char_type* extended_iso_date_format() { return L"%Y-%m-%d"; }
64 
65     static const char_type* iso_time_format() { return L"%H%M%S"; }
66     static const char_type* extended_iso_time_format() { return L"%H:%M:%S"; }
67     static const char_type* default_time_format() { return L"%H:%M:%S.%f"; }
68 };
69 
70 #endif // BOOST_LOG_USE_WCHAR_T
71 
72 template< typename CallbackT >
73 struct common_flags
74 {
75     typedef CallbackT callback_type;
76     typedef typename callback_type::char_type char_type;
77     typedef std::basic_string< char_type > string_type;
78 
79     const char_type* parse(const char_type* begin, const char_type* end, callback_type& callback)
80     {
81         switch (begin[1])
82         {
83         case '%':
84             m_literal.push_back('%');
85             break;
86 
87         default:
88             flush(callback);
89             callback.on_placeholder(iterator_range< const char_type* >(begin, begin + 2));
90             break;
91         }
92 
93         return begin + 2;
94     }
95 
96     void add_literal(const char_type* begin, const char_type* end)
97     {
98         m_literal.append(begin, end);
99     }
100 
101     void flush(callback_type& callback)
102     {
103         if (!m_literal.empty())
104         {
105             const char_type* p = m_literal.c_str();
106             callback.on_literal(iterator_range< const char_type* >(p, p + m_literal.size()));
107             m_literal.clear();
108         }
109     }
110 
111 private:
112     string_type m_literal;
113 };
114 
115 template< typename BaseT >
116 struct date_flags :
117     public BaseT
118 {
119     typedef typename BaseT::callback_type callback_type;
120     typedef typename BaseT::char_type char_type;
121 
122     const char_type* parse(const char_type* begin, const char_type* end, callback_type& callback)
123     {
124         typedef string_constants< char_type > constants;
125 
126         switch (begin[1])
127         {
128         case 'Y':
129             {
130                 this->flush(callback);
131 
132                 std::size_t len = end - begin;
133                 if (len >= 8 && std::memcmp(begin, constants::extended_iso_date_format(), 8 * sizeof(char_type)) == 0)
134                 {
135                     callback.on_extended_iso_date();
136                     return begin + 8;
137                 }
138                 else if (len >= 6 && std::memcmp(begin, constants::iso_date_format(), 6 * sizeof(char_type)) == 0)
139                 {
140                     callback.on_iso_date();
141                     return begin + 6;
142                 }
143                 else
144                 {
145                     callback.on_full_year();
146                 }
147             }
148             break;
149 
150         case 'y':
151             this->flush(callback);
152             callback.on_short_year();
153             break;
154 
155         case 'm':
156             this->flush(callback);
157             callback.on_numeric_month();
158             break;
159 
160         case 'B':
161             this->flush(callback);
162             callback.on_full_month();
163             break;
164 
165         case 'b':
166             this->flush(callback);
167             callback.on_short_month();
168             break;
169 
170         case 'd':
171             this->flush(callback);
172             callback.on_month_day(true);
173             break;
174 
175         case 'e':
176             this->flush(callback);
177             callback.on_month_day(false);
178             break;
179 
180         case 'w':
181             this->flush(callback);
182             callback.on_numeric_week_day();
183             break;
184 
185         case 'A':
186             this->flush(callback);
187             callback.on_full_week_day();
188             break;
189 
190         case 'a':
191             this->flush(callback);
192             callback.on_short_week_day();
193             break;
194 
195         default:
196             return BaseT::parse(begin, end, callback);
197         }
198 
199         return begin + 2;
200     }
201 };
202 
203 template< typename BaseT >
204 struct time_flags :
205     public BaseT
206 {
207     typedef typename BaseT::callback_type callback_type;
208     typedef typename BaseT::char_type char_type;
209 
210     const char_type* parse(const char_type* begin, const char_type* end, callback_type& callback)
211     {
212         typedef string_constants< char_type > constants;
213 
214         switch (begin[1])
215         {
216         case 'O':
217         case 'H':
218             {
219                 this->flush(callback);
220 
221                 std::size_t len = end - begin;
222                 if (len >= 11 && std::memcmp(begin, constants::default_time_format(), 11 * sizeof(char_type)) == 0)
223                 {
224                     callback.on_default_time();
225                     return begin + 11;
226                 }
227                 else if (len >= 8 && std::memcmp(begin, constants::extended_iso_time_format(), 8 * sizeof(char_type)) == 0)
228                 {
229                     callback.on_extended_iso_time();
230                     return begin + 8;
231                 }
232                 else if (len >= 6 && std::memcmp(begin, constants::iso_time_format(), 6 * sizeof(char_type)) == 0)
233                 {
234                     callback.on_iso_time();
235                     return begin + 6;
236                 }
237                 else
238                 {
239                     callback.on_hours(true);
240                 }
241             }
242             break;
243 
244         case 'T':
245             this->flush(callback);
246             callback.on_extended_iso_time();
247             break;
248 
249         case 'k':
250             this->flush(callback);
251             callback.on_hours(false);
252             break;
253 
254         case 'I':
255             this->flush(callback);
256             callback.on_hours_12(true);
257             break;
258 
259         case 'l':
260             this->flush(callback);
261             callback.on_hours_12(false);
262             break;
263 
264         case 'M':
265             this->flush(callback);
266             callback.on_minutes();
267             break;
268 
269         case 'S':
270             this->flush(callback);
271             callback.on_seconds();
272             break;
273 
274         case 'f':
275             this->flush(callback);
276             callback.on_fractional_seconds();
277             break;
278 
279         case 'P':
280             this->flush(callback);
281             callback.on_am_pm(false);
282             break;
283 
284         case 'p':
285             this->flush(callback);
286             callback.on_am_pm(true);
287             break;
288 
289         case 'Q':
290             this->flush(callback);
291             callback.on_extended_iso_time_zone();
292             break;
293 
294         case 'q':
295             this->flush(callback);
296             callback.on_iso_time_zone();
297             break;
298 
299         case '-':
300             this->flush(callback);
301             callback.on_duration_sign(false);
302             break;
303 
304         case '+':
305             this->flush(callback);
306             callback.on_duration_sign(true);
307             break;
308 
309         default:
310             return BaseT::parse(begin, end, callback);
311         }
312 
313         return begin + 2;
314     }
315 };
316 
317 template< typename CharT, typename ParserT, typename CallbackT >
318 inline void parse_format(const CharT* begin, const CharT* end, ParserT& parser, CallbackT& callback)
319 {
320     typedef CharT char_type;
321     typedef CallbackT callback_type;
322 
323     while (begin != end)
324     {
325         const char_type* p = std::find(begin, end, static_cast< char_type >('%'));
326         parser.add_literal(begin, p);
327 
328         if ((end - p) >= 2)
329         {
330             begin = parser.parse(p, end, callback);
331         }
332         else
333         {
334             if (p != end)
335                 parser.add_literal(p, end); // a single '%' character at the end of the string
336             begin = end;
337         }
338     }
339 
340     parser.flush(callback);
341 }
342 
343 } // namespace
344 
345 //! Parses the date format string and invokes the callback object
346 template< typename CharT >
347 BOOST_LOG_API void parse_date_format(const CharT* begin, const CharT* end, date_format_parser_callback< CharT >& callback)
348 {
349     typedef CharT char_type;
350     typedef date_format_parser_callback< char_type > callback_type;
351     date_flags< common_flags< callback_type > > parser;
352     parse_format(begin, end, parser, callback);
353 }
354 
355 //! Parses the time format string and invokes the callback object
356 template< typename CharT >
parse_time_format(const CharT * begin,const CharT * end,time_format_parser_callback<CharT> & callback)357 BOOST_LOG_API void parse_time_format(const CharT* begin, const CharT* end, time_format_parser_callback< CharT >& callback)
358 {
359     typedef CharT char_type;
360     typedef time_format_parser_callback< char_type > callback_type;
361     time_flags< common_flags< callback_type > > parser;
362     parse_format(begin, end, parser, callback);
363 }
364 
365 //! Parses the date and time format string and invokes the callback object
366 template< typename CharT >
parse_date_time_format(const CharT * begin,const CharT * end,date_time_format_parser_callback<CharT> & callback)367 BOOST_LOG_API void parse_date_time_format(const CharT* begin, const CharT* end, date_time_format_parser_callback< CharT >& callback)
368 {
369     typedef CharT char_type;
370     typedef date_time_format_parser_callback< char_type > callback_type;
371     date_flags< time_flags< common_flags< callback_type > > > parser;
372     parse_format(begin, end, parser, callback);
373 }
374 
375 template< typename CharT >
put_integer(std::basic_string<CharT> & str,uint32_t value,unsigned int width,CharT fill_char)376 BOOST_LOG_API void put_integer(std::basic_string< CharT >& str, uint32_t value, unsigned int width, CharT fill_char)
377 {
378     typedef CharT char_type;
379     char_type buf[std::numeric_limits< uint32_t >::digits10 + 2];
380     char_type* p = buf;
381 
382     typedef karma::uint_generator< uint32_t, 10 > uint_gen;
383     karma::generate(p, uint_gen(), value);
384     const std::size_t len = p - buf;
385     if (len < width)
386         str.insert(str.end(), width - len, fill_char);
387     str.append(buf, p);
388 }
389 
390 #ifdef BOOST_LOG_USE_CHAR
391 
392 template BOOST_LOG_API
393 void parse_date_format(const char* begin, const char* end, date_format_parser_callback< char >& callback);
394 template BOOST_LOG_API
395 void parse_time_format(const char* begin, const char* end, time_format_parser_callback< char >& callback);
396 template BOOST_LOG_API
397 void parse_date_time_format(const char* begin, const char* end, date_time_format_parser_callback< char >& callback);
398 template BOOST_LOG_API
399 void put_integer(std::basic_string< char >& str, uint32_t value, unsigned int width, char fill_char);
400 
401 #endif // BOOST_LOG_USE_CHAR
402 
403 #ifdef BOOST_LOG_USE_WCHAR_T
404 
405 template BOOST_LOG_API
406 void parse_date_format(const wchar_t* begin, const wchar_t* end, date_format_parser_callback< wchar_t >& callback);
407 template BOOST_LOG_API
408 void parse_time_format(const wchar_t* begin, const wchar_t* end, time_format_parser_callback< wchar_t >& callback);
409 template BOOST_LOG_API
410 void parse_date_time_format(const wchar_t* begin, const wchar_t* end, date_time_format_parser_callback< wchar_t >& callback);
411 template BOOST_LOG_API
412 void put_integer(std::basic_string< wchar_t >& str, uint32_t value, unsigned int width, wchar_t fill_char);
413 
414 #endif // BOOST_LOG_USE_WCHAR_T
415 
416 } // namespace aux
417 
418 BOOST_LOG_CLOSE_NAMESPACE // namespace log
419 
420 } // namespace boost
421 
422 #include <boost/log/detail/footer.hpp>
423