%{
/* 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
#include
#include
#include
#include
#include
#if ENABLE_NLS
# include "gettext.h"
# define _(msgid) gettext (msgid)
#else
# define _(msgid) msgid
#endif
typedef union
{
struct sockaddr s;
struct sockaddr_in s_in;
struct sockaddr_un s_un;
} sockaddr_union_t;
static struct gconf_keyword config_keywords;
static struct gconf_keyword *cursect;
static gl_list_t sections;
int gconf_error_count;
int gconf_default_port = 0;
static void *target_ptr(struct gconf_keyword *kwp);
static void stmt_begin(struct gconf_keyword *kwp, gconf_value_t tag);
static void stmt_end(struct gconf_keyword *kwp);
static struct gconf_keyword *find_keyword(const char *ident);
static void process_ident(struct gconf_keyword *kwp, gconf_value_t *value);
static gl_list_t simple_list_create (bool dispose);
%}
%union {
char *string;
gconf_value_t value;
gl_list_t list;
struct gconf_keyword *kw;
}
%token IDENT STRING QSTRING MSTRING
%type string slist
%type slist0
%type value tag vallist
%type values list vlist
%type ident
%%
input : stmtlist
;
stmtlist: stmt
| stmtlist stmt
;
stmt : simple
| block
;
simple : ident vallist ';'
{
process_ident($1, &$2);
}
;
block : ident tag { stmt_begin($1, $2); } '{' stmtlist '}' opt_sc
{
stmt_end($1);
}
;
ident : IDENT
{
$$ = find_keyword($1);
if (!$$)
gconf_error(&gconf_current_locus, 0, _("Unknown keyword"));
}
;
tag : /* empty */
{
$$.type = GCONF_TYPE_STRING;
$$.v.string = NULL;
}
| value
;
vallist : vlist
{
size_t n;
if ((n = gl_list_size ($1)) == 1)
{
$$ = *(gconf_value_t *)gl_list_get_at ($1, 0);
}
else
{
size_t i;
$$.type = GCONF_TYPE_ARRAY;
$$.v.arg.c = n;
$$.v.arg.v = xcalloc (n, sizeof ($$.v.arg.v[0]));
for (i = 0; i < n; i++)
$$.v.arg.v[i] = *(gconf_value_t *)gl_list_get_at ($1, i);
}
gl_list_free ($1);
}
;
vlist : value
{
$$ = simple_list_create (false);
gl_list_add_last ($$, gconf_value_dup (&$1));
}
| vlist value
{
gl_list_add_last ($1, gconf_value_dup (&$2));
}
;
value : string
{
$$.type = GCONF_TYPE_STRING;
$$.v.string = $1;
}
| list
{
$$.type = GCONF_TYPE_LIST;
$$.v.list = $1;
}
| MSTRING
{
$$.type = GCONF_TYPE_STRING;
$$.v.string = $1;
}
;
string : STRING
| IDENT
| slist
;
slist : slist0
{
const void *p;
gl_list_iterator_t itr = gl_list_iterator ($1);
gconf_line_begin ();
while (gl_list_iterator_next (&itr, &p, NULL))
gconf_line_add (p, strlen (p));
$$ = gconf_line_finish ();
gl_list_iterator_free (&itr);
gl_list_free ($1);
}
;
slist0 : QSTRING
{
$$ = simple_list_create (false);
gl_list_add_last ($$, $1);
}
| slist0 QSTRING
{
gl_list_add_last ($1, $2);
$$ = $1;
}
;
list : '(' ')'
{
$$ = NULL;
}
| '(' values ')'
{
$$ = $2;
}
| '(' values ',' ')'
{
$$ = $2;
}
;
values : value
{
$$ = simple_list_create (true);
gl_list_add_last ($$, gconf_value_dup (&$1));
}
| values ',' value
{
gl_list_add_last ($1, gconf_value_dup (&$3));
$$ = $1;
}
;
opt_sc : /* empty */
| ';'
;
%%
int
yyerror(char *s)
{
gconf_error (&gconf_current_locus, 0, "%s", s);
return 0;
}
static void
listel_dispose(const void *el)
{
free((void*)el);
}
static gl_list_t
simple_list_create (bool dispose)
{
return gl_list_create_empty(&gl_linked_list_implementation,
NULL,
NULL,
dispose ? listel_dispose : NULL,
false);
}
void
gconf_warning(gconf_locus_t *locus, int errcode, const char *fmt, ...)
{
va_list ap;
char *buf = NULL;
va_start (ap, fmt);
vasprintf (&buf, fmt, ap);
va_end (ap);
gconf_print_diag (locus, 0, errcode, buf);
free(buf);
}
void
gconf_error (gconf_locus_t *locus, int errcode, const char *fmt, ...)
{
va_list ap;
char *buf = NULL;
va_start (ap, fmt);
vasprintf (&buf, fmt, ap);
va_end (ap);
gconf_print_diag (locus, 1, errcode, buf);
free (buf);
gconf_error_count++;
}
void
gconf_set_keywords (struct gconf_keyword *kwd)
{
config_keywords.kwd = kwd;
}
int
gconf_parse (const char *name)
{
int rc;
if (gconf_lex_begin (name))
return 1;
cursect = &config_keywords;
if (sections)
{
gl_list_free (sections);
sections = NULL;
}
rc = yyparse ();
gconf_lex_end ();
if (gconf_error_count)
rc = 1;
return rc;
}
void
gconf_gram_trace (int n)
{
yydebug = n;
}
static void *
target_ptr (struct gconf_keyword *kwp)
{
char *base;
if (kwp->varptr)
base = (char*) kwp->varptr + kwp->offset;
else if (cursect && cursect->callback_data)
base = (char*) cursect->callback_data + kwp->offset;
else
base = NULL;
return base;
}
static int
fake_callback (enum gconf_callback_command cmd,
gconf_locus_t *locus,
void *varptr,
gconf_value_t *value,
void *cb_data)
{
return 0;
}
static struct gconf_keyword fake = {
"*",
NULL,
NULL,
gconf_type_void,
NULL,
0,
fake_callback,
NULL,
&fake
};
static void
stmt_begin (struct gconf_keyword *kwp, gconf_value_t tag)
{
void *target;
if (!sections)
sections = simple_list_create (false);
gl_list_add_first (sections, cursect);
if (kwp)
{
target = target_ptr (kwp);
cursect = kwp;
if (kwp->callback && kwp->callback (gconf_callback_section_begin,
&gconf_current_locus, /* FIXME */
target,
&tag,
&kwp->callback_data))
cursect = &fake;
}
else
/* install "ignore-all" section */
cursect = kwp;
}
static void
stmt_end (struct gconf_keyword *kwp)
{
gconf_callback_fn callback = NULL;
void *dataptr = NULL;
if (cursect && cursect->callback)
{
callback = cursect->callback;
dataptr = &cursect->callback_data;
}
if (gl_list_size (sections) == 0)
abort ();
cursect = (struct gconf_keyword *) gl_list_get_at (sections, 0);
gl_list_remove_at (sections, 0);
if (callback)
callback (gconf_callback_section_end,
&gconf_current_locus, /* FIXME */
kwp ? target_ptr(kwp) : NULL,
NULL,
dataptr);
}
static struct gconf_keyword *
find_keyword (const char *ident)
{
struct gconf_keyword *kwp;
if (cursect && cursect != &fake)
{
for (kwp = cursect->kwd; kwp->ident; kwp++)
if (strcmp (kwp->ident, ident) == 0)
return kwp;
}
else
{
return &fake;
}
return NULL;
}
static int
string_to_signed (intmax_t *sval, const char *string,
intmax_t minval, intmax_t maxval)
{
intmax_t t;
char *p;
t = strtoimax (string, &p, 0);
if (*p)
{
gconf_error (&gconf_current_locus, 0, _("cannot convert `%s' to number"),
string);
return 1;
}
else if (t < minval || t > maxval)
{
gconf_error (&gconf_current_locus, 0,
_("%s: value out of allowed range %"PRIiMAX"..%"PRIiMAX),
string, minval, maxval);
return 1;
}
*sval = t;
return 0;
}
static int
string_to_unsigned (uintmax_t *sval, const char *string, uintmax_t maxval,
gconf_locus_t *loc)
{
uintmax_t t;
char *p;
t = strtoumax (string, &p, 0);
if (*p)
{
gconf_error (loc, 0, _("cannot convert `%s' to number"),
string);
return 1;
}
else if (t > maxval)
{
gconf_error (loc, 0,
_("%s: value out of allowed range 0..%"PRIuMAX),
string, maxval);
return 1;
}
*sval = t;
return 0;
}
static int
string_to_bool (const char *string, int *pval)
{
if (strcmp (string, "yes") == 0
|| strcmp (string, "true") == 0
|| strcmp (string, "t") == 0
|| strcmp (string, "1") == 0)
*pval = 1;
else if (strcmp (string, "no") == 0
|| strcmp (string, "false") == 0
|| strcmp (string, "nil") == 0
|| strcmp (string, "0") == 0)
*pval = 0;
else
{
gconf_error (&gconf_current_locus, 0,
_("%s: not a valid boolean value"),
string);
return 1;
}
return 0;
}
static int
string_to_host (struct in_addr *in, const char *string)
{
if (inet_aton (string, in) == 0)
{
struct hostent *hp;
hp = gethostbyname (string);
if (hp == NULL)
return 1;
memcpy (in, hp->h_addr, sizeof (struct in_addr));
}
return 0;
}
static int
string_to_sockaddr (struct gconf_sockaddr *sp, const char *string)
{
if (string[0] == '/')
{
struct sockaddr_un s_un;
if (strlen (string) >= sizeof (s_un.sun_path))
{
gconf_error (&gconf_current_locus, 0,
_("%s: UNIX socket name too long"),
string);
return 1;
}
s_un.sun_family = AF_UNIX;
strcpy (s_un.sun_path, string);
sp->len = sizeof (s_un);
sp->sa = xmalloc (sp->len);
memcpy (sp->sa, &s_un, sp->len);
}
else
{
char *p = strchr (string, ':');
size_t len;
struct sockaddr_in sa;
sa.sin_family = AF_INET;
if (p)
len = p - string;
else
len = strlen (string);
if (len == 0)
sa.sin_addr.s_addr = INADDR_ANY;
else
{
char *host = xmalloc (len + 1);
memcpy (host, string, len);
host[len] = 0;
if (string_to_host (&sa.sin_addr, host))
{
gconf_error (&gconf_current_locus, 0,
_("%s: not a valid IP address or hostname"),
host);
free (host);
return 1;
}
free (host);
}
if (p)
{
struct servent *serv;
p++;
serv = getservbyname (p, "tcp");
if (serv != NULL)
sa.sin_port = serv->s_port;
else
{
unsigned long l;
char *q;
/* Not in services, maybe a number? */
l = strtoul (p, &q, 0);
if (*q || l > USHRT_MAX)
{
gconf_error (&gconf_current_locus, 0,
_("%s: not a valid port number"), p);
return 1;
}
sa.sin_port = htons (l);
}
}
else if (gconf_default_port)
sa.sin_port = gconf_default_port;
else
{
gconf_error (&gconf_current_locus, 0, _("missing port number"));
return 1;
}
sp->len = sizeof (sa);
sp->sa = xmalloc (sp->len);
memcpy (sp->sa, &sa, sp->len);
}
return 0;
}
static int
string_convert (void *target, enum gconf_data_type type, const char *string)
{
uintmax_t uval;
intmax_t sval;
switch (type)
{
case gconf_type_void:
abort ();
case gconf_type_string:
*(const char**)target = string;
break;
case gconf_type_short:
if (string_to_signed (&sval, string, SHRT_MIN, SHRT_MAX) == 0)
*(short*)target = sval;
else
return 1;
break;
case gconf_type_ushort:
if (string_to_unsigned (&uval, string, USHRT_MAX, &gconf_current_locus) == 0)
*(unsigned short*)target = uval;
else
return 1;
break;
case gconf_type_bool:
return string_to_bool (string, (int*)target);
case gconf_type_int:
if (string_to_signed (&sval, string, INT_MIN, INT_MAX) == 0)
*(int*)target = sval;
else
return 1;
break;
case gconf_type_uint:
if (string_to_unsigned (&uval, string, UINT_MAX, &gconf_current_locus) == 0)
*(unsigned int*)target = uval;
else
return 1;
break;
case gconf_type_long:
if (string_to_signed (&sval, string, LONG_MIN, LONG_MAX) == 0)
*(long*)target = sval;
else
return 1;
break;
case gconf_type_ulong:
if (string_to_unsigned (&uval, string, ULONG_MAX, &gconf_current_locus) == 0)
*(unsigned long*)target = uval;
else
return 1;
break;
case gconf_type_size:
if (string_to_unsigned (&uval, string, SIZE_MAX, &gconf_current_locus) == 0)
*(size_t*)target = uval;
else
return 1;
break;
case gconf_type_intmax:
return string_to_signed ((intmax_t*)target, string,
INTMAX_MIN, INTMAX_MAX);
case gconf_type_uintmax:
return string_to_unsigned ((uintmax_t*)target, string, UINTMAX_MAX,
&gconf_current_locus);
case gconf_type_time:
/*FIXME: Use getdate */
if (string_to_unsigned (&uval, string, (time_t)-1, &gconf_current_locus) == 0)
*(time_t*)target = uval;
else
return 1;
break;
case gconf_type_ipv4:
if (inet_aton (string, (struct in_addr *)target))
{
gconf_error (&gconf_current_locus, 0, _("%s: not a valid IP address"), string);
return 1;
}
break;
case gconf_type_host:
if (string_to_host ((struct in_addr *)target, string))
{
gconf_error (&gconf_current_locus, 0,
_("%s: not a valid IP address or hostname"), string);
return 1;
}
break;
case gconf_type_sockaddr:
return string_to_sockaddr ((struct gconf_sockaddr *)target, string);
/* FIXME: */
case gconf_type_cidr:
case gconf_type_section:
gconf_error (&gconf_current_locus, 0, _("INTERNAL ERROR at %s:%d"), __FILE__,
__LINE__);
abort();
}
return 0;
}
size_t gconf_type_size_tab[] = {
0 /* gconf_type_void */,
sizeof (char*) /* gconf_type_string */,
sizeof (short) /* gconf_type_short */,
sizeof (unsigned short) /* gconf_type_ushort */,
sizeof (int) /* gconf_type_int */,
sizeof (unsigned int) /* gconf_type_uint */,
sizeof (long) /* gconf_type_long */,
sizeof (unsigned long) /* gconf_type_ulong */,
sizeof (size_t) /* gconf_type_size */,
/* gconf_type_off,*/
sizeof (uintmax_t) /* gconf_type_uintmax */,
sizeof (intmax_t) /* gconf_type_intmax */,
sizeof (time_t) /* gconf_type_time */,
sizeof (int) /* gconf_type_bool */,
sizeof (struct in_addr) /* gconf_type_ipv4 */,
0 /* FIXME: gconf_type_cidr */,
sizeof (struct in_addr) /* gconf_type_host */,
sizeof (struct gconf_sockaddr) /* gconf_type_sockaddr */,
0 /* gconf_type_section */
};
#define gconf_type_size_count \
(sizeof (gconf_type_size_tab) / sizeof (gconf_type_size_tab[0]))
static void
process_ident (struct gconf_keyword *kwp, gconf_value_t *value)
{
void *target;
if (!kwp)
return;
target = target_ptr (kwp);
if (kwp->callback)
kwp->callback (gconf_callback_set_value,
&gconf_current_locus, /* FIXME */
target,
value,
&kwp->callback_data);
else if (value->type == GCONF_TYPE_ARRAY)
{
gconf_error (&gconf_current_locus, 0,
_("too many arguments to `%s'; missing semicolon?"),
kwp->ident);
return;
}
else if (value->type == GCONF_TYPE_LIST)
{
if (GCONF_IS_LIST (kwp->type))
{
gl_list_iterator_t itr = gl_list_iterator (value->v.list);
enum gconf_data_type type = GCONF_TYPE (kwp->type);
int num = 1;
const void *p;
gl_list_t list = simple_list_create (true);
while (gl_list_iterator_next (&itr, &p, NULL))
{
const gconf_value_t *vp = p;
size_t size;
if (type >= gconf_type_size_count
|| (size = gconf_type_size_tab[type]) == 0)
{
gconf_error (&gconf_current_locus, 0,
_("INTERNAL ERROR at %s:%d: "
"unhandled data type %d"),
__FILE__, __LINE__, type);
abort ();
}
if (vp->type != GCONF_TYPE_STRING)
gconf_error (&gconf_current_locus, 0,
_("%s: incompatible data type in list item #%d"),
kwp->ident, num);
else
{
void *ptr = xmalloc (size);
if (string_convert (ptr, type, vp->v.string) == 0)
gl_list_add_last (list, ptr);
else
free (ptr);
}
}
gl_list_iterator_free (&itr);
*(gl_list_t*)target = list;
}
else
{
gconf_error (&gconf_current_locus, 0, _("incompatible data type for `%s'"),
kwp->ident);
return;
}
}
else if (GCONF_IS_LIST (kwp->type))
{
gl_list_t list = simple_list_create (true);
enum gconf_data_type type = GCONF_TYPE (kwp->type);
size_t size;
void *ptr;
if (type >= gconf_type_size_count
|| (size = gconf_type_size_tab[type]) == 0)
{
gconf_error (&gconf_current_locus, 0,
_("INTERNAL ERROR at %s:%d: unhandled data type %d"),
__FILE__, __LINE__, type);
abort();
}
ptr = xmalloc (size);
if (string_convert (ptr, type, value->v.string))
{
free (ptr);
gl_list_free (list);
return;
}
gl_list_add_last (list, ptr);
*(gl_list_t*)target = list;
}
else
string_convert (target, GCONF_TYPE (kwp->type), value->v.string);
}