cfg_lexer.l 7.91 KB
%{
/* cfg_lexer.l -- default lexer for Mailutils configuration files
   Copyright (C) 2007, 2008 Free Software Foundation, Inc.

   GNU Mailutils is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 3, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <mailutils/errno.h>
#include <mailutils/error.h>
#include <mailutils/debug.h>  
#include <mailutils/argcv.h>
#include <mailutils/alloc.h>  
#include <mailutils/nls.h>
#include <mailutils/cfg.h>
#include <mailutils/list.h>

#include "cfg_parser.h"

void _mu_line_begin (void);
void _mu_line_add (char *text, size_t len);
char *_mu_line_finish (void);

static void _mu_line_add_unescape_last (char *text, size_t len);
static void multiline_begin (char *p);
static char *multiline_strip_tabs (char *text);
static void multiline_add (char *s);
static char *multiline_finish (void);
 
static char *multiline_delimiter;
static size_t multiline_delimiter_len;
static int multiline_unescape;         /* Unescape here-document contents */
static int (*char_to_strip)(char);     /* Strip matching characters of each
					  here-document line */
static int isemptystr(int off);

static mu_opool_t pool;

%}

%x COMMENT ML STR

WS [ \t\f][ \t\f]*
ID [a-zA-Z_][a-zA-Z_0-9-]+
P [1-9][0-9]*

%%
         /* C-style comments */
"/*"         BEGIN(COMMENT);
<COMMENT>[^*\n]*        /* eat anything that's not a '*' */
<COMMENT>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
<COMMENT>\n             ++mu_cfg_locus.line;
<COMMENT>"*"+"/"        BEGIN (INITIAL);
         /* End-of-line comments */
#.*\n     { mu_cfg_locus.line++; }
#.*     /* end-of-file comment */;
"//".*\n  { mu_cfg_locus.line++; }
"//".*    /* end-of-file comment */;
        /* Identifiers */
<INITIAL>{ID}         {
                        _mu_line_begin ();
			_mu_line_add (yytext, yyleng);
			yylval.string = _mu_line_finish ();
			return MU_TOK_IDENT; }
         /* Strings */
[a-zA-Z0-9_\./:-]+    { _mu_line_begin ();
                        _mu_line_add (yytext, yyleng);
                        yylval.string = _mu_line_finish ();
                        return MU_TOK_STRING; }
         /* Quoted strings */
\"[^\\"\n]*\"         { _mu_line_begin ();
                        _mu_line_add (yytext + 1, yyleng - 2);
                        yylval.string = _mu_line_finish ();
                        return MU_TOK_QSTRING; }
\"[^\\"\n]*\\. |
\"[^\\"\n]*\\\n        { BEGIN (STR);
                        _mu_line_begin ();
		        _mu_line_add_unescape_last (yytext + 1, yyleng - 1); }
<STR>[^\\"\n]*\\. |
<STR>\"[^\\"\n]*\\\n  { _mu_line_add_unescape_last (yytext, yyleng); }
<STR>[^\\"\n]*\"      { BEGIN (INITIAL);
                        if (yyleng > 1) 
                          _mu_line_add (yytext, yyleng - 1); 
                        yylval.string = _mu_line_finish ();
		        return MU_TOK_QSTRING; }
         /* Multiline strings */
"<<"(-" "?)?\\?{ID}[ \t]*#.*\n |
"<<"(-" "?)?\\?{ID}[ \t]*"//".*\n |
"<<"(-" "?)?\\?{ID}[ \t]*\n |
"<<"(-" "?)?\"{ID}\"[ \t]*#.*\n |
"<<"(-" "?)?\"{ID}\"[ \t]*"//".*\n |
"<<"(-" "?)?\"{ID}\"[ \t]*\n {
                        BEGIN (ML);
			multiline_begin (yytext+2);
			mu_cfg_locus.line++;
		  }
<ML>.*\n { char *p = multiline_strip_tabs (yytext);
	   
           if (!strncmp (p, multiline_delimiter, multiline_delimiter_len)
	       && isemptystr (p + multiline_delimiter_len - yytext))
	     {
	       free (multiline_delimiter);
	       multiline_delimiter = NULL;
	       BEGIN (INITIAL);
	       yylval.string = multiline_finish ();
	       return MU_TOK_MSTRING;
	     }
           mu_cfg_locus.line++;
	   multiline_add (p); } 
{WS}     ;
         /* Other tokens */
\n       { mu_cfg_locus.line++; } 
[,;{}()] return yytext[0];
.        mu_cfg_perror (&mu_cfg_locus, _("stray character \\%03o"), yytext[0]);
%%

int
yywrap ()
{
  return 1;
}

static void
unescape_to_line (int c)
{
  if (c != '\n')
    {
      char t = mu_argcv_unquote_char (c);
      if (t == c)
	mu_cfg_perror (&mu_cfg_locus, _("unknown escape sequence '\\%c'"), c);
      mu_opool_append_char (pool, t);
    }
}

void
_mu_line_add (char *text, size_t len)
{
  mu_opool_append (pool, text, len);
}

void
_mu_line_add_unescape_last (char *text, size_t len)
{
  mu_opool_append (pool, text, len - 2);
  unescape_to_line (text[len - 1]);
}

void
_mu_line_begin ()
{
  if (!pool)
    mu_opool_create (&pool, 1);
  else
    mu_opool_clear (pool);
}

char *
_mu_line_finish ()
{
  mu_opool_append_char (pool, 0);
  return mu_opool_finish (pool, NULL);
}



static int
is_tab (char c)
{
    return c == '\t';
}
 
static int
is_ws (char c)
{
    return c == '\t' || c == ' ';
}

static int
isemptystr (int off)
{
  for (; yytext[off] && isspace (yytext[off]); off++)
    ;
  if (yytext[off] == ';')
    {
      int i;
      for (i = off + 1; yytext[i]; i++) 
	if (!isspace (yytext[i]))
	  return 0;
      yyless (off);
      return 1;
    }
  return yytext[off] == 0;
}

static void
multiline_begin (char *p)
{
  if (*p == '-')
    {
      if (*++p == ' ')
	{
	  char_to_strip = is_ws;
	  p++;
	}
      else
	char_to_strip = is_tab;
    }
  else
    char_to_strip = NULL;
  if (*p == '\\')
    {
      p++;
      multiline_unescape = 0;
    }
  else if (*p == '"')
    {
      char *q;
      
      p++;
      multiline_unescape = 0;
      q = strchr (p, '"');
      multiline_delimiter_len = q - p;
    }
  else
    {
	multiline_delimiter_len = strcspn (p, " \t");
	multiline_unescape = 1;
    }

  /* Remove trailing newline */
  multiline_delimiter_len--;
  multiline_delimiter = mu_alloc (multiline_delimiter_len + 1);
  memcpy (multiline_delimiter, p, multiline_delimiter_len);
  multiline_delimiter[multiline_delimiter_len] = 0;
  _mu_line_begin ();
}

static char *
multiline_strip_tabs (char *text)
{
  if (char_to_strip)
    for (; *text && char_to_strip (*text); text++)
      ;
  return text;
}

static void
multiline_add (char *s)
{
  if (multiline_unescape)
    {
      for (; *s; s++)
	{
	  if (*s == '\\')
	    {
	      unescape_to_line (s[1]);
	      ++s;
	    }
	  else
	    _mu_line_add (s, 1);
	}
    }
  else
    _mu_line_add (s, strlen (s));
}

static char *
multiline_finish ()
{
  return _mu_line_finish ();
}


int
mu_get_config (const char *file, const char *progname,
               struct mu_cfg_param *progparam, int flags, void *target_ptr)
{
  struct stat st;
  FILE *fp;
  int rc;
  mu_cfg_tree_t *parse_tree;
  
  if (stat (file, &st))
    {
      if (errno != ENOENT)
	mu_error (_("cannot stat `%s': %s"), file, mu_strerror (errno));
      return -1;
    }
  fp = fopen (file, "r");
  if (!fp)
    {
      mu_error (_("cannot open config file `%s': %s"), file,
		mu_strerror (errno));
      return -1;
    }

  if (flags & MU_PARSE_CONFIG_VERBOSE)
    mu_error (_("Info: parsing file `%s'"), file);

  yy_flex_debug = mu_debug_check_level (mu_cfg_get_debug (), MU_DEBUG_TRACE2);
  
  /* Parse configuration */
  mu_cfg_locus.file = (char*) file;
  mu_cfg_locus.line = 1;
  yyrestart (fp);
  rc = mu_cfg_parse (&parse_tree);
  fclose (fp);
  if (rc == 0)
    rc = mu_cfg_tree_reduce (parse_tree, progname, progparam, flags,
			     target_ptr);

  mu_cfg_destroy_tree (&parse_tree);

  if (flags & MU_PARSE_CONFIG_VERBOSE)
    mu_error (_("Info: finished parsing file `%s'"), file);
  
  return rc;
}

mu_opool_t 
mu_cfg_lexer_pool ()
{
  mu_opool_t p = pool;
  pool = NULL;
  return p;
}