Line data Source code
1 : /*
2 : * Copyright (c) 2013-2019 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 : // C++ lib
67 : //
68 : #include <iostream>
69 : #include <queue>
70 :
71 :
72 : // last include
73 : //
74 : #include <snapdev/poison.h>
75 :
76 :
77 :
78 : namespace snaplogger
79 : {
80 :
81 :
82 :
83 : namespace
84 : {
85 :
86 :
87 :
88 : //
89 : // Change Parameters
90 : //
91 :
92 :
93 12 : DECLARE_FUNCTION(padding)
94 : {
95 4 : snap::NOTUSED(msg);
96 :
97 8 : std::u32string pad;
98 4 : if(p->get_type() == param::type_t::TYPE_STRING)
99 : {
100 3 : pad = libutf8::to_u32string(p->get_value());
101 : }
102 : else
103 : {
104 1 : std::int64_t const digit(p->get_integer());
105 1 : if(digit < 0 || digit > 9)
106 : {
107 : throw invalid_parameter(
108 : "the ${...:padding=' '} when set to a number must be one digit ('0' to '9'), not \""
109 0 : + p->get_value()
110 0 : + "\".");
111 : }
112 1 : pad = libutf8::to_u32string(std::to_string(digit));
113 : }
114 4 : if(pad.length() == 1)
115 : {
116 4 : d.set_param(std::string("padding"), pad);
117 : }
118 : else
119 : {
120 : throw invalid_parameter(
121 : "the ${...:padding=' '} must be exactly one character, not \""
122 0 : + p->get_value()
123 0 : + "\".");
124 : }
125 4 : }
126 :
127 :
128 12 : DECLARE_FUNCTION(align)
129 : {
130 4 : snap::NOTUSED(msg);
131 :
132 4 : if(p->get_value() == "left")
133 : {
134 1 : d.set_param("align", "L");
135 : }
136 2 : else if(p->get_value() == "right")
137 : {
138 2 : d.set_param("align", "R");
139 : }
140 0 : else if(p->get_value() == "center")
141 : {
142 0 : d.set_param("align", "C");
143 : }
144 : else
145 : {
146 : throw invalid_parameter("the ${...:align=left|right} was expected, got \""
147 0 : + p->get_value()
148 0 : + "\".");
149 : }
150 3 : }
151 :
152 :
153 :
154 :
155 :
156 : //
157 : // Apply Functions
158 : //
159 :
160 :
161 9 : DECLARE_FUNCTION(max_width)
162 : {
163 1 : snap::NOTUSED(msg);
164 :
165 1 : std::int64_t const max_width(p->get_integer());
166 1 : std::int64_t const extra(static_cast<std::int64_t>(d.get_value().length()) - max_width);
167 1 : if(extra > 0)
168 : {
169 1 : char32_t const align(d.get_param("align", U"L")[0]);
170 1 : if(align == U'C')
171 : {
172 0 : d.set_value(d.get_value().substr(extra / 2, max_width));
173 : }
174 1 : else if(align == U'L')
175 : {
176 1 : d.get_value().resize(max_width);
177 : }
178 : else
179 : {
180 0 : d.set_value(d.get_value().substr(extra));
181 : }
182 : }
183 1 : }
184 :
185 :
186 11 : DECLARE_FUNCTION(min_width)
187 : {
188 3 : snap::NOTUSED(msg);
189 :
190 3 : std::int64_t const min_width(p->get_integer());
191 2 : std::int64_t const pad(min_width - static_cast<std::int64_t>(d.get_value().length()));
192 2 : if(pad > 0)
193 : {
194 2 : char32_t const padding_char(d.get_param("padding", U" ")[0]);
195 2 : char32_t const align(d.get_param("align", U"L")[0]);
196 2 : if(align == U'C')
197 : {
198 0 : std::u32string const padding(pad / 2, padding_char);
199 0 : d.set_value(padding + d.get_value() + padding);
200 0 : if((pad & 1) != 0)
201 : {
202 0 : d.get_value() += padding_char;
203 : }
204 : }
205 : else
206 : {
207 4 : std::u32string const padding(pad, padding_char);
208 2 : if(align == U'L')
209 : {
210 0 : d.get_value() += padding;
211 : }
212 : else
213 : {
214 2 : d.set_value(padding + d.get_value());
215 : }
216 : }
217 : }
218 2 : }
219 :
220 :
221 8 : DECLARE_FUNCTION(exact_width)
222 : {
223 0 : snap::NOTUSED(msg);
224 :
225 0 : std::int64_t const exact_width(p->get_integer());
226 :
227 0 : std::int64_t const pad(exact_width - static_cast<std::int64_t>(d.get_value().length()));
228 0 : char32_t const align(d.get_param("align", U"L")[0]);
229 0 : if(pad > 0)
230 : {
231 0 : char32_t const padding_char(d.get_param("padding", U" ")[0]);
232 0 : if(align == U'C')
233 : {
234 0 : std::u32string const padding(pad / 2, padding_char);
235 0 : d.set_value(padding + d.get_value() + padding);
236 0 : if((pad & 1) != 0)
237 : {
238 0 : d.get_value() += padding_char;
239 : }
240 : }
241 : else
242 : {
243 0 : std::u32string const padding(pad, padding_char);
244 0 : if(align == U'L')
245 : {
246 0 : d.get_value() += padding;
247 : }
248 : else
249 : {
250 0 : d.set_value(padding + d.get_value());
251 : }
252 : }
253 0 : return;
254 : }
255 :
256 0 : if(pad < 0)
257 : {
258 0 : if(align == U'C')
259 : {
260 0 : d.set_value(d.get_value().substr(-pad / 2, exact_width));
261 : }
262 0 : else if(align == U'L')
263 : {
264 0 : d.get_value().resize(exact_width);
265 : }
266 : else
267 : {
268 0 : d.set_value(d.get_value().substr(-pad));
269 : }
270 : }
271 : }
272 :
273 :
274 :
275 :
276 :
277 8 : DECLARE_FUNCTION(append)
278 : {
279 0 : snap::NOTUSED(msg);
280 :
281 0 : std::string const append_utf8(p->get_value());
282 0 : std::u32string const str(libutf8::to_u32string(append_utf8));
283 0 : d.get_value() += str;
284 0 : }
285 :
286 8 : DECLARE_FUNCTION(prepend)
287 : {
288 0 : snap::NOTUSED(msg);
289 :
290 0 : std::string const prepend_utf8(p->get_value());
291 0 : std::u32string const prepend(libutf8::to_u32string(prepend_utf8));
292 0 : d.set_value(prepend + d.get_value());
293 0 : }
294 :
295 :
296 :
297 :
298 :
299 10 : DECLARE_FUNCTION(escape)
300 : {
301 2 : snap::NOTUSED(msg);
302 :
303 4 : std::string to_escape(p->get_value());
304 2 : if(to_escape.empty())
305 : {
306 0 : to_escape = "\\\n\r\t";
307 : }
308 :
309 4 : std::u32string e(libutf8::to_u32string(to_escape));
310 4 : std::u32string r;
311 80 : for(auto wc : d.get_value())
312 : {
313 78 : if(e.find(wc) != std::u32string::npos)
314 : {
315 2 : if(wc >= 0x80 && wc < 0xA0)
316 : {
317 : // "graphical controls"
318 : //
319 0 : r += '@';
320 0 : wc -= 0x40;
321 : }
322 2 : else if(wc < 0x20)
323 : {
324 0 : switch(wc)
325 : {
326 : case '\a':
327 0 : r += '\\';
328 0 : wc = U'a';
329 0 : break;
330 :
331 : case '\b':
332 0 : r += '\\';
333 0 : wc = U'b';
334 0 : break;
335 :
336 : case '\f':
337 0 : r += '\\';
338 0 : wc = U'f';
339 0 : break;
340 :
341 : case '\n':
342 0 : r += '\\';
343 0 : wc = U'n';
344 0 : break;
345 :
346 : case '\r':
347 0 : r += '\\';
348 0 : wc = U'r';
349 0 : break;
350 :
351 : case '\t':
352 0 : r += '\\';
353 0 : wc = U't';
354 0 : break;
355 :
356 : case '\v':
357 0 : r += '\\';
358 0 : wc = U'v';
359 0 : break;
360 :
361 : default:
362 0 : r += '^';
363 0 : wc += 0x40;
364 0 : break;
365 :
366 : }
367 : }
368 : else
369 : {
370 2 : r += '\\';
371 : }
372 : }
373 78 : r += wc;
374 : }
375 2 : d.set_value(r);
376 2 : }
377 :
378 :
379 :
380 :
381 :
382 8 : DECLARE_FUNCTION(caps)
383 : {
384 0 : snap::NOTUSED(msg);
385 0 : snap::NOTUSED(p);
386 :
387 0 : std::u32string r;
388 0 : bool first(true);
389 0 : for(auto wc : d.get_value())
390 : {
391 : // we should look into having a libutf8::isalpha() function
392 : // instead of testing two special cases
393 : //
394 0 : if(std::isspace(wc)
395 0 : || wc == '-')
396 : {
397 0 : first = true;
398 : }
399 : else
400 : {
401 0 : if(first)
402 : {
403 0 : r += std::towupper(wc);
404 0 : first = false;
405 : }
406 : else
407 : {
408 0 : r += std::towlower(wc);
409 : }
410 : }
411 : }
412 0 : d.set_value(r);
413 0 : }
414 :
415 8 : DECLARE_FUNCTION(lower)
416 : {
417 0 : snap::NOTUSED(msg);
418 0 : snap::NOTUSED(p);
419 :
420 0 : std::transform(d.get_value().begin(), d.get_value().end(), d.get_value().begin(), std::towlower);
421 0 : }
422 :
423 8 : DECLARE_FUNCTION(upper)
424 : {
425 0 : snap::NOTUSED(msg);
426 0 : snap::NOTUSED(p);
427 :
428 0 : std::transform(d.get_value().begin(), d.get_value().end(), d.get_value().begin(), std::towupper);
429 0 : }
430 :
431 :
432 :
433 :
434 : }
435 : // no name namespace
436 :
437 :
438 :
439 :
440 :
441 :
442 :
443 6 : } // snaplogger namespace
444 : // vim: ts=4 sw=4 et
445 :
|