LCOV - code coverage report
Current view: top level - snaplogger - system_functions.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 167 167
Test Date: 2025-08-10 10:49:55 Functions: 100.0 % 33 33
Legend: Lines: hit not hit

            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
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions