LCOV - code coverage report
Current view: top level - snaplogger - format.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 124 143 86.7 %
Date: 2021-08-20 16:41:45 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2013-2021  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++ lib
      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          60 : 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             : 
      70          60 :     parser(std::string const & f, variable::vector_t & variables)
      71          60 :         : f_input(f)
      72          60 :         , f_variables(variables)
      73             :     {
      74          60 :     }
      75             : 
      76        3450 :     int getc()
      77             :     {
      78        3450 :         if(f_pos >= f_input.length())
      79             :         {
      80          60 :             return EOF;
      81             :         }
      82             : 
      83        3390 :         int const c(f_input[f_pos]);
      84        3390 :         ++f_pos;
      85        3390 :         return c;
      86             :     }
      87             : 
      88         309 :     void ungetc(int c)
      89             :     {
      90         309 :         if(f_pos == 0)
      91             :         {
      92             :             throw logger_logic_error("ungetc() called too many times.");                // LCOV_EXCL_LINE
      93             :         }
      94         309 :         --f_pos;
      95         309 :         if(f_input[f_pos] != c)
      96             :         {
      97             :             throw logger_logic_error("ungetc() called with the wrong character.");      // LCOV_EXCL_LINE
      98             :         }
      99         309 :     }
     100             : 
     101         632 :     token_t get_token()
     102             :     {
     103             :         for(;;)
     104             :         {
     105         632 :             int c(getc());
     106         632 :             if(c == EOF)
     107             :             {
     108           0 :                 return token_t::TOKEN_EOF;
     109             :             }
     110         632 :             switch(c)
     111             :             {
     112           0 :             case ' ':
     113             :             case '\t':
     114             :             case '\n':
     115             :             case '\r':
     116             :                 // ignore spaces
     117           0 :                 break;
     118             : 
     119         100 :             case ':':
     120         100 :                 return token_t::TOKEN_COLON;
     121             : 
     122          91 :             case '=':
     123          91 :                 return token_t::TOKEN_EQUAL;
     124             : 
     125         125 :             case '}':
     126         125 :                 return token_t::TOKEN_END;
     127             : 
     128          23 :             case '"':
     129             :             case '\'':
     130             :             case '`':
     131             :                 {
     132          23 :                     f_text.clear();
     133          23 :                     int quote(c);
     134             :                     for(;;)
     135             :                     {
     136         115 :                         c = getc();
     137          69 :                         if(c == '\\')
     138             :                         {
     139           1 :                             c = getc();
     140             :                         }
     141          68 :                         else if(c == quote)
     142             :                         {
     143          23 :                             break;
     144             :                         }
     145          46 :                         if(c == EOF)
     146             :                         {
     147           0 :                             throw invalid_variable("unterminated string in format variable.");
     148             :                         }
     149          46 :                         f_text += c;
     150             :                     }
     151          23 :                     return token_t::TOKEN_STRING;
     152             :                 }
     153             :                 break;
     154             : 
     155          30 :             case '0':
     156             :             case '1':
     157             :             case '2':
     158             :             case '3':
     159             :             case '4':
     160             :             case '5':
     161             :             case '6':
     162             :             case '7':
     163             :             case '8':
     164             :             case '9':
     165             :                 {
     166          30 :                     f_integer = c - '0';
     167             :                     for(;;)
     168             :                     {
     169          88 :                         c = getc();
     170          59 :                         if(c < '0' || c > '9')
     171             :                         {
     172          30 :                             ungetc(c);
     173          30 :                             break;
     174             :                         }
     175          29 :                         f_integer *= 10;
     176          29 :                         f_integer += c - '0';
     177             :                     }
     178          30 :                     return token_t::TOKEN_INTEGER;
     179             :                 }
     180             :                 break;
     181             : 
     182         263 :             default:
     183         263 :                 if((c >= 'a' && c <= 'z')
     184           0 :                 || (c >= 'A' && c <= 'Z')
     185           0 :                 || c == '_')
     186             :                 {
     187         263 :                     f_text.clear();
     188         263 :                     f_text += c;
     189             :                     for(;;)
     190             :                     {
     191        3423 :                         c = getc();
     192        1843 :                         if((c < 'a' || c > 'z')
     193         301 :                         && (c < 'A' || c > 'Z')
     194         301 :                         && (c < '0' || c > '9')
     195         301 :                         && c != '_')
     196             :                         {
     197         263 :                             ungetc(c);
     198         263 :                             break;
     199             :                         }
     200        1580 :                         f_text += c;
     201             :                     }
     202         263 :                     return token_t::TOKEN_IDENTIFIER;
     203             :                 }
     204             :                 else
     205             :                 {
     206           0 :                     std::stringstream ss;
     207             : 
     208           0 :                     ss << "unexpected character '\\x"
     209           0 :                        << std::hex
     210             :                        << c
     211           0 :                        << "' in format variable.";
     212             : 
     213           0 :                     throw invalid_variable(ss.str());
     214             :                 }
     215             :                 break;
     216             : 
     217             :             }
     218           0 :         }
     219             :     }
     220             : 
     221         125 :     void parse_variable()
     222             :     {
     223         125 :         token_t tok(get_token());
     224         125 :         if(tok != token_t::TOKEN_IDENTIFIER)
     225             :         {
     226           0 :             throw invalid_variable("expected a token as the variable name.");
     227             :         }
     228         250 :         variable::pointer_t var(get_variable(f_text));
     229         125 :         if(var == nullptr)
     230             :         {
     231           0 :             throw invalid_variable("unknown variable \"" + f_text + "\".");
     232             :         }
     233         125 :         f_variables.push_back(var);
     234             : 
     235         125 :         tok = get_token();
     236             :         for(;;)
     237             :         {
     238         225 :             if(tok == token_t::TOKEN_END)
     239             :             {
     240         125 :                 break;
     241             :             }
     242         100 :             if(tok != token_t::TOKEN_COLON)
     243             :             {
     244           0 :                 throw invalid_variable("variable parameters must be delimited by colon (:) characters.");
     245             :             }
     246         100 :             tok = get_token();
     247         100 :             if(tok != token_t::TOKEN_IDENTIFIER)
     248             :             {
     249           0 :                 throw invalid_variable("variable parameters must be given a name (an identifier).");
     250             :             }
     251         200 :             param::pointer_t p(std::make_shared<param>(f_text));
     252         100 :             var->add_param(p);
     253             : 
     254         100 :             tok = get_token();
     255         100 :             if(tok == token_t::TOKEN_EQUAL)
     256             :             {
     257             :                 // the token is followed by a value
     258             :                 //
     259          91 :                 tok = get_token();
     260          91 :                 if(tok == token_t::TOKEN_STRING
     261          68 :                 || tok == token_t::TOKEN_IDENTIFIER)
     262             :                 {
     263          61 :                     p->set_value(f_text);
     264             :                 }
     265          30 :                 else if(tok == token_t::TOKEN_INTEGER)
     266             :                 {
     267          30 :                     p->set_integer(f_integer);
     268             :                 }
     269             :                 else
     270             :                 {
     271           0 :                     throw invalid_variable("unexpected token for a parameter value.");
     272             :                 }
     273             : 
     274          91 :                 tok = get_token();
     275             :             }
     276         100 :         }
     277         125 :     }
     278             : 
     279          60 :     void parse()
     280             :     {
     281         245 :         auto add_text = [this](std::string const & text)
     282         104 :         {
     283         185 :             if(!text.empty())
     284             :             {
     285         208 :                 variable::pointer_t var(get_variable("direct"));
     286         104 :                 if(var == nullptr)
     287             :                 {
     288           0 :                     throw logger_logic_error("variable type \"direct\" not registered?");
     289             :                 }
     290         208 :                 param::pointer_t p(std::make_shared<param>("msg"));
     291         104 :                 var->add_param(p);
     292         104 :                 p->set_value(text);
     293         104 :                 f_variables.push_back(var);
     294             :             }
     295         245 :         };
     296             : 
     297         120 :         std::string text;
     298             : 
     299             :         for(;;)
     300             :         {
     301         705 :             int c(getc());
     302         705 :             if(c == EOF)
     303             :             {
     304          60 :                 break;
     305             :             }
     306         645 :             if(c == '$')
     307             :             {
     308         141 :                 c = getc();
     309         141 :                 if(c == '{')
     310             :                 {
     311             :                     // we found a variable definition
     312             :                     //
     313         125 :                     add_text(text);
     314         125 :                     text.clear();
     315             : 
     316         125 :                     parse_variable();
     317             :                 }
     318             :                 else
     319             :                 {
     320          16 :                     text += '$';
     321          16 :                     ungetc(c);
     322             :                 }
     323             :             }
     324             :             else
     325             :             {
     326         504 :                 text += c;
     327             :             }
     328         645 :         }
     329          60 :         add_text(text);
     330          60 :     }
     331             : 
     332             : private:
     333             :     std::string const       f_input;
     334             :     size_t                  f_pos = 0;
     335             :     variable::vector_t &    f_variables;
     336             :     std::string             f_text = std::string();
     337             :     std::int64_t            f_integer = 0;
     338             : };
     339             : 
     340             : 
     341             : 
     342             : }
     343             : // no name namespace
     344             : 
     345             : 
     346          60 : format::format(std::string const & f)
     347             : {
     348         120 :     parser p(f, f_variables);
     349          60 :     p.parse();
     350          60 : }
     351             : 
     352             : 
     353       46879 : std::string format::process_message(message const & msg, bool ignore_on_no_repeat)
     354             : {
     355             :     return std::accumulate(
     356             :               f_variables.begin()
     357             :             , f_variables.end()
     358       93758 :             , std::string()
     359       47162 :             , [&msg, &ignore_on_no_repeat](std::string const & r, variable::pointer_t v)
     360       94324 :             {
     361       94324 :                 if(ignore_on_no_repeat
     362       47162 :                 && v->ignore_on_no_repeat())
     363             :                 {
     364             :                     // do not include this variable to generate the "no-repeat"
     365             :                     // message (i.e. probably a time base message)
     366             :                     //
     367           0 :                     return r;
     368             :                 }
     369       94324 :                 return r + v->get_value(msg);
     370      140631 :             });
     371             : }
     372             : 
     373             : 
     374             : 
     375             : 
     376             : 
     377           6 : } // snaplogger namespace
     378             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13