LCOV - code coverage report
Current view: top level - snaplogger - format.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 103 132 78.0 %
Date: 2019-08-13 00:35:33 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12