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 : snapdev::NOT_USED(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 : snapdev::NOT_USED(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 20 : DECLARE_FUNCTION(max_width)
166 : {
167 12 : snapdev::NOT_USED(msg);
168 :
169 12 : std::int64_t const max_width(p->get_integer());
170 12 : std::int64_t const extra(static_cast<std::int64_t>(d.get_value().length()) - max_width);
171 12 : if(extra > 0)
172 : {
173 12 : char32_t const align(d.get_param("align", U"L")[0]);
174 12 : if(align == U'C')
175 : {
176 1 : d.set_value(d.get_value().substr(extra / 2, max_width));
177 : }
178 11 : else if(align == U'L')
179 : {
180 10 : d.get_value().resize(max_width);
181 : }
182 : else
183 : {
184 1 : d.set_value(d.get_value().substr(extra));
185 : }
186 : }
187 12 : }
188 :
189 :
190 13 : DECLARE_FUNCTION(min_width)
191 : {
192 5 : snapdev::NOT_USED(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 : snapdev::NOT_USED(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 : snapdev::NOT_USED(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 : snapdev::NOT_USED(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 26 : DECLARE_FUNCTION(escape)
304 : {
305 18 : snapdev::NOT_USED(msg);
306 :
307 36 : std::string to_escape(p->get_value());
308 18 : if(to_escape.empty())
309 : {
310 1 : to_escape = "\\\n\r\t";
311 : }
312 :
313 36 : std::u32string e(libutf8::to_u32string(to_escape));
314 36 : std::u32string r;
315 789 : for(auto wc : d.get_value())
316 : {
317 771 : 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 771 : r += wc;
378 : }
379 18 : d.set_value(r);
380 18 : }
381 :
382 :
383 :
384 :
385 :
386 21 : DECLARE_FUNCTION(caps)
387 : {
388 13 : snapdev::NOT_USED(msg, p);
389 :
390 26 : std::u32string r;
391 13 : bool first(true);
392 530 : for(auto wc : d.get_value())
393 : {
394 : // we should look into having a libutf8::isalpha() function
395 : // instead of testing two special cases
396 : //
397 517 : if(std::isspace(wc)
398 455 : || wc == '-')
399 : {
400 75 : first = true;
401 75 : r += wc;
402 : }
403 : else
404 : {
405 : // TODO: make use of boost::locale::to_upper(d); so we benefit
406 : // from the current locale (or something close--how do
407 : // you handle multi-chars in this case?!?)
408 : //
409 442 : if(first)
410 : {
411 78 : r += std::towupper(wc);
412 78 : first = false;
413 : }
414 : else
415 : {
416 364 : r += std::towlower(wc);
417 : }
418 : }
419 : }
420 13 : d.set_value(r);
421 13 : }
422 :
423 9 : DECLARE_FUNCTION(lower)
424 : {
425 1 : snapdev::NOT_USED(msg, p);
426 :
427 : // TODO: make use of boost::locale::to_upper(d); so we benefit from the
428 : // current locale
429 : //
430 1 : std::transform(d.get_value().begin(), d.get_value().end(), d.get_value().begin(), std::towlower);
431 1 : }
432 :
433 19 : DECLARE_FUNCTION(upper)
434 : {
435 11 : snapdev::NOT_USED(msg, p);
436 :
437 : // TODO: make use of boost::locale::to_upper(d); so we benefit from the
438 : // current locale
439 : //
440 11 : std::transform(d.get_value().begin(), d.get_value().end(), d.get_value().begin(), std::towupper);
441 11 : }
442 :
443 :
444 :
445 :
446 : }
447 : // no name namespace
448 :
449 :
450 :
451 :
452 :
453 :
454 :
455 6 : } // snaplogger namespace
456 : // vim: ts=4 sw=4 et
|