LCOV - code coverage report
Current view: top level - snaplogger - format.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 115 132 87.1 %
Date: 2019-08-18 12:59:55 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013-2019  Made to Order Software Corp.  All Rights Reserved
       3             :  *
       4             :  * https://snapwebsites.org/project/snaplogger
       5             :  * contact@m2osw.com
       6             :  *
       7             :  * This program is free software; you can redistribute it and/or modify
       8             :  * it under the terms of the GNU General Public License as published by
       9             :  * the Free Software Foundation; either version 2 of the License, or
      10             :  * (at your option) any later version.
      11             :  *
      12             :  * This program is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :  * GNU General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU General Public License along
      18             :  * with this program; if not, write to the Free Software Foundation, Inc.,
      19             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20             :  */
      21             : 
      22             : /** \file
      23             :  * \brief Format a message.
      24             :  *
      25             :  * This file declares the format class used to transform a message with
      26             :  * the administrator defined format.
      27             :  */
      28             : 
      29             : 
      30             : // self
      31             : //
      32             : #include    "snaplogger/format.h"
      33             : 
      34             : #include    "snaplogger/exception.h"
      35             : 
      36             : 
      37             : // C++ lib
      38             : //
      39             : #include    <numeric>
      40             : #include    <iostream>
      41             : 
      42             : 
      43             : // last include
      44             : //
      45             : #include    <snapdev/poison.h>
      46             : 
      47             : 
      48             : 
      49             : namespace snaplogger
      50             : {
      51             : 
      52             : 
      53             : namespace
      54             : {
      55             : 
      56             : 
      57             : 
      58          20 : class parser
      59             : {
      60             : public:
      61             :     enum class token_t
      62             :     {
      63             :         TOKEN_EOF,
      64             :         TOKEN_COLON,
      65             :         TOKEN_EQUAL,
      66             :         TOKEN_STRING,
      67             :         TOKEN_INTEGER,
      68             :         TOKEN_IDENTIFIER,
      69             :         TOKEN_END
      70             :     };
      71             : 
      72          20 :     parser(std::string const & f, variable::vector_t & variables)
      73             :         : f_input(f)
      74          20 :         , f_variables(variables)
      75             :     {
      76          20 :     }
      77             : 
      78         999 :     int getc()
      79             :     {
      80         999 :         if(f_pos >= f_input.length())
      81             :         {
      82          20 :             return EOF;
      83             :         }
      84             : 
      85         979 :         int const c(f_input[f_pos]);
      86         979 :         ++f_pos;
      87         979 :         return c;
      88             :     }
      89             : 
      90          75 :     void ungetc(int c)
      91             :     {
      92          75 :         if(f_pos == 0)
      93             :         {
      94           0 :             throw logger_logic_error("ungetc() called too many times.");
      95             :         }
      96          75 :         --f_pos;
      97          75 :         if(f_input[f_pos] != c)
      98             :         {
      99           0 :             throw logger_logic_error("ungetc() called with the wrong character.");
     100             :         }
     101          75 :     }
     102             : 
     103         158 :     token_t get_token()
     104             :     {
     105           0 :         for(;;)
     106             :         {
     107         158 :             int c(getc());
     108         158 :             if(c == EOF)
     109             :             {
     110           0 :                 return token_t::TOKEN_EOF;
     111             :             }
     112         158 :             switch(c)
     113             :             {
     114             :             case ' ':
     115             :             case '\t':
     116             :             case '\n':
     117             :             case '\r':
     118             :                 // ignore spaces
     119           0 :                 break;
     120             : 
     121             :             case ':':
     122          20 :                 return token_t::TOKEN_COLON;
     123             : 
     124             :             case '=':
     125          15 :                 return token_t::TOKEN_EQUAL;
     126             : 
     127             :             case '}':
     128          44 :                 return token_t::TOKEN_END;
     129             : 
     130             :             case '"':
     131             :             case '\'':
     132             :             case '`':
     133             :                 {
     134           4 :                     f_text.clear();
     135           4 :                     int quote(c);
     136           7 :                     for(;;)
     137             :                     {
     138          11 :                         c = getc();
     139          11 :                         if(c == '\\')
     140             :                         {
     141           1 :                             c = getc();
     142             :                         }
     143          10 :                         else if(c == quote)
     144             :                         {
     145           4 :                             break;
     146             :                         }
     147           7 :                         if(c == EOF)
     148             :                         {
     149           0 :                             throw invalid_variable("unterminated string in format variable.");
     150             :                         }
     151           7 :                         f_text += c;
     152             :                     }
     153           4 :                     return token_t::TOKEN_STRING;
     154             :                 }
     155             :                 break;
     156             : 
     157             :             case '0':
     158             :             case '1':
     159             :             case '2':
     160             :             case '3':
     161             :             case '4':
     162             :             case '5':
     163             :             case '6':
     164             :             case '7':
     165             :             case '8':
     166             :             case '9':
     167             :                 {
     168           7 :                     f_integer = c - '0';
     169           8 :                     for(;;)
     170             :                     {
     171          15 :                         c = getc();
     172          15 :                         if(c < '0' || c > '9')
     173             :                         {
     174           7 :                             ungetc(c);
     175           7 :                             break;
     176             :                         }
     177           8 :                         f_integer *= 10;
     178           8 :                         f_integer += c - '0';
     179             :                     }
     180           7 :                     return token_t::TOKEN_INTEGER;
     181             :                 }
     182             :                 break;
     183             : 
     184             :             default:
     185          68 :                 if((c >= 'a' && c <= 'z')
     186           0 :                 || (c >= 'A' && c <= 'Z')
     187           0 :                 || c == '_')
     188             :                 {
     189          68 :                     f_text.clear();
     190          68 :                     f_text += c;
     191         391 :                     for(;;)
     192             :                     {
     193         459 :                         c = getc();
     194         459 :                         if((c < 'a' || c > 'z')
     195          78 :                         && (c < 'A' || c > 'Z')
     196          78 :                         && (c < '0' || c > '9')
     197          78 :                         && c != '_')
     198             :                         {
     199          68 :                             ungetc(c);
     200          68 :                             break;
     201             :                         }
     202         391 :                         f_text += c;
     203             :                     }
     204          68 :                     return token_t::TOKEN_IDENTIFIER;
     205             :                 }
     206             :                 else
     207             :                 {
     208           0 :                     throw invalid_variable("unexpected character in format variable.");
     209             :                 }
     210             :                 break;
     211             : 
     212             :             }
     213             :         }
     214             :     }
     215             : 
     216          44 :     void parse_variable()
     217             :     {
     218          44 :         token_t tok(get_token());
     219          44 :         if(tok != token_t::TOKEN_IDENTIFIER)
     220             :         {
     221           0 :             throw invalid_variable("expected a token as the variable name.");
     222             :         }
     223          88 :         variable::pointer_t var(get_variable(f_text));
     224          44 :         if(var == nullptr)
     225             :         {
     226           0 :             throw invalid_variable("unknown variable \"" + f_text + "\".");
     227             :         }
     228          44 :         f_variables.push_back(var);
     229             : 
     230          44 :         tok = get_token();
     231          20 :         for(;;)
     232             :         {
     233          64 :             if(tok == token_t::TOKEN_END)
     234             :             {
     235          44 :                 break;
     236             :             }
     237          20 :             if(tok != token_t::TOKEN_COLON)
     238             :             {
     239           0 :                 throw invalid_variable("variable parameters must be delimited by colon (:) characters.");
     240             :             }
     241          20 :             tok = get_token();
     242          20 :             if(tok != token_t::TOKEN_IDENTIFIER)
     243             :             {
     244           0 :                 throw invalid_variable("variable parameters must be given a name (an identifier).");
     245             :             }
     246          40 :             param::pointer_t p(std::make_shared<param>(f_text));
     247          20 :             var->add_param(p);
     248             : 
     249          20 :             tok = get_token();
     250          20 :             if(tok == token_t::TOKEN_EQUAL)
     251             :             {
     252             :                 // the token is followed by a value
     253             :                 //
     254          15 :                 tok = get_token();
     255          15 :                 if(tok == token_t::TOKEN_STRING
     256          11 :                 || tok == token_t::TOKEN_IDENTIFIER)
     257             :                 {
     258           8 :                     p->set_value(f_text);
     259             :                 }
     260           7 :                 else if(tok == token_t::TOKEN_INTEGER)
     261             :                 {
     262           7 :                     p->set_integer(f_integer);
     263             :                 }
     264             :                 else
     265             :                 {
     266           0 :                     throw invalid_variable("unexpected token for a parameter value.");
     267             :                 }
     268             : 
     269          15 :                 tok = get_token();
     270             :             }
     271             :         }
     272          44 :     }
     273             : 
     274          20 :     void parse()
     275             :     {
     276          64 :         auto add_text = [this](std::string const & text)
     277          33 :         {
     278          64 :             if(!text.empty())
     279             :             {
     280          66 :                 variable::pointer_t var(get_variable("direct"));
     281          33 :                 if(var == nullptr)
     282             :                 {
     283           0 :                     throw logger_logic_error("variable type \"direct\" not registered?.");
     284             :                 }
     285          66 :                 param::pointer_t p(std::make_shared<param>("msg"));
     286          33 :                 var->add_param(p);
     287          33 :                 p->set_value(text);
     288          33 :                 f_variables.push_back(var);
     289             :             }
     290          84 :         };
     291             : 
     292          40 :         std::string text;
     293             : 
     294         291 :         for(;;)
     295             :         {
     296         311 :             int c(getc());
     297         311 :             if(c == EOF)
     298             :             {
     299          20 :                 break;
     300             :             }
     301         291 :             if(c == '$')
     302             :             {
     303          44 :                 c = getc();
     304          44 :                 if(c == '{')
     305             :                 {
     306             :                     // we found a variable definition
     307             :                     //
     308          44 :                     add_text(text);
     309          44 :                     text.clear();
     310             : 
     311          44 :                     parse_variable();
     312             :                 }
     313             :                 else
     314             :                 {
     315           0 :                     text += '$';
     316           0 :                     text += c;
     317             :                 }
     318             :             }
     319             :             else
     320             :             {
     321         247 :                 text += c;
     322             :             }
     323             :         }
     324          20 :         add_text(text);
     325          20 :     }
     326             : 
     327             : private:
     328             :     std::string const       f_input;
     329             :     size_t                  f_pos = 0;
     330             :     variable::vector_t &    f_variables;
     331             :     std::string             f_text = std::string();
     332             :     std::int64_t            f_integer = 0;
     333             : };
     334             : 
     335             : 
     336             : 
     337             : }
     338             : // no name namespace
     339             : 
     340             : 
     341          20 : format::format(std::string const & f)
     342             : {
     343          40 :     parser p(f, f_variables);
     344          20 :     p.parse();
     345          20 : }
     346             : 
     347             : 
     348       45551 : std::string format::process_message(message const & msg)
     349             : {
     350             :     return std::accumulate(
     351             :               f_variables.begin()
     352             :             , f_variables.end()
     353             :             , std::string()
     354       45587 :             , [&msg](std::string const & r, variable::pointer_t v)
     355       45587 :             {
     356       91172 :                 return r + v->get_value(msg);
     357      136721 :             });
     358             : }
     359             : 
     360             : 
     361             : 
     362             : 
     363             : 
     364           6 : } // snaplogger namespace
     365             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12