LCOV - code coverage report
Current view: top level - snaplogger - format.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 86.8 % 144 125
Test Date: 2025-08-10 10:49:55 Functions: 90.9 % 11 10
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 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 Format a message.
      22              :  *
      23              :  * This file declares the format class used to transform a message with
      24              :  * the administrator defined format.
      25              :  */
      26              : 
      27              : 
      28              : // self
      29              : //
      30              : #include    "snaplogger/format.h"
      31              : 
      32              : #include    "snaplogger/exception.h"
      33              : 
      34              : 
      35              : // C++
      36              : //
      37              : #include    <numeric>
      38              : #include    <iostream>
      39              : 
      40              : 
      41              : // last include
      42              : //
      43              : #include    <snapdev/poison.h>
      44              : 
      45              : 
      46              : 
      47              : namespace snaplogger
      48              : {
      49              : 
      50              : 
      51              : namespace
      52              : {
      53              : 
      54              : 
      55              : 
      56              : class parser
      57              : {
      58              : public:
      59              :     enum class token_t
      60              :     {
      61              :         TOKEN_EOF,
      62              :         TOKEN_COLON,
      63              :         TOKEN_EQUAL,
      64              :         TOKEN_STRING,
      65              :         TOKEN_INTEGER,
      66              :         TOKEN_IDENTIFIER,
      67              :         TOKEN_END,
      68              : 
      69              :         TOKEN_ERROR, // mark an error
      70              :     };
      71              : 
      72           63 :     parser(std::string const & f, variable::vector_t & variables)
      73           63 :         : f_input(f)
      74           63 :         , f_variables(variables)
      75              :     {
      76           63 :     }
      77              : 
      78         3600 :     int getc()
      79              :     {
      80         3600 :         if(f_pos >= f_input.length())
      81              :         {
      82           63 :             return EOF;
      83              :         }
      84              : 
      85         3537 :         int const c(f_input[f_pos]);
      86         3537 :         ++f_pos;
      87         3537 :         return c;
      88              :     }
      89              : 
      90          312 :     void ungetc(int c)
      91              :     {
      92          312 :         if(f_pos == 0)
      93              :         {
      94              :             throw logger_logic_error("ungetc() called too many times.");                // LCOV_EXCL_LINE
      95              :         }
      96          312 :         --f_pos;
      97          312 :         if(f_input[f_pos] != c)
      98              :         {
      99              :             // LCOV_EXCL_START
     100              :             throw logger_logic_error(
     101              :                   std::string("ungetc() called with the wrong character (expected '")
     102              :                 + static_cast<char>(f_input[f_pos])
     103              :                 + "', got '"
     104              :                 + static_cast<char>(c)
     105              :                 + "').");
     106              :             // LCOV_EXCL_STOP
     107              :         }
     108          312 :     }
     109              : 
     110          654 :     token_t get_token()
     111              :     {
     112              :         for(;;)
     113              :         {
     114          654 :             int c(getc());
     115          654 :             if(c == EOF)
     116              :             {
     117            0 :                 return token_t::TOKEN_EOF;
     118              :             }
     119          654 :             switch(c)
     120              :             {
     121            0 :             case ' ':
     122              :             case '\t':
     123              :             case '\n':
     124              :             case '\r':
     125              :                 // ignore spaces
     126            0 :                 break;
     127              : 
     128          100 :             case ':':
     129          100 :                 return token_t::TOKEN_COLON;
     130              : 
     131           91 :             case '=':
     132           91 :                 return token_t::TOKEN_EQUAL;
     133              : 
     134          136 :             case '}':
     135          136 :                 return token_t::TOKEN_END;
     136              : 
     137           23 :             case '"':
     138              :             case '\'':
     139              :             case '`':
     140              :                 {
     141           23 :                     f_text.clear();
     142           23 :                     int quote(c);
     143              :                     for(;;)
     144              :                     {
     145           69 :                         c = getc();
     146           69 :                         if(c == '\\')
     147              :                         {
     148            1 :                             c = getc();
     149              :                         }
     150           68 :                         else if(c == quote)
     151              :                         {
     152           23 :                             break;
     153              :                         }
     154           46 :                         if(c == EOF)
     155              :                         {
     156            0 :                             return token_t::TOKEN_ERROR;
     157              :                         }
     158           46 :                         f_text += c;
     159              :                     }
     160           23 :                     return token_t::TOKEN_STRING;
     161              :                 }
     162              :                 break;
     163              : 
     164           26 :             case '0':
     165              :             case '1':
     166              :             case '2':
     167              :             case '3':
     168              :             case '4':
     169              :             case '5':
     170              :             case '6':
     171              :             case '7':
     172              :             case '8':
     173              :             case '9':
     174              :                 {
     175           26 :                     f_integer = c - '0';
     176              :                     for(;;)
     177              :                     {
     178           52 :                         c = getc();
     179           52 :                         if(c < '0' || c > '9')
     180              :                         {
     181           26 :                             ungetc(c);
     182           26 :                             break;
     183              :                         }
     184           26 :                         f_integer *= 10;
     185           26 :                         f_integer += c - '0';
     186              :                     }
     187           26 :                     return token_t::TOKEN_INTEGER;
     188              :                 }
     189              :                 break;
     190              : 
     191          278 :             default:
     192          278 :                 if((c >= 'a' && c <= 'z')
     193            0 :                 || (c >= 'A' && c <= 'Z')
     194            0 :                 || c == '_')
     195              :                 {
     196          278 :                     f_text.clear();
     197          278 :                     f_text += c;
     198              :                     for(;;)
     199              :                     {
     200         1929 :                         c = getc();
     201         1929 :                         if((c < 'a' || c > 'z')
     202          314 :                         && (c < 'A' || c > 'Z')
     203          314 :                         && (c < '0' || c > '9')
     204          314 :                         && c != '_')
     205              :                         {
     206          278 :                             ungetc(c);
     207          278 :                             break;
     208              :                         }
     209         1651 :                         f_text += c;
     210              :                     }
     211          278 :                     return token_t::TOKEN_IDENTIFIER;
     212              :                 }
     213              :                 else
     214              :                 {
     215            0 :                     return token_t::TOKEN_ERROR;
     216              :                 }
     217              :                 break;
     218              : 
     219              :             }
     220            0 :         }
     221              :     }
     222              : 
     223          136 :     bool parse_variable()
     224              :     {
     225          136 :         token_t tok(get_token());
     226          136 :         if(tok != token_t::TOKEN_IDENTIFIER)
     227              :         {
     228            0 :             return false;
     229              :         }
     230          136 :         variable::pointer_t var(get_variable(f_text));
     231          136 :         if(var == nullptr)
     232              :         {
     233            0 :             return false;
     234              :         }
     235          136 :         f_variables.push_back(var);
     236              : 
     237          136 :         tok = get_token();
     238              :         for(;;)
     239              :         {
     240          236 :             if(tok == token_t::TOKEN_END)
     241              :             {
     242          136 :                 break;
     243              :             }
     244          100 :             if(tok != token_t::TOKEN_COLON)
     245              :             {
     246            0 :                 return false;
     247              :             }
     248          100 :             tok = get_token();
     249          100 :             if(tok != token_t::TOKEN_IDENTIFIER)
     250              :             {
     251            0 :                 return false;
     252              :             }
     253          100 :             param::pointer_t p(std::make_shared<param>(f_text));
     254          100 :             var->add_param(p);
     255              : 
     256          100 :             tok = get_token();
     257          100 :             if(tok == token_t::TOKEN_EQUAL)
     258              :             {
     259              :                 // the token is followed by a value
     260              :                 //
     261           91 :                 tok = get_token();
     262           91 :                 if(tok == token_t::TOKEN_STRING
     263           68 :                 || tok == token_t::TOKEN_IDENTIFIER)
     264              :                 {
     265           65 :                     p->set_value(f_text);
     266              :                 }
     267           26 :                 else if(tok == token_t::TOKEN_INTEGER)
     268              :                 {
     269           26 :                     p->set_integer(f_integer);
     270              :                 }
     271              :                 else
     272              :                 {
     273            0 :                     return false;
     274              :                 }
     275              : 
     276           91 :                 tok = get_token();
     277              :             }
     278          200 :         }
     279              : 
     280          136 :         return true;
     281          136 :     }
     282              : 
     283           63 :     void parse()
     284              :     {
     285           63 :         auto add_text = [this](std::string const & text)
     286              :         {
     287          199 :             if(!text.empty())
     288              :             {
     289          327 :                 variable::pointer_t var(get_variable("direct"));
     290          109 :                 if(var == nullptr)
     291              :                 {
     292            0 :                     throw logger_logic_error("variable type \"direct\" not registered?");
     293              :                 }
     294          109 :                 param::pointer_t p(std::make_shared<param>("msg"));
     295          109 :                 var->add_param(p);
     296          109 :                 p->set_value(text);
     297          109 :                 f_variables.push_back(var);
     298          109 :             }
     299          199 :         };
     300              : 
     301           63 :         std::string text;
     302              : 
     303              :         for(;;)
     304              :         {
     305          751 :             int c(getc());
     306          751 :             if(c == EOF)
     307              :             {
     308           63 :                 break;
     309              :             }
     310          688 :             if(c == '$')
     311              :             {
     312          144 :                 c = getc();
     313          144 :                 if(c == '{')
     314              :                 {
     315              :                     // we found what looks like a variable definition
     316              :                     //
     317          136 :                     add_text(text);
     318          136 :                     text.clear();
     319              : 
     320              :                     // try parsing the variable, if that fails, ignore
     321              :                     // the variable and instead add the "${" introducer
     322              :                     // on its own
     323              :                     //
     324          136 :                     auto const save_pos(f_pos);
     325          136 :                     if(!parse_variable())
     326              :                     {
     327            0 :                         text += "${";
     328            0 :                         f_pos = save_pos;
     329              :                     }
     330              :                 }
     331              :                 else
     332              :                 {
     333            8 :                     text += '$';
     334            8 :                     ungetc(c);
     335              :                 }
     336              :             }
     337              :             else
     338              :             {
     339          544 :                 text += c;
     340              :             }
     341          688 :         }
     342           63 :         add_text(text);
     343          126 :     }
     344              : 
     345              : private:
     346              :     std::string const       f_input;
     347              :     size_t                  f_pos = 0;
     348              :     variable::vector_t &    f_variables;
     349              :     std::string             f_text = std::string();
     350              :     std::int64_t            f_integer = 0;
     351              : };
     352              : 
     353              : 
     354              : 
     355              : }
     356              : // no name namespace
     357              : 
     358              : 
     359           63 : format::format(std::string const & f)
     360           63 :     : f_format(f)
     361              : {
     362           63 :     parser p(f, f_variables);
     363           63 :     p.parse();
     364          126 : }
     365              : 
     366              : 
     367            0 : std::string format::get_format() const
     368              : {
     369            0 :     return f_format;
     370              : }
     371              : 
     372              : 
     373        41839 : std::string format::process_message(message const & msg, bool ignore_on_no_repeat)
     374              : {
     375        41839 :     return std::accumulate(
     376              :               f_variables.begin()
     377              :             , f_variables.end()
     378        41845 :             , std::string()
     379        42174 :             , [&msg, &ignore_on_no_repeat](std::string const & r, variable::pointer_t v)
     380              :             {
     381        84348 :                 if(ignore_on_no_repeat
     382        42174 :                 && v->ignore_on_no_repeat())
     383              :                 {
     384              :                     // do not include this variable to generate the "no-repeat"
     385              :                     // message (i.e. probably a time base message)
     386              :                     //
     387            0 :                     return r;
     388              :                 }
     389        84342 :                 return r + v->get_value(msg);
     390       125511 :             });
     391              : }
     392              : 
     393              : 
     394              : 
     395              : 
     396              : 
     397              : } // snaplogger namespace
     398              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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