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

Generated by: LCOV version 1.13