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