Line data Source code
1 : /*
2 : * Copyright (c) 2013-2021 Made to Order Software Corp. All Rights Reserved
3 : *
4 : * https://snapwebsites.org/project/snaplogger
5 : * contact@m2osw.com
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 2 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * This program is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License along
18 : * with this program; if not, write to the Free Software Foundation, Inc.,
19 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 : */
21 :
22 : /** \file
23 : * \brief Variables are used to dynamically add parameters to log messages.
24 : *
25 : * This file declares the base variable class.
26 : *
27 : * The format defines \em functions which are written as in
28 : * `${function-name}`.
29 : *
30 : * Parameters can be passed to these functions by adding `:<param>`
31 : * to those definitions. These are named parameters and their default
32 : * value is "present" or not. A specific value can be assignd using
33 : * the equal sign as in `:param=<value>`.
34 : *
35 : * For example, the date function can be called as follow:
36 : *
37 : * \code
38 : * ${date:year:align=right:exact_width=2}
39 : * \endcode
40 : *
41 : * The `year` parameter is specific to the `date` function. The other
42 : * parameters are available whatever the function. This variable asks
43 : * to truncate the year to 2 character right aligned (i.e. "18" in
44 : * "2018".)
45 : *
46 : * In C, this would look something like:
47 : *
48 : * \code
49 : * date(FLAG_YEAR, ALIGN_RIGHT, 2);
50 : * \endcode
51 : */
52 :
53 :
54 : // self
55 : //
56 : #include "snaplogger/exception.h"
57 : #include "snaplogger/guard.h"
58 : #include "snaplogger/variable.h"
59 :
60 :
61 : // libutf8 lib
62 : //
63 : #include <libutf8/libutf8.h>
64 :
65 :
66 : // snapdev lib
67 : //
68 : #include <snapdev/not_used.h>
69 :
70 :
71 : // C++ lib
72 : //
73 : #include <algorithm>
74 : #include <iostream>
75 : #include <queue>
76 :
77 :
78 : // last include
79 : //
80 : #include <snapdev/poison.h>
81 :
82 :
83 :
84 : namespace snaplogger
85 : {
86 :
87 :
88 :
89 : namespace
90 : {
91 :
92 :
93 :
94 : //
95 : // Change Parameters
96 : //
97 :
98 :
99 28 : DECLARE_FUNCTION(padding)
100 : {
101 20 : snap::NOTUSED(msg);
102 :
103 40 : std::u32string pad;
104 20 : if(p->get_type() == param::type_t::TYPE_STRING)
105 : {
106 18 : pad = libutf8::to_u32string(p->get_value());
107 : }
108 : else
109 : {
110 2 : std::int64_t const digit(p->get_integer());
111 2 : if(digit < 0 || digit > 9)
112 : {
113 : throw invalid_parameter(
114 : "the ${...:padding=<value>} when set to a number must be one digit ('0' to '9'), not \""
115 2 : + std::to_string(digit)
116 3 : + "\".");
117 : }
118 1 : pad = libutf8::to_u32string(std::to_string(digit));
119 : }
120 19 : if(pad.length() == 1)
121 : {
122 18 : d.set_param(std::string("padding"), pad);
123 : }
124 : else
125 : {
126 : throw invalid_parameter(
127 : "the ${...:padding=' '} must be exactly one character, not \""
128 2 : + p->get_value()
129 3 : + "\".");
130 : }
131 18 : }
132 :
133 :
134 26 : DECLARE_FUNCTION(align)
135 : {
136 18 : snap::NOTUSED(msg);
137 :
138 18 : if(p->get_value() == "left")
139 : {
140 4 : d.set_param("align", "L");
141 : }
142 13 : else if(p->get_value() == "right")
143 : {
144 6 : d.set_param("align", "R");
145 : }
146 7 : else if(p->get_value() == "center")
147 : {
148 6 : d.set_param("align", "C");
149 : }
150 : else
151 : {
152 : throw invalid_parameter("the ${...:align=left|center|right} was expected, got \""
153 2 : + p->get_value()
154 3 : + "\".");
155 : }
156 16 : }
157 :
158 :
159 :
160 :
161 :
162 : //
163 : // Apply Functions
164 : //
165 :
166 :
167 11 : DECLARE_FUNCTION(max_width)
168 : {
169 3 : snap::NOTUSED(msg);
170 :
171 3 : std::int64_t const max_width(p->get_integer());
172 3 : std::int64_t const extra(static_cast<std::int64_t>(d.get_value().length()) - max_width);
173 3 : if(extra > 0)
174 : {
175 3 : char32_t const align(d.get_param("align", U"L")[0]);
176 3 : if(align == U'C')
177 : {
178 1 : d.set_value(d.get_value().substr(extra / 2, max_width));
179 : }
180 2 : else if(align == U'L')
181 : {
182 1 : d.get_value().resize(max_width);
183 : }
184 : else
185 : {
186 1 : d.set_value(d.get_value().substr(extra));
187 : }
188 : }
189 3 : }
190 :
191 :
192 13 : DECLARE_FUNCTION(min_width)
193 : {
194 5 : snap::NOTUSED(msg);
195 :
196 5 : std::int64_t const min_width(p->get_integer());
197 4 : std::int64_t const pad(min_width - static_cast<std::int64_t>(d.get_value().length()));
198 4 : if(pad > 0)
199 : {
200 4 : char32_t const padding_char(d.get_param("padding", U" ")[0]);
201 4 : char32_t const align(d.get_param("align", U"L")[0]);
202 4 : if(align == U'C')
203 : {
204 4 : std::u32string const padding(pad / 2, padding_char);
205 2 : d.set_value(padding + d.get_value() + padding);
206 2 : if((pad & 1) != 0)
207 : {
208 2 : d.get_value() += padding_char;
209 : }
210 : }
211 : else
212 : {
213 4 : std::u32string const padding(pad, padding_char);
214 2 : if(align == U'L')
215 : {
216 1 : d.get_value() += padding;
217 : }
218 : else
219 : {
220 1 : d.set_value(padding + d.get_value());
221 : }
222 : }
223 : }
224 4 : }
225 :
226 :
227 17 : DECLARE_FUNCTION(exact_width)
228 : {
229 9 : snap::NOTUSED(msg);
230 :
231 9 : std::int64_t const exact_width(p->get_integer());
232 :
233 9 : std::int64_t const pad(exact_width - static_cast<std::int64_t>(d.get_value().length()));
234 9 : char32_t const align(d.get_param("align", U"L")[0]);
235 9 : if(pad > 0)
236 : {
237 6 : char32_t const padding_char(d.get_param("padding", U" ")[0]);
238 6 : if(align == U'C')
239 : {
240 4 : std::u32string const padding(pad / 2, padding_char);
241 2 : d.set_value(padding + d.get_value() + padding);
242 2 : if((pad & 1) != 0)
243 : {
244 1 : d.get_value() += padding_char;
245 : }
246 : }
247 : else
248 : {
249 8 : std::u32string const padding(pad, padding_char);
250 4 : if(align == U'L')
251 : {
252 1 : d.get_value() += padding;
253 : }
254 : else
255 : {
256 3 : d.set_value(padding + d.get_value());
257 : }
258 : }
259 6 : return;
260 : }
261 :
262 3 : if(pad < 0)
263 : {
264 3 : if(align == U'C')
265 : {
266 1 : d.set_value(d.get_value().substr(-pad / 2, exact_width));
267 : }
268 2 : else if(align == U'L')
269 : {
270 1 : d.get_value().resize(exact_width);
271 : }
272 : else
273 : {
274 1 : d.set_value(d.get_value().substr(-pad));
275 : }
276 : }
277 : }
278 :
279 :
280 :
281 :
282 :
283 9 : DECLARE_FUNCTION(append)
284 : {
285 1 : snap::NOTUSED(msg);
286 :
287 2 : std::string const append_utf8(p->get_value());
288 2 : std::u32string const str(libutf8::to_u32string(append_utf8));
289 1 : d.get_value() += str;
290 1 : }
291 :
292 9 : DECLARE_FUNCTION(prepend)
293 : {
294 1 : snap::NOTUSED(msg);
295 :
296 2 : std::string const prepend_utf8(p->get_value());
297 2 : std::u32string const prepend(libutf8::to_u32string(prepend_utf8));
298 1 : d.set_value(prepend + d.get_value());
299 1 : }
300 :
301 :
302 :
303 :
304 :
305 17 : DECLARE_FUNCTION(escape)
306 : {
307 9 : snap::NOTUSED(msg);
308 :
309 18 : std::string to_escape(p->get_value());
310 9 : if(to_escape.empty())
311 : {
312 1 : to_escape = "\\\n\r\t";
313 : }
314 :
315 18 : std::u32string e(libutf8::to_u32string(to_escape));
316 18 : std::u32string r;
317 464 : for(auto wc : d.get_value())
318 : {
319 455 : if(e.find(wc) != std::u32string::npos)
320 : {
321 25 : if(wc >= 0x80 && wc < 0xA0)
322 : {
323 : // "graphical controls"
324 : //
325 2 : r += '@';
326 2 : wc -= 0x40;
327 : }
328 23 : else if(wc < 0x20)
329 : {
330 11 : switch(wc)
331 : {
332 1 : case '\a':
333 1 : r += '\\';
334 1 : wc = U'a';
335 1 : break;
336 :
337 1 : case '\b':
338 1 : r += '\\';
339 1 : wc = U'b';
340 1 : break;
341 :
342 1 : case '\f':
343 1 : r += '\\';
344 1 : wc = U'f';
345 1 : break;
346 :
347 2 : case '\n':
348 2 : r += '\\';
349 2 : wc = U'n';
350 2 : break;
351 :
352 2 : case '\r':
353 2 : r += '\\';
354 2 : wc = U'r';
355 2 : break;
356 :
357 2 : case '\t':
358 2 : r += '\\';
359 2 : wc = U't';
360 2 : break;
361 :
362 1 : case '\v':
363 1 : r += '\\';
364 1 : wc = U'v';
365 1 : break;
366 :
367 1 : default:
368 1 : r += '^';
369 1 : wc += 0x40;
370 1 : break;
371 :
372 : }
373 : }
374 : else
375 : {
376 12 : r += '\\';
377 : }
378 : }
379 455 : r += wc;
380 : }
381 9 : d.set_value(r);
382 9 : }
383 :
384 :
385 :
386 :
387 :
388 12 : DECLARE_FUNCTION(caps)
389 : {
390 4 : snap::NOTUSED(msg);
391 4 : snap::NOTUSED(p);
392 :
393 8 : std::u32string r;
394 4 : bool first(true);
395 215 : for(auto wc : d.get_value())
396 : {
397 : // we should look into having a libutf8::isalpha() function
398 : // instead of testing two special cases
399 : //
400 211 : if(std::isspace(wc)
401 185 : || wc == '-')
402 : {
403 30 : first = true;
404 30 : r += wc;
405 : }
406 : else
407 : {
408 : // TODO: make use of boost::locale::to_upper(d); so we benefit
409 : // from the current locale (or something close--how do
410 : // you handle multi-chars in this case?!?)
411 : //
412 181 : if(first)
413 : {
414 33 : r += std::towupper(wc);
415 33 : first = false;
416 : }
417 : else
418 : {
419 148 : r += std::towlower(wc);
420 : }
421 : }
422 : }
423 4 : d.set_value(r);
424 4 : }
425 :
426 9 : DECLARE_FUNCTION(lower)
427 : {
428 1 : snap::NOTUSED(msg);
429 1 : snap::NOTUSED(p);
430 :
431 : // TODO: make use of boost::locale::to_upper(d); so we benefit from the
432 : // current locale
433 : //
434 1 : std::transform(d.get_value().begin(), d.get_value().end(), d.get_value().begin(), std::towlower);
435 1 : }
436 :
437 10 : DECLARE_FUNCTION(upper)
438 : {
439 2 : snap::NOTUSED(msg);
440 2 : snap::NOTUSED(p);
441 :
442 : // TODO: make use of boost::locale::to_upper(d); so we benefit from the
443 : // current locale
444 : //
445 2 : std::transform(d.get_value().begin(), d.get_value().end(), d.get_value().begin(), std::towupper);
446 2 : }
447 :
448 :
449 :
450 :
451 : }
452 : // no name namespace
453 :
454 :
455 :
456 :
457 :
458 :
459 :
460 6 : } // snaplogger namespace
461 : // vim: ts=4 sw=4 et
462 :
|