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

Generated by: LCOV version 2.0-1

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