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 parser_utils.cpp
9 * \author Andrey Semashev
10 * \date 31.03.2008
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 <cctype>
20 #include <iterator>
21 #include <algorithm>
22 #include <boost/log/exceptions.hpp>
23 #include "parser_utils.hpp"
24 #include <boost/log/detail/header.hpp>
25 #ifdef BOOST_LOG_USE_WCHAR_T
26 #include <cwctype>
27 #endif
28
29 namespace boost {
30
31 BOOST_LOG_OPEN_NAMESPACE
32
33 namespace aux {
34
35 #ifdef BOOST_LOG_USE_CHAR
36
37 #ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
38
39 const char_constants< char >::char_type char_constants< char >::char_comment;
40 const char_constants< char >::char_type char_constants< char >::char_comma;
41 const char_constants< char >::char_type char_constants< char >::char_dot;
42 const char_constants< char >::char_type char_constants< char >::char_quote;
43 const char_constants< char >::char_type char_constants< char >::char_percent;
44 const char_constants< char >::char_type char_constants< char >::char_exclamation;
45 const char_constants< char >::char_type char_constants< char >::char_and;
46 const char_constants< char >::char_type char_constants< char >::char_or;
47 const char_constants< char >::char_type char_constants< char >::char_equal;
48 const char_constants< char >::char_type char_constants< char >::char_greater;
49 const char_constants< char >::char_type char_constants< char >::char_less;
50 const char_constants< char >::char_type char_constants< char >::char_underline;
51 const char_constants< char >::char_type char_constants< char >::char_backslash;
52 const char_constants< char >::char_type char_constants< char >::char_section_bracket_left;
53 const char_constants< char >::char_type char_constants< char >::char_section_bracket_right;
54 const char_constants< char >::char_type char_constants< char >::char_paren_bracket_left;
55 const char_constants< char >::char_type char_constants< char >::char_paren_bracket_right;
56
57 #endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
58
59 //! Skips spaces in the beginning of the input
trim_spaces_left(const char_type * begin,const char_type * end)60 const char* char_constants< char >::trim_spaces_left(const char_type* begin, const char_type* end)
61 {
62 using namespace std;
63 while (begin != end && isspace(*begin))
64 ++begin;
65 return begin;
66 }
67
68 //! Skips spaces in the end of the input
trim_spaces_right(const char_type * begin,const char_type * end)69 const char* char_constants< char >::trim_spaces_right(const char_type* begin, const char_type* end)
70 {
71 using namespace std;
72 while (begin != end && isspace(*(end - 1)))
73 --end;
74 return end;
75 }
76
77 //! Scans for the attribute name placeholder in the input
scan_attr_placeholder(const char_type * begin,const char_type * end)78 const char* char_constants< char >::scan_attr_placeholder(const char_type* begin, const char_type* end)
79 {
80 using namespace std;
81 while (begin != end)
82 {
83 char_type c = *begin;
84 if (!isalnum(c) && c != char_underline)
85 break;
86 ++begin;
87 }
88
89 return begin;
90 }
91
92 //! Parses an operand string (possibly quoted) from the input
parse_operand(const char_type * begin,const char_type * end,string_type & operand)93 const char* char_constants< char >::parse_operand(const char_type* begin, const char_type* end, string_type& operand)
94 {
95 using namespace std; // to make sure we can use C functions unqualified
96
97 const char_type* p = begin;
98 if (p == end)
99 BOOST_LOG_THROW_DESCR(parse_error, "Operand value is empty");
100
101 char_type c = *p;
102 if (c == char_quote)
103 {
104 // The value is specified as a quoted string
105 const char_type* start = ++p;
106 for (; p != end; ++p)
107 {
108 c = *p;
109 if (c == char_quote)
110 {
111 break;
112 }
113 else if (c == char_backslash)
114 {
115 ++p;
116 if (p == end)
117 BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the argument value");
118 }
119 }
120 if (p == end)
121 BOOST_LOG_THROW_DESCR(parse_error, "Unterminated quoted string in the argument value");
122
123 operand.assign(start, p);
124 translate_escape_sequences(operand);
125
126 ++p; // skip the closing quote
127 }
128 else
129 {
130 // The value is specified as a single word
131 const char_type* start = p;
132 for (++p; p != end; ++p)
133 {
134 c = *p;
135 if (!isalnum(c) && c != '_' && c != '-' && c != '+' && c != '.')
136 break;
137 }
138
139 operand.assign(start, p);
140 }
141
142 return p;
143 }
144
145 //! Converts escape sequences to the corresponding characters
translate_escape_sequences(string_type & str)146 void char_constants< char >::translate_escape_sequences(string_type& str)
147 {
148 using namespace std; // to make sure we can use C functions unqualified
149
150 string_type::iterator it = str.begin();
151 while (it != str.end())
152 {
153 it = std::find(it, str.end(), '\\');
154 if (std::distance(it, str.end()) >= 2)
155 {
156 it = str.erase(it);
157 switch (*it)
158 {
159 case 'n':
160 *it = '\n'; break;
161 case 'r':
162 *it = '\r'; break;
163 case 'a':
164 *it = '\a'; break;
165 case '\\':
166 ++it; break;
167 case 't':
168 *it = '\t'; break;
169 case 'b':
170 *it = '\b'; break;
171 case 'x':
172 {
173 string_type::iterator b = it;
174 if (std::distance(++b, str.end()) >= 2)
175 {
176 char_type c1 = *b++, c2 = *b++;
177 if (isxdigit(c1) && isxdigit(c2))
178 {
179 *it++ = char_type((to_number(c1) << 4) | to_number(c2));
180 it = str.erase(it, b);
181 }
182 }
183 break;
184 }
185 default:
186 {
187 if (*it >= '0' && *it <= '7')
188 {
189 string_type::iterator b = it;
190 int c = (*b++) - '0';
191 if (*b >= '0' && *b <= '7')
192 c = c * 8 + (*b++) - '0';
193 if (*b >= '0' && *b <= '7')
194 c = c * 8 + (*b++) - '0';
195
196 *it++ = char_type(c);
197 it = str.erase(it, b);
198 }
199 break;
200 }
201 }
202 }
203 }
204 }
205
206 #endif // BOOST_LOG_USE_CHAR
207
208 #ifdef BOOST_LOG_USE_WCHAR_T
209
210 #ifndef BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
211
212 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_comment;
213 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_comma;
214 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_dot;
215 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_quote;
216 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_percent;
217 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_exclamation;
218 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_and;
219 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_or;
220 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_equal;
221 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_greater;
222 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_less;
223 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_underline;
224 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_backslash;
225 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_section_bracket_left;
226 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_section_bracket_right;
227 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_paren_bracket_left;
228 const char_constants< wchar_t >::char_type char_constants< wchar_t >::char_paren_bracket_right;
229
230 #endif // BOOST_LOG_BROKEN_STATIC_CONSTANTS_LINKAGE
231
232 //! Skips spaces in the beginning of the input
trim_spaces_left(const char_type * begin,const char_type * end)233 const wchar_t* char_constants< wchar_t >::trim_spaces_left(const char_type* begin, const char_type* end)
234 {
235 using namespace std;
236 while (begin != end && iswspace(*begin))
237 ++begin;
238 return begin;
239 }
240
241 //! Skips spaces in the end of the input
trim_spaces_right(const char_type * begin,const char_type * end)242 const wchar_t* char_constants< wchar_t >::trim_spaces_right(const char_type* begin, const char_type* end)
243 {
244 using namespace std;
245 while (begin != end && iswspace(*(end - 1)))
246 --end;
247 return end;
248 }
249
250 //! Scans for the attribute name placeholder in the input
scan_attr_placeholder(const char_type * begin,const char_type * end)251 const wchar_t* char_constants< wchar_t >::scan_attr_placeholder(const char_type* begin, const char_type* end)
252 {
253 using namespace std;
254 while (begin != end)
255 {
256 char_type c = *begin;
257 if (!iswalnum(c) && c != char_underline)
258 break;
259 ++begin;
260 }
261
262 return begin;
263 }
264
265 //! Parses an operand string (possibly quoted) from the input
parse_operand(const char_type * begin,const char_type * end,string_type & operand)266 const wchar_t* char_constants< wchar_t >::parse_operand(const char_type* begin, const char_type* end, string_type& operand)
267 {
268 using namespace std; // to make sure we can use C functions unqualified
269
270 const char_type* p = begin;
271 if (p == end)
272 BOOST_LOG_THROW_DESCR(parse_error, "Operand value is empty");
273
274 char_type c = *p;
275 if (c == char_quote)
276 {
277 // The value is specified as a quoted string
278 const char_type* start = ++p;
279 for (; p != end; ++p)
280 {
281 c = *p;
282 if (c == char_quote)
283 {
284 break;
285 }
286 else if (c == char_backslash)
287 {
288 ++p;
289 if (p == end)
290 BOOST_LOG_THROW_DESCR(parse_error, "Invalid escape sequence in the argument value");
291 }
292 }
293 if (p == end)
294 BOOST_LOG_THROW_DESCR(parse_error, "Unterminated quoted string in the argument value");
295
296 operand.assign(start, p);
297 translate_escape_sequences(operand);
298
299 ++p; // skip the closing quote
300 }
301 else
302 {
303 // The value is specified as a single word
304 const char_type* start = p;
305 for (++p; p != end; ++p)
306 {
307 c = *p;
308 if (!iswalnum(c) && c != L'_' && c != L'-' && c != L'+' && c != L'.')
309 break;
310 }
311
312 operand.assign(start, p);
313 }
314
315 return p;
316 }
317
318 //! Converts escape sequences to the corresponding characters
translate_escape_sequences(string_type & str)319 void char_constants< wchar_t >::translate_escape_sequences(string_type& str)
320 {
321 using namespace std; // to make sure we can use C functions unqualified
322
323 string_type::iterator it = str.begin();
324 while (it != str.end())
325 {
326 it = std::find(it, str.end(), L'\\');
327 if (std::distance(it, str.end()) >= 2)
328 {
329 it = str.erase(it);
330 switch (*it)
331 {
332 case L'n':
333 *it = L'\n'; break;
334 case L'r':
335 *it = L'\r'; break;
336 case L'a':
337 *it = L'\a'; break;
338 case L'\\':
339 ++it; break;
340 case L't':
341 *it = L'\t'; break;
342 case L'b':
343 *it = L'\b'; break;
344 case L'x':
345 {
346 string_type::iterator b = it;
347 if (std::distance(++b, str.end()) >= 2)
348 {
349 char_type c1 = *b++, c2 = *b++;
350 if (iswxdigit(c1) && iswxdigit(c2))
351 {
352 *it++ = char_type((to_number(c1) << 4) | to_number(c2));
353 it = str.erase(it, b);
354 }
355 }
356 break;
357 }
358 case L'u':
359 {
360 string_type::iterator b = it;
361 if (std::distance(++b, str.end()) >= 4)
362 {
363 char_type c1 = *b++, c2 = *b++, c3 = *b++, c4 = *b++;
364 if (iswxdigit(c1) && iswxdigit(c2) && iswxdigit(c3) && iswxdigit(c4))
365 {
366 *it++ = char_type(
367 (to_number(c1) << 12) |
368 (to_number(c2) << 8) |
369 (to_number(c3) << 4) |
370 to_number(c4));
371 it = str.erase(it, b);
372 }
373 }
374 break;
375 }
376 case L'U':
377 {
378 string_type::iterator b = it;
379 if (std::distance(++b, str.end()) >= 8)
380 {
381 char_type c1 = *b++, c2 = *b++, c3 = *b++, c4 = *b++;
382 char_type c5 = *b++, c6 = *b++, c7 = *b++, c8 = *b++;
383 if (iswxdigit(c1) && iswxdigit(c2) && iswxdigit(c3) && iswxdigit(c4) &&
384 iswxdigit(c5) && iswxdigit(c6) && iswxdigit(c7) && iswxdigit(c8))
385 {
386 *it++ = char_type(
387 (to_number(c1) << 28) |
388 (to_number(c2) << 24) |
389 (to_number(c3) << 20) |
390 (to_number(c4) << 16) |
391 (to_number(c5) << 12) |
392 (to_number(c6) << 8) |
393 (to_number(c7) << 4) |
394 to_number(c8));
395 it = str.erase(it, b);
396 }
397 }
398 break;
399 }
400 default:
401 {
402 if (*it >= L'0' && *it <= L'7')
403 {
404 string_type::iterator b = it;
405 int c = (*b++) - L'0';
406 if (*b >= L'0' && *b <= L'7')
407 c = c * 8 + (*b++) - L'0';
408 if (*b >= L'0' && *b <= L'7')
409 c = c * 8 + (*b++) - L'0';
410
411 *it++ = char_type(c);
412 it = str.erase(it, b);
413 }
414 break;
415 }
416 }
417 }
418 }
419 }
420
421 #endif // BOOST_LOG_USE_WCHAR_T
422
423 } // namespace aux
424
425 BOOST_LOG_CLOSE_NAMESPACE // namespace log
426
427 } // namespace boost
428
429 #include <boost/log/detail/footer.hpp>
430
431 #endif // BOOST_LOG_WITHOUT_SETTINGS_PARSERS
432