/* gconf - General purpose configuration parser. -*- c -*- */
%{
/* gconf - General purpose configuration parser.
Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
This program 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 of the License, 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
#include
#include
#include
#if ENABLE_NLS
# include "gettext.h"
# define _(msgid) gettext (msgid)
#else
# define _(msgid) msgid
#endif
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 */
gconf_locus_t gconf_current_locus; /* Input file location */
/* Line correction. Equals to the number of #line directives inserted into
the input by the preprocessor instance. The external preprocessor, if
any, counts these as input lines and therefore the line numbers in *its*
#line directives are offset by the value of XLINES.
Uff, running two preprocessors is confusing...
*/
static size_t xlines;
static struct obstack stk;
static void multiline_begin (char *);
static void multiline_add (char *);
static char *multiline_strip_tabs (char *text);
static void line_add_unescape_last (char *text, size_t len);
static int ident (void);
static int isemptystr (int off);
static void parse_line (char *text, gconf_locus_t *ploc, size_t *pxlines);
static void parse_line_cpp (char *text, gconf_locus_t *ploc, size_t *pxlines);
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
do \
{ \
if (gconf_preprocessor) \
result = fread (buf, 1, max_size, yyin); \
else \
result = gconf_preproc_fill_buffer(buf, max_size); \
} \
while (0)
%}
%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);
[^*\n]* /* eat anything that's not a '*' */
"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
\n ++gconf_current_locus.line;
"*"+"/" BEGIN (INITIAL);
/* Line directive */
^[ \t]*#[ \t]*{P}[ \t]+\".*\".*\n { parse_line_cpp (yytext,
&gconf_current_locus,
&xlines); }
^[ \t]*#[ \t]*line[ \t].*\n { parse_line (yytext, &gconf_current_locus,
&xlines); }
/* End-of-line comments */
#.*\n { gconf_current_locus.line++; }
#.* /* end-of-file comment */;
"//".*\n { gconf_current_locus.line++; }
"//".* /* end-of-file comment */;
/* Identifiers */
{ID} return ident ();
/* Strings */
[a-zA-Z0-9_\.\*/:@-]+ { gconf_line_begin ();
gconf_line_add (yytext, yyleng);
yylval.string = gconf_line_finish ();
return STRING; }
/* Quoted strings */
\"[^\\"\n]*\" { gconf_line_begin ();
gconf_line_add (yytext + 1, yyleng - 2);
yylval.string = gconf_line_finish ();
return QSTRING; }
\"[^\\"\n]*\\. |
\"[^\\"\n]*\\\n { BEGIN (STR);
gconf_line_begin ();
line_add_unescape_last (yytext + 1, yyleng - 1); }
[^\\"\n]*\\. |
\"[^\\"\n]*\\\n { line_add_unescape_last (yytext, yyleng); }
[^\\"\n]*\" { BEGIN(INITIAL);
if (yyleng > 1)
gconf_line_add (yytext, yyleng - 1);
yylval.string = gconf_line_finish ();
return 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);
gconf_current_locus.line++; }
/* Ignore m4 line statements */
^"#line ".*\n { gconf_current_locus.line++; }
.*\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 = gconf_line_finish ();
return MSTRING;
}
gconf_current_locus.line++;
multiline_add (p); }
{WS} ;
/* Other tokens */
\n { gconf_current_locus.line++; }
[,;{}()] return yytext[0];
. { if (isascii (yytext[0]) && isprint (yytext[0]))
gconf_error (&gconf_current_locus, 0, _("stray character %c"), yytext[0]);
else
gconf_error (&gconf_current_locus, 0, _("stray character \\%03o"),
(unsigned char) yytext[0]); }
%%
pid_t gconf_preproc_pid;
int
yywrap ()
{
if (yyin)
gconf_preproc_extrn_shutdown (gconf_preproc_pid);
else
gconf_preproc_done ();
gconf_current_locus.file = NULL;
return 1;
}
int
gconf_lex_begin (const char *name)
{
if (yy_flex_debug > 0)
yy_flex_debug = 0;
obstack_init (&stk);
if (gconf_preprocessor)
{
int fd;
fd = open (name, O_RDONLY);
if (fd == -1)
{
gconf_error (NULL, errno, _("Cannot open `%s'"), name);
return 1;
}
close (fd);
yyin = gconf_preproc_extrn_start (name, &gconf_preproc_pid);
if (!yyin)
{
gconf_error (NULL, errno,
_("Unable to start external preprocessor `%s'"),
gconf_preprocessor);
return 1;
}
}
else
return gconf_preproc_init (name);
return 0;
}
void
gconf_lex_end ()
{
}
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;
}
char *
multiline_strip_tabs (char *text)
{
if (char_to_strip)
for (; *text && char_to_strip (*text); text++)
;
return text;
}
static int
unquote_char (int c)
{
static char quote_transtab[] = "\\\\a\ab\bf\fn\nr\rt\t";
char *p;
for (p = quote_transtab; *p; p += 2)
{
if (*p == c)
return p[1];
}
return -1;
}
static void
unescape_to_obstack (int c)
{
if (c != '\n')
{
int t = unquote_char (c);
if (t != -1)
obstack_1grow (&stk, t);
else
{
gconf_warning(&gconf_current_locus, 0,
_("unknown escape sequence '\\%c'"),
c);
obstack_1grow (&stk, c);
}
}
}
void
gconf_line_add (const char *text, size_t len)
{
obstack_grow (&stk, text, len);
}
/* Same, but unescapes the last character from yytext */
static void
line_add_unescape_last (char *text, size_t len)
{
obstack_grow (&stk, text, len - 2);
unescape_to_obstack (text[len - 1]);
}
static void
multiline_add (char *s)
{
if (multiline_unescape)
{
for (; *s; s++)
{
if (*s == '\\')
{
unescape_to_obstack (s[1]);
++s;
}
else
obstack_1grow (&stk, *s);
}
}
else
gconf_line_add (s, strlen (s));
}
void
gconf_line_begin ()
{
/* FIXME: nothing so far. Maybe prepare stk by calling obstack_finish? */
}
static int
is_tab (char c)
{
return c == '\t';
}
static int
is_ws (char c)
{
return c == '\t' || c == ' ';
}
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 = xmalloc (multiline_delimiter_len + 1);
memcpy (multiline_delimiter, p, multiline_delimiter_len);
multiline_delimiter[multiline_delimiter_len] = 0;
gconf_line_begin ();
}
char *
gconf_line_finish ()
{
obstack_1grow (&stk, 0);
return obstack_finish (&stk);
}
static int
ident ()
{
char *p;
for (p = yytext; *p && isspace (*p); p++)
;
obstack_grow (&stk, p, strlen (p));
obstack_1grow (&stk, 0);
yylval.string = obstack_finish (&stk);
return IDENT;
}
void
gconf_lex_trace (int n)
{
yy_flex_debug = -n;
}
gconf_value_t *
gconf_value_dup(gconf_value_t *input)
{
gconf_value_t *ptr = obstack_alloc (&stk, sizeof (*ptr));
*ptr = *input;
return ptr;
}
static int
assign_locus (gconf_locus_t *ploc, char *name, char *line, size_t *pxlines)
{
char *p;
if (name)
{
if (pxlines && (!ploc->file || strcmp(name, ploc->file)))
*pxlines = 0;
ploc->file = gconf_install_text (name);
}
ploc->line = strtoul (line, &p, 10) - (pxlines ? *pxlines : 0);
return *p != 0;
}
static void
parse_line (char *text, gconf_locus_t *ploc, size_t *pxlines)
{
int rc = 1;
struct wordsplit ws;
if (wordsplit (text, &ws, WRDSF_DEFFLAGS))
gconf_error (ploc, 0, _("cannot parse #line line"));
else
{
if (ws.ws_wordc == 2)
rc = assign_locus (ploc, NULL, ws.ws_wordv[1], pxlines);
else if (ws.ws_wordc == 3)
rc = assign_locus (ploc, ws.ws_wordv[2], ws.ws_wordv[1], pxlines);
else if (ws.ws_wordc == 4)
{
rc = assign_locus (ploc, ws.ws_wordv[2], ws.ws_wordv[1], 0);
if (rc == 0)
{
char *p;
unsigned long x = strtoul (ws.ws_wordv[3], &p, 10);
rc = *p != 0;
if (rc == 0)
*pxlines = x;
}
}
else
gconf_error (ploc, 0, _("invalid #line statement"));
if (rc)
gconf_error (ploc, 0, _("malformed #line statement"));
wordsplit_free (&ws);
}
}
static void
parse_line_cpp (char *text, gconf_locus_t *ploc, size_t *pxlines)
{
struct wordsplit ws;
if (wordsplit (text, &ws, WRDSF_DEFFLAGS))
{
gconf_error (ploc, 0, _("cannot parse #line line"));
return;
}
else if (ws.ws_wordc < 3)
gconf_error (ploc, 0, _("invalid #line statement"));
else
{
if (assign_locus (ploc, ws.ws_wordv[2], ws.ws_wordv[1], pxlines))
gconf_error (ploc, 0, _("malformed #line statement"));
}
wordsplit_free (&ws);
}