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