aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-02-16 01:30:44 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-02-16 01:37:24 +0200
commit4ea235014b22e357c7a6e283ce52b80a1778c0bd (patch)
tree7b79814ed11bcb02d08a13d92138ea9215778723
downloadgrecs-4ea235014b22e357c7a6e283ce52b80a1778c0bd.tar.gz
grecs-4ea235014b22e357c7a6e283ce52b80a1778c0bd.tar.bz2
Rewrite configuration parser, drop dependency on GSC
* bootstrap: Replaced with a modified version from gnulib. * configure.ac: Bump version to 1.9.90 (--without-preprocessor): New option Require Mailutils 2.0 (AC_CONFIG_FILES): Remove lib, add gconf * gconf/: New directory. Contains general-purpose configuration file parser, distilled from Dico and Mailutils. * src/Makefile.am (wydawca_SOURCES): Add interval.c * src/pp-setup, src/update-2.0.awk: New files. * src/config.c: Full rewrite. * src/exec.c (start_prog): Use getdtablesize unconditionally. * src/mail.c: Keep templates in a hash table. Template references begin with a single @ * src/process.c, src/triplet.c, src/verify.c: Reflect changes to struct directory_pair * src/wydawca.c: Change configuration parsing. * src/wydawca.h (enum access_method_id): New constants (struct directory_pair): Replace four access methods with an array. * Makefile.am (SUBDIRS): Remove lib, add gconf * .gitignore, NEWS, doc/.gitignore, src/.gitignore
-rw-r--r--.gitignore4
-rw-r--r--Makefile.am17
-rw-r--r--argcv.c632
-rw-r--r--argcv.h79
-rw-r--r--gconf-format.c200
-rw-r--r--gconf-gram.y819
-rw-r--r--gconf-lex.l479
-rw-r--r--gconf-preproc.c727
-rw-r--r--gconf-text.c73
-rw-r--r--gconf.h153
-rw-r--r--gnulib.modules13
11 files changed, 3196 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42c88b2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+gconf-gram.c
+gconf-gram.h
+gconf-gram.output
+gconf-lex.c
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..4f50bd6
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,17 @@
+noinst_LIBRARIES=libgconf.a
+libgconf_a_SOURCES = \
+ argcv.c\
+ argcv.h\
+ gconf-format.c\
+ gconf-gram.y\
+ gconf-lex.l\
+ gconf-preproc.c\
+ gconf-text.c\
+ gconf.h
+
+EXTRA_DIST=gconf-gram.h
+
+INCLUDES = -I$(top_srcdir)/gnu -I../gnu
+AM_YFLAGS = -dtv
+AM_LFLAGS = -dvp
+
diff --git a/argcv.c b/argcv.c
new file mode 100644
index 0000000..d5f2bda
--- /dev/null
+++ b/argcv.c
@@ -0,0 +1,632 @@
+/* argcv.c - simple functions for parsing input based on whitespace
+ Copyright (C) 1999, 2000, 2001, 2003, 2004,
+ 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <c-ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <argcv.h>
+
+enum argcv_quoting_style argcv_quoting_style;
+
+#define _ARGCV_WORD_SED_EXPR 0x1000
+#define _ARGCV_WORD_MASK 0xf000
+
+/*
+ * takes a string and splits it into several strings, breaking at ' '
+ * command is the string to split
+ * the number of strings is placed into argc
+ * the split strings are put into argv
+ * returns 0 on success, nonzero on failure
+ */
+
+#define isws(c) ((c)==' '||(c)=='\t'||(c)=='\n')
+#define isdelim(c,delim) (strchr(delim,(c))!=NULL)
+
+struct argcv_info
+{
+ int len;
+ const char *command;
+ const char *delim;
+ const char *comment;
+ int flags;
+
+ int start;
+ int end;
+ int save;
+ int finish_pos;
+};
+
+static void
+init_argcv_info (struct argcv_info *ap, int flags,
+ int len, const char *command, const char *delim,
+ const char *comment)
+{
+ memset (ap, 0, sizeof *ap);
+ ap->len = len;
+ ap->command = command;
+ ap->delim = delim;
+ ap->comment = comment;
+ ap->flags = flags;
+}
+
+static int
+skip_sed_expr(const char *command, int i, int len)
+{
+ int state;
+
+ do
+ {
+ int delim;
+
+ if (command[i] == ';')
+ i++;
+ if (!(command[i] == 's' && i + 3 < len && c_ispunct(command[i+1])))
+ break;
+
+ delim = command[++i];
+ state = 1;
+ for (i++; i < len; i++)
+ {
+ if (state == 3)
+ {
+ if (command[i] == delim || !c_isalnum(command[i]))
+ break;
+ }
+ else if (command[i] == '\\')
+ i++;
+ else if (command[i] == delim)
+ state++;
+ }
+ }
+ while (state == 3 && i < len && command[i] == ';');
+ i--;
+ return i;
+}
+
+static int
+argcv_scan (struct argcv_info *ap)
+{
+ int i = 0;
+ int len = ap->len;
+ const char *command = ap->command;
+ const char *delim = ap->delim;
+ const char *comment = ap->comment;
+
+ for (;;)
+ {
+ i = ap->save;
+
+ if (i >= len)
+ return i + 1;
+
+ if (ap->flags & ARGCV_WS)
+ {
+ /* Skip initial whitespace */
+ while (i < len && isws (command[i]))
+ i++;
+ }
+ ap->start = i;
+
+ ap->flags &= ~_ARGCV_WORD_MASK;
+
+ if (ap->flags & ARGCV_SED_EXPR
+ && command[i] == 's' && i + 3 < len && c_ispunct(command[i+1]))
+ {
+ ap->flags |= _ARGCV_WORD_SED_EXPR;
+ i = skip_sed_expr(command, i, len);
+ }
+ else if (!isdelim (command[i], delim))
+ {
+ while (i < len)
+ {
+ if (ap->flags & ARGCV_QUOTE)
+ {
+ if (command[i] == '\\')
+ {
+ if (++i == len)
+ break;
+ i++;
+ continue;
+ }
+
+ if (command[i] == '\'' || command[i] == '"')
+ {
+ int j;
+ for (j = i + 1; j < len && command[j] != command[i]; j++)
+ if (command[j] == '\\')
+ j++;
+ if (j < len)
+ i = j + 1;
+ else
+ i++;
+ continue;
+ }
+ }
+ if ((ap->flags & ARGCV_WS && isws (command[i]))
+ || isdelim (command[i], delim))
+ break;
+ else
+ i++;
+ }
+ i--;
+ }
+ else if (!(ap->flags & ARGCV_RETURN_DELIMS))
+ {
+ if (ap->flags & ARGCV_SQUEEZE_DELIMS)
+ while (i < len && isdelim (command[i], delim))
+ i++;
+ else if (i < len)
+ i++;
+
+ ap->save = i;
+ continue;
+ }
+
+
+ ap->end = i;
+ ap->save = ap->finish_pos = i + 1;
+
+ /* If we have a token, and it starts with a comment character, skip
+ to the newline and restart the token search. */
+ if (ap->save <= len)
+ {
+ if (strchr (comment, command[ap->start]) != NULL)
+ {
+ ap->finish_pos = ap->start;
+ i = ap->save;
+ while (i < len && command[i] != '\n')
+ i++;
+
+ ap->save = i;
+ continue;
+ }
+ }
+ break;
+ }
+ return ap->save;
+}
+
+static char quote_transtab[] = "\\\\a\ab\bf\fn\nr\rt\tv\v";
+
+int
+argcv_unquote_char (int c)
+{
+ char *p;
+
+ for (p = quote_transtab; *p; p += 2)
+ {
+ if (*p == c)
+ return p[1];
+ }
+ return c;
+}
+
+int
+argcv_quote_char (int c)
+{
+ char *p;
+
+ for (p = quote_transtab + sizeof(quote_transtab) - 2;
+ p > quote_transtab; p -= 2)
+ {
+ if (*p == c)
+ return p[-1];
+ }
+ return -1;
+}
+
+#define to_num(c) \
+ (isdigit(c) ? c - '0' : (isxdigit(c) ? toupper(c) - 'A' + 10 : 255 ))
+
+static int
+xtonum (int *pval, const char *src, int base, int cnt)
+{
+ int i, val;
+
+ for (i = 0, val = 0; i < cnt; i++, src++)
+ {
+ int n = *(unsigned char*)src;
+ if (n > 127 || (n = to_num(n)) >= base)
+ break;
+ val = val*base + n;
+ }
+ *pval = val;
+ return i;
+}
+
+size_t
+argcv_quoted_length (const char *str, int *quote)
+{
+ size_t len = 0;
+
+ *quote = 0;
+ for (; *str; str++)
+ {
+ if (*str == ' ')
+ {
+ len++;
+ *quote = 1;
+ }
+ else if (*str == '"')
+ {
+ len += 2;
+ *quote = 1;
+ }
+ else if (*str != '\t' && *str != '\\' && isprint (*str))
+ len++;
+ else
+ {
+ switch (argcv_quoting_style)
+ {
+ case argcv_quoting_octal:
+ if (argcv_quote_char (*str) != -1)
+ len += 2;
+ else
+ len += 4;
+ break;
+
+ case argcv_quoting_hex:
+ len += 3;
+ break;
+ }
+ }
+ }
+ return len;
+}
+
+void
+argcv_unquote_copy (char *dst, const char *src, size_t n)
+{
+ int i = 0;
+ int c;
+ int expect_delim = 0;
+
+ while (i < n)
+ {
+ switch (src[i])
+ {
+ case '\'':
+ case '"':
+ if (!expect_delim)
+ {
+ const char *p;
+
+ for (p = src+i+1; *p && *p != src[i]; p++)
+ if (*p == '\\')
+ p++;
+ if (*p)
+ expect_delim = src[i++];
+ else
+ *dst++ = src[i++];
+ }
+ else if (expect_delim == src[i])
+ ++i;
+ else
+ *dst++ = src[i++];
+ break;
+
+ case '\\':
+ ++i;
+ if (src[i] == 'x' || src[i] == 'X')
+ {
+ if (n - i < 2)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ int off = xtonum(&c, src + i + 1, 16, 2);
+ if (off == 0)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ *dst++ = c;
+ i += off + 1;
+ }
+ }
+ }
+ else if ((unsigned char)src[i] < 128 && isdigit (src[i]))
+ {
+ if (n - i < 1)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ int off = xtonum (&c, src+i, 8, 3);
+ if (off == 0)
+ {
+ *dst++ = '\\';
+ *dst++ = src[i++];
+ }
+ else
+ {
+ *dst++ = c;
+ i += off;
+ }
+ }
+ }
+ else
+ *dst++ = argcv_unquote_char (src[i++]);
+ break;
+
+ default:
+ *dst++ = src[i++];
+ }
+ }
+ *dst = 0;
+}
+
+void
+argcv_quote_copy (char *dst, const char *src)
+{
+ for (; *src; src++)
+ {
+ if (*src == '"')
+ {
+ *dst++ = '\\';
+ *dst++ = *src;
+ }
+ else if (*src != '\t' && *src != '\\' && isprint(*src))
+ *dst++ = *src;
+ else
+ {
+ char tmp[4];
+
+ switch (argcv_quoting_style)
+ {
+ case argcv_quoting_octal:
+ {
+ int c = argcv_quote_char (*src);
+ *dst++ = '\\';
+ if (c != -1)
+ *dst++ = c;
+ else
+ {
+ snprintf (tmp, sizeof tmp, "%03o", *(unsigned char*)src);
+ memcpy (dst, tmp, 3);
+ dst += 3;
+ }
+ break;
+ }
+
+ case argcv_quoting_hex:
+ snprintf (tmp, sizeof tmp, "%%%02X", *(unsigned char*)src);
+ memcpy (dst, tmp, 3);
+ dst += 3;
+ break;
+ }
+ }
+ }
+}
+
+int
+argcv_get_np (const char *command, int len,
+ const char *delim, const char *cmnt,
+ int flags,
+ int *pargc, char ***pargv, char **endp)
+{
+ int i = 0;
+ struct argcv_info info;
+ int argc;
+ char **argv;
+
+ if (!delim)
+ delim = " ";
+ if (!cmnt)
+ cmnt = "";
+
+ init_argcv_info (&info, flags, len, command, delim, cmnt);
+
+ /* Count number of arguments */
+ argc = 0;
+ while (argcv_scan (&info) <= len)
+ argc++;
+
+ argv = calloc ((argc + 1), sizeof (char *));
+ if (argv == NULL)
+ return ENOMEM;
+
+ i = 0;
+ info.save = 0;
+ for (i = 0; i < argc; i++)
+ {
+ int n;
+ int unquote;
+
+ argcv_scan (&info);
+
+ if (info.flags & ARGCV_QUOTE && !(info.flags & _ARGCV_WORD_SED_EXPR))
+ {
+ if ((command[info.start] == '"' || command[info.end] == '\'')
+ && command[info.end] == command[info.start])
+ {
+ if (info.start < info.end)
+ {
+ info.start++;
+ info.end--;
+ }
+ unquote = 0;
+ }
+ else
+ unquote = 1;
+ }
+ else
+ unquote = 0;
+
+ n = info.end - info.start + 1;
+ argv[i] = calloc (n + 1, sizeof (char));
+ if (argv[i] == NULL)
+ {
+ argcv_free (i, argv);
+ return ENOMEM;
+ }
+ if (unquote)
+ argcv_unquote_copy (argv[i], &command[info.start], n);
+ else
+ memcpy (argv[i], &command[info.start], n);
+ argv[i][n] = 0;
+ }
+ argv[i] = NULL;
+
+ *pargc = argc;
+ *pargv = argv;
+ if (endp)
+ *endp = (char*) (command + info.finish_pos);
+ return 0;
+}
+
+int
+argcv_get_n (const char *command, int len, const char *delim, const char *cmnt,
+ int *pargc, char ***pargv)
+{
+ return argcv_get_np (command, len, delim, cmnt, ARGCV_DEFFLAGS,
+ pargc, pargv, NULL);
+}
+
+int
+argcv_get (const char *command, const char *delim, const char *cmnt,
+ int *argc, char ***argv)
+{
+ return argcv_get_np (command, strlen (command), delim, cmnt,
+ ARGCV_DEFFLAGS,
+ argc, argv, NULL);
+}
+
+
+/*
+ * frees all elements of an argv array
+ * argc is the number of elements
+ * argv is the array
+ */
+void
+argcv_free (int argc, char **argv)
+{
+ if (argc)
+ {
+ while (--argc >= 0)
+ if (argv[argc])
+ free (argv[argc]);
+ free (argv);
+ }
+}
+
+void
+argv_free (char **argv)
+{
+ int i;
+
+ for (i = 0; argv[i]; i++)
+ free (argv[i]);
+ free (argv);
+}
+
+/* Make a argv an make string separated by ' '. */
+
+int
+argcv_string (int argc, char **argv, char **pstring)
+{
+ size_t i, j, len;
+ char *buffer;
+
+ /* No need. */
+ if (pstring == NULL)
+ return EINVAL;
+
+ buffer = malloc (1);
+ if (buffer == NULL)
+ return ENOMEM;
+ *buffer = '\0';
+
+ for (len = i = j = 0; i < argc; i++)
+ {
+ int quote;
+ int toklen;
+
+ toklen = argcv_quoted_length (argv[i], &quote);
+
+ len += toklen + 2;
+ if (quote)
+ len += 2;
+
+ buffer = realloc (buffer, len);
+ if (buffer == NULL)
+ return ENOMEM;
+
+ if (i != 0)
+ buffer[j++] = ' ';
+ if (quote)
+ buffer[j++] = '"';
+ argcv_quote_copy (buffer + j, argv[i]);
+ j += toklen;
+ if (quote)
+ buffer[j++] = '"';
+ }
+
+ for (; j > 0 && isspace (buffer[j-1]); j--)
+ ;
+ buffer[j] = 0;
+ if (pstring)
+ *pstring = buffer;
+ return 0;
+}
+
+void
+argcv_remove (int *pargc, char ***pargv,
+ int (*sel) (const char *, void *), void *data)
+{
+ int i, j;
+ int argc = *pargc;
+ char **argv = *pargv;
+ int cnt = 0;
+
+ for (i = j = 0; i < argc; i++)
+ {
+ if (sel (argv[i], data))
+ {
+ free (argv[i]);
+ cnt++;
+ }
+ else
+ {
+ if (i != j)
+ argv[j] = argv[i];
+ j++;
+ }
+ }
+ if (i != j)
+ argv[j] = NULL;
+ argc -= cnt;
+
+ *pargc = argc;
+ *pargv = argv;
+}
+
+
diff --git a/argcv.h b/argcv.h
new file mode 100644
index 0000000..88bcb90
--- /dev/null
+++ b/argcv.h
@@ -0,0 +1,79 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2005, 2007,
+ 2008, 2009 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifndef _ARGCV_H
+#define _ARGCV_H 1
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Treat whitespace as delimiters */
+#define ARGCV_WS 0x01
+ /* Handle quotes and escape directives */
+#define ARGCV_QUOTE 0x02
+ /* replace each input sequence of repeated delimiters into a single
+ delimiter */
+#define ARGCV_SQUEEZE_DELIMS 0x04
+ /* Return delimiters */
+#define ARGCV_RETURN_DELIMS 0x08
+ /* Treat sed expressions as words */
+#define ARGCV_SED_EXPR 0x10
+
+#define ARGCV_DEFFLAGS \
+ (ARGCV_WS | ARGCV_QUOTE | ARGCV_SQUEEZE_DELIMS | ARGCV_RETURN_DELIMS)
+
+enum argcv_quoting_style {
+ argcv_quoting_octal,
+ argcv_quoting_hex
+};
+
+extern enum argcv_quoting_style argcv_quoting_style;
+
+extern int argcv_get (const char *command, const char *delim,
+ const char *cmnt,
+ int *argc, char ***argv);
+extern int argcv_get_n (const char *command, int len,
+ const char *delim, const char *cmnt,
+ int *argc, char ***argv);
+extern int argcv_get_np (const char *command, int len,
+ const char *delim, const char *cmnt,
+ int flags,
+ int *pargc, char ***pargv, char **endp);
+
+extern int argcv_string (int argc, char **argv, char **string);
+extern void argcv_free (int argc, char **argv);
+extern void argv_free (char **argv);
+
+extern int argcv_unquote_char (int c);
+extern int argcv_quote_char (int c);
+extern size_t argcv_quoted_length (const char *str, int *quote);
+extern void argcv_unquote_copy (char *dst, const char *src, size_t n);
+extern void argcv_quote_copy (char *dst, const char *src);
+extern void argcv_remove (int *pargc, char ***pargv,
+ int (*sel) (const char *, void *), void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ARGCV_H */
diff --git a/gconf-format.c b/gconf-format.c
new file mode 100644
index 0000000..ea8a808
--- /dev/null
+++ b/gconf-format.c
@@ -0,0 +1,200 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <gconf.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 *
+gconf_data_type_string (enum gconf_data_type type)
+{
+ switch (type)
+ {
+ case gconf_type_void:
+ return "void";
+
+ case gconf_type_string:
+ return "string";
+
+ case gconf_type_short:
+ case gconf_type_ushort:
+ case gconf_type_int:
+ case gconf_type_uint:
+ case gconf_type_long:
+ case gconf_type_ulong:
+ case gconf_type_size:
+/* case gconf_type_off:*/
+ case gconf_type_uintmax:
+ case gconf_type_intmax:
+ return "number";
+
+ case gconf_type_time:
+ return "time";
+
+ case gconf_type_bool:
+ return "boolean";
+
+ case gconf_type_ipv4:
+ return "IPv4";
+
+ case gconf_type_cidr:
+ return "CIDR";
+
+ case gconf_type_host:
+ return "hostname";
+
+ case gconf_type_sockaddr:
+ return "sock-addr";
+
+ case gconf_type_section:
+ return "section";
+ }
+ return "UNKNOWN?";
+}
+
+static void
+format_level (FILE *stream, unsigned level)
+{
+ while (level--)
+ fprintf (stream, " ");
+}
+
+void
+gconf_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
+gconf_format_simple_statement (FILE *stream, struct gconf_keyword *kwp,
+ unsigned level)
+{
+ const char *argstr;
+
+ if (kwp->docstring)
+ gconf_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 (gconf_data_type_string (GCONF_TYPE (kwp->type))));
+ else
+ fprintf (stream, "%s", gettext (gconf_data_type_string (kwp->type)));
+ fprintf (stream, ">;\n");
+ }
+}
+
+void
+gconf_format_block_statement (FILE *stream, struct gconf_keyword *kwp,
+ unsigned level)
+{
+ if (kwp->docstring)
+ gconf_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");
+ gconf_format_statement_array (stream, kwp->kwd, 0, level + 1);
+ format_level (stream, level);
+ fprintf (stream, "}\n");
+}
+
+void
+gconf_format_statement_array (FILE *stream, struct gconf_keyword *kwp,
+ unsigned n,
+ unsigned level)
+{
+ for (; kwp->ident; kwp++, n++)
+ {
+ if (n)
+ fputc ('\n', stream);
+ if (kwp->type == gconf_type_section)
+ gconf_format_block_statement (stream, kwp, level);
+ else
+ gconf_format_simple_statement (stream, kwp, level);
+ }
+}
diff --git a/gconf-gram.y b/gconf-gram.y
new file mode 100644
index 0000000..ed685d0
--- /dev/null
+++ b/gconf-gram.y
@@ -0,0 +1,819 @@
+%{
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <gconf.h>
+#include <gconf-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 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 <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 (!$$)
+ 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);<