aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-04-20 14:48:39 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-04-20 14:48:39 +0300
commit0f081a2643ba0a7f7a5dcfb6a5977d9da1d2b6db (patch)
treeb62e9404cd5dfdfa471ec1202e183f9ca92187e5 /src
parent0983c9ab7a6ea5b3592a297e029a935cc0e4bebc (diff)
downloadgrecs-0f081a2643ba0a7f7a5dcfb6a5977d9da1d2b6db.tar.gz
grecs-0f081a2643ba0a7f7a5dcfb6a5977d9da1d2b6db.tar.bz2
Diverge from Wydawca gconf/ subdirectory into a separate project
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am35
-rw-r--r--src/format.c200
-rw-r--r--src/grecs-gram.y891
-rw-r--r--src/grecs-lex.l477
-rw-r--r--src/grecs.h163
-rw-r--r--src/pp-setup106
-rw-r--r--src/preproc.c728
-rw-r--r--src/text.c73
-rw-r--r--src/wordsplit.c568
-rw-r--r--src/wordsplit.h88
10 files changed, 3329 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..df98842
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,35 @@
+# This file is part of grecs - Gray's Extensible Configuration System
+# Copyright (C) 2007, 2009 Sergey Poznyakoff
+#
+# Grex 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.
+#
+# Grex 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 Grex. If not, see <http://www.gnu.org/licenses/>.
+
+noinst_LIBRARIES=libgrecs.a
+libgrecs_a_SOURCES = \
+ format.c\
+ grecs-gram.y\
+ grecs-lex.l\
+ preproc.c\
+ text.c\
+ grecs.h\
+ wordsplit.c\
+ wordsplit.h
+
+EXTRA_DIST=gram.h
+
+INCLUDES = -I$(top_srcdir)/gnu -I../gnu
+AM_YFLAGS = -dtv
+AM_LFLAGS = -dvp
+
+incdir=$(pkgdatadir)/$(VERSION)/include
+inc_DATA = $(PP_SETUP_FILE)
diff --git a/src/format.c b/src/format.c
new file mode 100644
index 0000000..165c688
--- /dev/null
+++ b/src/format.c
@@ -0,0 +1,200 @@
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
+
+ Grecs 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.
+
+ Grecs 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 Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <grecs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#if ENABLE_NLS
+# include "gettext.h"
+#else
+# define gettext(s) s
+#endif
+
+#define _(s) gettext (s)
+#define N_(s) s
+
+const char *
+grecs_data_type_string (enum grecs_data_type type)
+{
+ switch (type)
+ {
+ case grecs_type_void:
+ return "void";
+
+ case grecs_type_string:
+ return "string";
+
+ case grecs_type_short:
+ case grecs_type_ushort:
+ case grecs_type_int:
+ case grecs_type_uint:
+ case grecs_type_long:
+ case grecs_type_ulong:
+ case grecs_type_size:
+/* case grecs_type_off:*/
+ case grecs_type_uintmax:
+ case grecs_type_intmax:
+ return "number";
+
+ case grecs_type_time:
+ return "time";
+
+ case grecs_type_bool:
+ return "boolean";
+
+ case grecs_type_ipv4:
+ return "IPv4";
+
+ case grecs_type_cidr:
+ return "CIDR";
+
+ case grecs_type_host:
+ return "hostname";
+
+ case grecs_type_sockaddr:
+ return "sock-addr";
+
+ case grecs_type_section:
+ return "section";
+ }
+ return "UNKNOWN?";
+}
+
+static void
+format_level (FILE *stream, unsigned level)
+{
+ while (level--)
+ fprintf (stream, " ");
+}
+
+void
+grecs_format_docstring (FILE *stream, const char *docstring, unsigned level)
+{
+ size_t len = strlen (docstring);
+ int width = 78 - level * 2;
+
+ if (width < 0)
+ {
+ width = 78;
+ level = 0;
+ }
+
+ while (len)
+ {
+ size_t seglen;
+ const char *p;
+
+ for (seglen = 0, p = docstring; p < docstring + width && *p; p++)
+ {
+ if (*p == '\n')
+ {
+ seglen = p - docstring;
+ break;
+ }
+ if (isspace (*p))
+ seglen = p - docstring;
+ }
+ if (seglen == 0 || *p == 0)
+ seglen = p - docstring;
+
+ format_level (stream, level);
+ fprintf (stream, "# ");
+ fwrite (docstring, seglen, 1, stream);
+ fputc ('\n', stream);
+ len -= seglen;
+ docstring += seglen;
+ if (*docstring == '\n')
+ {
+ docstring++;
+ len--;
+ }
+ else
+ while (*docstring && isspace (*docstring))
+ {
+ docstring++;
+ len--;
+ }
+ }
+}
+
+void
+grecs_format_simple_statement (FILE *stream, struct grecs_keyword *kwp,
+ unsigned level)
+{
+ const char *argstr;
+
+ if (kwp->docstring)
+ grecs_format_docstring (stream, kwp->docstring, level);
+ format_level (stream, level);
+
+ if (kwp->argname)
+ argstr = kwp->argname;
+ else
+ argstr = N_("arg");
+
+ if (strchr ("<[", argstr[0]))
+ fprintf (stream, "%s %s;\n", kwp->ident, gettext (argstr));
+ else if (strchr (argstr, ':'))
+ fprintf (stream, "%s <%s>;\n", kwp->ident, gettext (argstr));
+ else
+ {
+ fprintf (stream, "%s <%s: ", kwp->ident, gettext (argstr));
+ if (GCONF_IS_LIST (kwp->type))
+ fprintf (stream, "list of %s",
+ gettext (grecs_data_type_string (GCONF_TYPE (kwp->type))));
+ else
+ fprintf (stream, "%s", gettext (grecs_data_type_string (kwp->type)));
+ fprintf (stream, ">;\n");
+ }
+}
+
+void
+grecs_format_block_statement (FILE *stream, struct grecs_keyword *kwp,
+ unsigned level)
+{
+ if (kwp->docstring)
+ grecs_format_docstring (stream, kwp->docstring, level);
+ format_level (stream, level);
+ fprintf (stream, "%s", kwp->ident);
+ if (kwp->argname)
+ fprintf (stream, " <%s>", gettext (kwp->argname));
+ fprintf (stream, " {\n");
+ grecs_format_statement_array (stream, kwp->kwd, 0, level + 1);
+ format_level (stream, level);
+ fprintf (stream, "}\n");
+}
+
+void
+grecs_format_statement_array (FILE *stream, struct grecs_keyword *kwp,
+ unsigned n,
+ unsigned level)
+{
+ for (; kwp->ident; kwp++, n++)
+ {
+ if (n)
+ fputc ('\n', stream);
+ if (kwp->type == grecs_type_section)
+ grecs_format_block_statement (stream, kwp, level);
+ else
+ grecs_format_simple_statement (stream, kwp, level);
+ }
+}
diff --git a/src/grecs-gram.y b/src/grecs-gram.y
new file mode 100644
index 0000000..991f315
--- /dev/null
+++ b/src/grecs-gram.y
@@ -0,0 +1,891 @@
+%{
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
+
+ Grecs 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.
+
+ Grecs 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 Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <grecs.h>
+#include <grecs-gram.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <xalloc.h>
+#include <inttypes.h>
+
+#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 grecs_keyword config_keywords;
+static struct grecs_keyword *cursect;
+static gl_list_t sections;
+int grecs_error_count;
+
+int grecs_default_port = 0;
+
+static void *target_ptr(struct grecs_keyword *kwp);
+static void stmt_begin(struct grecs_keyword *kwp, grecs_value_t tag);
+static void stmt_end(struct grecs_keyword *kwp);
+static struct grecs_keyword *find_keyword(const char *ident);
+
+static void process_ident(struct grecs_keyword *kwp, grecs_value_t *value);
+static gl_list_t simple_list_create (bool dispose);
+%}
+
+%union {
+ char *string;
+ grecs_value_t value;
+ gl_list_t list;
+ struct grecs_keyword *kw;
+}
+
+%token <string> IDENT STRING QSTRING MSTRING
+%type <string> string slist
+%type <list> slist0
+%type <value> value tag vallist
+%type <list> values list vlist
+%type <kw> ident
+
+%%
+
+input : stmtlist
+ ;
+
+stmtlist: stmt
+ | stmtlist stmt
+ ;
+
+stmt : simple
+ | block
+ ;
+
+simple : ident vallist ';'
+ {
+ process_ident($1, &$2);
+ }
+ ;
+
+block : ident tag { stmt_begin($<kw>1, $<value>2); } '{' stmtlist '}' opt_sc
+ {
+ stmt_end($1);
+ }
+ ;
+
+ident : IDENT
+ {
+ $$ = find_keyword($1);
+ if (!$$)
+ grecs_error(&grecs_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)
+ {
+ $$ = *(grecs_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] = *(grecs_value_t *)gl_list_get_at ($1, i);
+ }
+ gl_list_free ($1);
+ }
+ ;
+
+vlist : value
+ {
+ $$ = simple_list_create (false);
+ gl_list_add_last ($$, grecs_value_dup (&$1));
+ }
+ | vlist value
+ {
+ gl_list_add_last ($1, grecs_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);
+
+ grecs_line_begin ();
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ grecs_line_add (p, strlen (p));
+ $$ = grecs_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 ($$, grecs_value_dup (&$1));
+ }
+ | values ',' value
+ {
+ gl_list_add_last ($1, grecs_value_dup (&$3));
+ $$ = $1;
+ }
+ ;
+
+opt_sc : /* empty */
+ | ';'
+ ;
+
+%%
+
+int
+yyerror(char *s)
+{
+ grecs_error (&grecs_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
+grecs_warning(grecs_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);
+ grecs_print_diag (locus, 0, errcode, buf);
+ free(buf);
+}
+
+void
+grecs_error (grecs_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);
+ grecs_print_diag (locus, 1, errcode, buf);
+ free (buf);
+ grecs_error_count++;
+}
+
+void
+grecs_set_keywords (struct grecs_keyword *kwd)
+{
+ config_keywords.kwd = kwd;
+}
+
+int
+grecs_parse (const char *name)
+{
+ int rc;
+ if (grecs_lex_begin (name))
+ return 1;
+ cursect = &config_keywords;
+ if (sections)
+ {
+ gl_list_free (sections);
+ sections = NULL;
+ }
+ rc = yyparse ();
+ grecs_lex_end ();
+ if (grecs_error_count)
+ rc = 1;
+ return rc;
+}
+
+void
+grecs_gram_trace (int n)
+{
+ yydebug = n;
+}
+
+
+
+static void *
+target_ptr (struct grecs_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 grecs_callback_command cmd,
+ grecs_locus_t *locus,
+ void *varptr,
+ grecs_value_t *value,
+ void *cb_data)
+{
+ return 0;
+}
+
+static struct grecs_keyword fake = {
+ "*",
+ NULL,
+ NULL,
+ grecs_type_void,
+ NULL,
+ 0,
+ fake_callback,
+ NULL,
+ &fake
+};
+
+static void
+stmt_begin (struct grecs_keyword *kwp, grecs_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 (grecs_callback_section_begin,
+ &grecs_current_locus, /* FIXME */
+ target,
+ &tag,
+ &kwp->callback_data))
+ cursect = &fake;
+ }
+ else
+ /* install "ignore-all" section */
+ cursect = kwp;
+}
+
+static void
+stmt_end (struct grecs_keyword *kwp)
+{
+ grecs_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 grecs_keyword *) gl_list_get_at (sections, 0);
+ gl_list_remove_at (sections, 0);
+ if (callback)
+ callback (grecs_callback_section_end,
+ &grecs_current_locus, /* FIXME */
+ kwp ? target_ptr(kwp) : NULL,
+ NULL,
+ dataptr);
+
+}
+
+static struct grecs_keyword *
+find_keyword (const char *ident)
+{
+ struct grecs_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)
+ {
+ grecs_error (&grecs_current_locus, 0, _("cannot convert `%s' to number"),
+ string);
+ return 1;
+ }
+ else if (t < minval || t > maxval)
+ {
+ grecs_error (&grecs_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,
+ grecs_locus_t *loc)
+{
+ uintmax_t t;
+ char *p;
+
+ t = strtoumax (string, &p, 0);
+ if (*p)
+ {
+ grecs_error (loc, 0, _("cannot convert `%s' to number"),
+ string);
+ return 1;
+ }
+ else if (t > maxval)
+ {
+ grecs_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
+ {
+ grecs_error (&grecs_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 grecs_sockaddr *sp, const char *string)
+{
+ if (string[0] == '/')
+ {
+ struct sockaddr_un s_un;
+ if (strlen (string) >= sizeof (s_un.sun_path))
+ {
+ grecs_error (&grecs_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))
+ {
+ grecs_error (&grecs_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)
+ {
+ grecs_error (&grecs_current_locus, 0,
+ _("%s: not a valid port number"), p);
+ return 1;
+ }
+ sa.sin_port = htons (l);
+ }
+ }
+ else if (grecs_default_port)
+ sa.sin_port = grecs_default_port;
+ else
+ {
+ grecs_error (&grecs_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;
+}
+
+int
+grecs_string_convert (void *target, enum grecs_data_type type,
+ const char *string)
+{
+ uintmax_t uval;
+ intmax_t sval;
+
+ switch (type)
+ {
+ case grecs_type_void:
+ abort ();
+
+ case grecs_type_string:
+ *(const char**)target = string;
+ break;
+
+ case grecs_type_short:
+ if (string_to_signed (&sval, string, SHRT_MIN, SHRT_MAX) == 0)
+ *(short*)target = sval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_ushort:
+ if (string_to_unsigned (&uval, string, USHRT_MAX, &grecs_current_locus) == 0)
+ *(unsigned short*)target = uval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_bool:
+ return string_to_bool (string, (int*)target);
+
+ case grecs_type_int:
+ if (string_to_signed (&sval, string, INT_MIN, INT_MAX) == 0)
+ *(int*)target = sval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_uint:
+ if (string_to_unsigned (&uval, string, UINT_MAX, &grecs_current_locus) == 0)
+ *(unsigned int*)target = uval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_long:
+ if (string_to_signed (&sval, string, LONG_MIN, LONG_MAX) == 0)
+ *(long*)target = sval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_ulong:
+ if (string_to_unsigned (&uval, string, ULONG_MAX, &grecs_current_locus) == 0)
+ *(unsigned long*)target = uval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_size:
+ if (string_to_unsigned (&uval, string, SIZE_MAX, &grecs_current_locus) == 0)
+ *(size_t*)target = uval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_intmax:
+ return string_to_signed ((intmax_t*)target, string,
+ INTMAX_MIN, INTMAX_MAX);
+
+ case grecs_type_uintmax:
+ return string_to_unsigned ((uintmax_t*)target, string, UINTMAX_MAX,
+ &grecs_current_locus);
+
+ case grecs_type_time:
+ /*FIXME: Use getdate */
+ if (string_to_unsigned (&uval, string, (time_t)-1, &grecs_current_locus) == 0)
+ *(time_t*)target = uval;
+ else
+ return 1;
+ break;
+
+ case grecs_type_ipv4:
+ if (inet_aton (string, (struct in_addr *)target))
+ {
+ grecs_error (&grecs_current_locus, 0, _("%s: not a valid IP address"), string);
+ return 1;
+ }
+ break;
+
+ case grecs_type_host:
+ if (string_to_host ((struct in_addr *)target, string))
+ {
+ grecs_error (&grecs_current_locus, 0,
+ _("%s: not a valid IP address or hostname"), string);
+ return 1;
+ }
+ break;
+
+ case grecs_type_sockaddr:
+ return string_to_sockaddr ((struct grecs_sockaddr *)target, string);
+
+ /* FIXME: */
+ case grecs_type_cidr:
+ grecs_error (&grecs_current_locus, 0, _("INTERNAL ERROR at %s:%d"), __FILE__,
+ __LINE__);
+ abort();
+
+ case grecs_type_section:
+ grecs_error (&grecs_current_locus, 0,
+ _("Invalid use of block statement"));
+ return 1;
+ }
+ return 0;
+}
+
+struct grecs_prop
+{
+ size_t size;
+ gl_listelement_equals_fn eqfn;
+};
+
+static bool
+string_eq (const void *elt1, const void *elt2)
+{
+ return strcmp ((const char *)elt1, (const char *)elt2) == 0;
+}
+
+#define __grecs_name_cat__(a,b) a ## b
+#define NUMEQ(type) __grecs_name_cat__(type,_eq)
+#define __DECL_NUMEQ(type,ctype) \
+ static bool \
+ NUMEQ(type) (const void *elt1, const void *elt2) \
+ { \
+ return memcmp (elt1, elt2, sizeof (ctype)) == 0; \
+ }
+#define DECL_NUMEQ(type) __DECL_NUMEQ(type,type)
+
+DECL_NUMEQ(short)
+DECL_NUMEQ(int)
+DECL_NUMEQ(long)
+DECL_NUMEQ(size_t)
+DECL_NUMEQ(uintmax_t)
+DECL_NUMEQ(intmax_t)
+DECL_NUMEQ(time_t)
+__DECL_NUMEQ(in_addr, struct in_addr)
+__DECL_NUMEQ(grecs_sockaddr, struct grecs_sockaddr)
+
+struct grecs_prop grecs_prop_tab[] = {
+ { 0, NULL }, /* grecs_type_void */
+ { sizeof (char*), string_eq }, /* grecs_type_string */
+ { sizeof (short), NUMEQ (short) }, /* grecs_type_short */
+ { sizeof (unsigned short), NUMEQ (short) }, /* grecs_type_ushort */
+ { sizeof (int), NUMEQ (int) }, /* grecs_type_int */
+ { sizeof (unsigned int), NUMEQ (int) }, /* grecs_type_uint */
+ { sizeof (long), NUMEQ (long) }, /* grecs_type_long */
+ { sizeof (unsigned long), NUMEQ (long) }, /* grecs_type_ulong */
+ { sizeof (size_t), NUMEQ (size_t) }, /* grecs_type_size */
+ /* grecs_type_off,*/
+ { sizeof (uintmax_t), NUMEQ (uintmax_t) }, /* grecs_type_uintmax */
+ { sizeof (intmax_t), NUMEQ (intmax_t) }, /* grecs_type_intmax */
+ { sizeof (time_t), NUMEQ (time_t) }, /* grecs_type_time */
+ { sizeof (int), NUMEQ (int) }, /* grecs_type_bool */
+ { sizeof (struct in_addr), NUMEQ (in_addr) }, /* grecs_type_ipv4 */
+ { 0, NULL }, /* FIXME: grecs_type_cidr */
+ { sizeof (struct in_addr), NUMEQ (in_addr) }, /* grecs_type_host */
+ { sizeof (struct grecs_sockaddr), NUMEQ (grecs_sockaddr) },
+ /* grecs_type_sockaddr */
+ { 0, NULL } /* grecs_type_section */
+};
+#define grecs_prop_count \
+ (sizeof (grecs_prop_tab) / sizeof (grecs_prop_tab[0]))
+
+static void
+process_ident (struct grecs_keyword *kwp, grecs_value_t *value)
+{
+ void *target;
+
+ if (!kwp)
+ return;
+
+ target = target_ptr (kwp);
+
+ if (kwp->callback)
+ kwp->callback (grecs_callback_set_value,
+ &grecs_current_locus, /* FIXME */
+ target,
+ value,
+ &kwp->callback_data);
+ else if (value->type == GCONF_TYPE_ARRAY)
+ {
+ grecs_error (&grecs_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 grecs_data_type type = GCONF_TYPE (kwp->type);
+ int num = 1;
+ const void *p;
+ gl_list_t list;
+ size_t size;
+
+ if (type >= grecs_prop_count
+ || (size = grecs_prop_tab[type].size) == 0)
+ {
+ grecs_error (&grecs_current_locus, 0,
+ _("INTERNAL ERROR at %s:%d: "
+ "unhandled data type %d"),
+ __FILE__, __LINE__, type);
+ abort ();
+ }
+
+ list = gl_list_create_empty (&gl_linked_list_implementation,
+ grecs_prop_tab[type].eqfn,
+ NULL,
+ listel_dispose,
+ false);
+
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ const grecs_value_t *vp = p;
+
+ if (vp->type != GCONF_TYPE_STRING)
+ grecs_error (&grecs_current_locus, 0,
+ _("%s: incompatible data type in list item #%d"),
+ kwp->ident, num);
+ else if (type == grecs_type_string)
+ gl_list_add_last (list, vp->v.string);
+ else
+ {
+ void *ptr = xmalloc (size);
+ if (grecs_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
+ {
+ grecs_error (&grecs_current_locus, 0,
+ _("incompatible data type for `%s'"),
+ kwp->ident);
+ return;
+ }
+ }
+ else if (GCONF_IS_LIST (kwp->type))
+ {
+ gl_list_t list;
+ enum grecs_data_type type = GCONF_TYPE (kwp->type);
+ size_t size;
+ void *ptr;
+
+ if (type >= grecs_prop_count
+ || (size = grecs_prop_tab[type].size) == 0)
+ {
+ grecs_error (&grecs_current_locus, 0,
+ _("INTERNAL ERROR at %s:%d: unhandled data type %d"),
+ __FILE__, __LINE__, type);
+ abort();
+ }
+
+ list = gl_list_create_empty (&gl_linked_list_implementation,
+ grecs_prop_tab[type].eqfn,
+ NULL,
+ listel_dispose,
+ false);
+ if (type == grecs_type_string)
+ gl_list_add_last (list, value->v.string);
+ else
+ {
+ ptr = xmalloc (size);
+ if (grecs_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
+ grecs_string_convert (target, GCONF_TYPE (kwp->type), value->v.string);
+}
+
diff --git a/src/grecs-lex.l b/src/grecs-lex.l
new file mode 100644
index 0000000..4d7f9bb
--- /dev/null
+++ b/src/grecs-lex.l
@@ -0,0 +1,477 @@
+/* grecs - Gray's Extensible Configuration System -*- c -*- */
+%{
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
+
+ Grecs 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.
+
+ Grecs 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 Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <grecs.h>
+#include <grecs-gram.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+#include <obstack.h>
+#include <xalloc.h>
+#include <wordsplit.h>
+
+#if ENABLE_NLS
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) msgid
+#endif
+
+static char *multiline_delimiter;
+static size_t multiline_delimiter_le