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