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