aboutsummaryrefslogtreecommitdiff
path: root/gconf
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
commit5000c56d29cf616c8df80f8ec9a19682769f512e (patch)
treed2cc46fda9e8905bdd2fb81bffdaf3a58898be53 /gconf
parenta72cd5987c404080f18ba40bdc4c06811493ab1c (diff)
downloadwydawca-5000c56d29cf616c8df80f8ec9a19682769f512e.tar.gz
wydawca-5000c56d29cf616c8df80f8ec9a19682769f512e.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
Diffstat (limited to 'gconf')
-rw-r--r--gconf/.gitignore4
-rw-r--r--gconf/Makefile.am17
-rw-r--r--gconf/argcv.c632
-rw-r--r--gconf/argcv.h79
-rw-r--r--gconf/gconf-format.c200
-rw-r--r--gconf/gconf-gram.y819
-rw-r--r--gconf/gconf-lex.l479
-rw-r--r--gconf/gconf-preproc.c727
-rw-r--r--gconf/gconf-text.c73
-rw-r--r--gconf/gconf.h153
-rw-r--r--gconf/gnulib.modules13
11 files changed, 3196 insertions, 0 deletions
diff --git a/gconf/.gitignore b/gconf/.gitignore
new file mode 100644
index 0000000..42c88b2
--- /dev/null
+++ b/gconf/.gitignore
@@ -0,0 +1,4 @@
+gconf-gram.c
+gconf-gram.h
+gconf-gram.output
+gconf-lex.c
diff --git a/gconf/Makefile.am b/gconf/Makefile.am
new file mode 100644
index 0000000..4f50bd6
--- /dev/null
+++ b/gconf/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/gconf/argcv.c b/gconf/argcv.c
new file mode 100644
index 0000000..d5f2bda
--- /dev/null
+++ b/gconf/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/gconf/argcv.h b/gconf/argcv.h
new file mode 100644
index 0000000..88bcb90
--- /dev/null
+++ b/gconf/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/gconf-format.c b/gconf/gconf-format.c
new file mode 100644
index 0000000..ea8a808
--- /dev/null
+++ b/gconf/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/gconf-gram.y b/gconf/gconf-gram.y
new file mode 100644
index 0000000..ed685d0
--- /dev/null
+++ b/gconf/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);
+ 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)
+{
+ if (cursect && cursect->callback)
+ cursect->callback (gconf_callback_section_end,
+ &gconf_current_locus, /* FIXME */
+ kwp ? target_ptr(kwp) : NULL,
+ NULL,
+ &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);
+}
+
+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 (sockaddr_union_t *s, const char *string)
+{
+ if (string[0] == '/')
+ {
+ if (strlen (string) >= sizeof (s->s_un.sun_path))
+ {
+ gconf_error (&gconf_current_locus, 0,
+ _("%s: UNIX socket name too long"),
+ string);
+ return 1;
+ }
+ s->s_un.sun_family = AF_UNIX;
+ strcpy (s->s_un.sun_path, string);
+ }
+ 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;
+ }
+ s->s_in = sa;
+ }
+ 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 ((sockaddr_union_t*)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 (sockaddr_union_t) /* 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);
+}
+
diff --git a/gconf/gconf-lex.l b/gconf/gconf-lex.l
new file mode 100644
index 0000000..1cdaada
--- /dev/null
+++ b/gconf/gconf-lex.l
@@ -0,0 +1,479 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <gconf.h>
+#include <gconf-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 <argcv.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_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);
+<COMMENT>[^*\n]* /* eat anything that's not a '*' */
+<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<COMMENT>\n ++gconf_current_locus.line;
+<COMMENT>"*"+"/" 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 */
+<INITIAL>{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); }
+<STR>[^\\"\n]*\\. |
+<STR>\"[^\\"\n]*\\\n { line_add_unescape_last (yytext, yyleng); }
+<STR>[^\\"\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 */
+<ML>^"#line ".*\n { gconf_current_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 = 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;
+ int argc;
+ char **argv;
+
+ while (*text && isspace (*text))
+ text++;
+ text++;
+
+ if (argcv_get (text, "", NULL, &argc, &argv))
+ gconf_error (ploc, 0, _("cannot parse #line line"));
+ else
+ {
+ if (argc == 2)
+ rc = assign_locus (ploc, NULL, argv[1], pxlines);
+ else if (argc == 3)
+ rc = assign_locus (ploc, argv[2], argv[1], pxlines);
+ else if (argc == 4)
+ {
+ rc = assign_locus (ploc, argv[2], argv[1], 0);
+ if (rc == 0)
+ {
+ char *p;
+ unsigned long x = strtoul (argv[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"));
+ }
+ argcv_free (argc, argv);
+}
+
+static void
+parse_line_cpp (char *text, gconf_locus_t *ploc, size_t *pxlines)
+{
+ int argc;
+ char **argv;
+
+ if (argcv_get (text, "", NULL, &argc, &argv))
+ gconf_error (ploc, 0, _("cannot parse #line line"));
+ else if (argc < 3)
+ gconf_error (ploc, 0, _("invalid #line statement"));
+ else
+ {
+ if (assign_locus (ploc, argv[2], argv[1], pxlines))
+ gconf_error (ploc, 0, _("malformed #line statement"));
+ }
+ argcv_free (argc, argv);
+}
+
diff --git a/gconf/gconf-preproc.c b/gconf/gconf-preproc.c
new file mode 100644
index 0000000..af26d1a
--- /dev/null
+++ b/gconf/gconf-preproc.c
@@ -0,0 +1,727 @@
+/* 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include <xalloc.h>
+#include <hash.h>
+#include <gl_linked_list.h>
+#include <inttostr.h>
+#include <argcv.h>
+
+#if ENABLE_NLS
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) msgid
+#endif
+
+bool gconf_log_to_stderr = true;
+void (*gconf_log_setup_hook) () = NULL;
+
+struct input_file_ident
+{
+ ino_t i_node;
+ dev_t device;
+};
+
+struct buffer_ctx
+{
+ struct buffer_ctx *prev; /* Pointer to previous context */
+ gconf_locus_t locus; /* Current input location */
+ size_t namelen; /* Length of the file name */
+ size_t xlines; /* Number of #line directives output so far */
+ struct input_file_ident id;
+ FILE *infile;
+};
+
+extern int yy_flex_debug;
+static struct buffer_ctx *context_stack;
+
+#define INFILE context_stack->infile
+#define LOCUS context_stack->locus
+
+static char *linebuf;
+static size_t bufsize;
+static char *putback_buffer;
+static size_t putback_size;
+static size_t putback_max;
+
+static int push_source (const char *name, int once);
+static int pop_source (void);
+static int parse_include (const char *text, int once);
+
+static void
+putback (const char *str)
+{
+ size_t len;
+
+ if (!*str)
+ return;
+ len = strlen (str) + 1;
+ if (len > putback_max)
+ {
+ putback_max = len;
+ putback_buffer = xrealloc (putback_buffer, putback_max);
+ }
+ strcpy (putback_buffer, str);
+ putback_size = len - 1;
+}
+
+/* Compute the size of the line
+
+ #line NNN "FILENAME"
+*/
+static size_t
+pp_line_stmt_size ()
+{
+ char lbuf[INT_BUFSIZE_BOUND(uintmax_t)];
+ char xbuf[INT_BUFSIZE_BOUND(uintmax_t)];
+ char *lp, *xp;
+
+ lp = umaxtostr (LOCUS.line, lbuf);
+ xp = umaxtostr (context_stack->xlines + 1, xbuf);
+ if (context_stack->namelen == 0)
+ context_stack->namelen = strlen (LOCUS.file);
+ /* "#line " is 6 chars, two more spaces, two quotes and a linefeed
+ make another 5, summa facit 11 */
+ return 11 + strlen (lp) + strlen (xp) + context_stack->namelen;
+}
+
+static void
+pp_line_stmt ()
+{
+ char *p;
+ size_t ls_size = pp_line_stmt_size ();
+ size_t pb_size = putback_size + ls_size + 1;
+
+ if (pb_size > putback_max)
+ {
+ putback_max = pb_size;
+ putback_buffer = xrealloc (putback_buffer, putback_max);
+ }
+
+ p = putback_buffer + putback_size;
+ context_stack->xlines++;
+ snprintf (p, putback_max - putback_size,
+ "#line %lu \"%s\" %lu\n",
+ (unsigned long) LOCUS.line,
+ LOCUS.file, (unsigned long) context_stack->xlines);
+ putback_size += ls_size;
+}
+
+#define STRMATCH(p, len, s) (len >= sizeof(s) \
+ && memcmp (p, s, sizeof(s) - 1) == 0 \
+ && isspace(p[sizeof(s) - 1]))
+
+static int
+next_line ()
+{
+ ssize_t rc;
+
+ do
+ {
+ if (putback_size)
+ {
+ if (putback_size + 1 > bufsize)
+ {
+ bufsize = putback_size + 1;
+ linebuf = xrealloc (linebuf, bufsize);
+ }
+ strcpy (linebuf, putback_buffer);
+ rc = putback_size;
+ putback_size = 0;
+ }
+ else if (!context_stack)
+ return 0;
+ else
+ rc = getline (&linebuf, &bufsize, INFILE);
+ }
+ while (rc == -1 && pop_source () == 0);
+ return rc;
+}
+
+size_t
+gconf_preproc_fill_buffer (char *buf, size_t size)
+{
+ size_t bufsize = size;
+
+ while (next_line () > 0)
+ {
+ char *p;
+ size_t len;
+ int is_line = 0;
+
+ for (p = linebuf; *p && isspace (*p); p++)
+ ;
+ if (*p == '#')
+ {
+ size_t l;
+ for (p++; *p && isspace (*p); p++)
+ ;
+ l = strlen (p);
+ if (STRMATCH (p, l, "include_once"))
+ {
+ if (parse_include (linebuf, 1))
+ putback ("/*include_once*/\n");
+ continue;
+ }
+ else if (STRMATCH (p, l, "include"))
+ {
+ if (parse_include (linebuf, 0))
+ putback ("/*include*/\n");
+ continue;
+ }
+ else if (STRMATCH (p, l, "line"))
+ is_line = 1;
+ }
+
+ len = strlen (linebuf);
+
+ if (len > size)
+ len = size;
+
+ memcpy (buf, linebuf, len);
+ buf += len;
+ size -= len;
+
+ if (size == 0)
+ {
+ putback (linebuf + len);
+ break;
+ }
+
+ if (!is_line && len > 0 && linebuf[len - 1] == '\n')
+ LOCUS.line++;
+ }
+ return bufsize - size;
+}
+
+#define STAT_ID_EQ(st,id) ((id).i_node == (st).st_ino \
+ && (id).device == (st).st_dev)
+
+static struct buffer_ctx *
+ctx_lookup (struct stat *st)
+{
+ struct buffer_ctx *ctx;
+
+ if (!context_stack)
+ return NULL;
+
+ for (ctx = context_stack->prev; ctx; ctx = ctx->prev)
+ if (STAT_ID_EQ (*st, ctx->id))
+ break;
+ return ctx;
+}
+
+const char *gconf_preprocessor = NULL;
+static gl_list_t include_path;
+static gl_list_t std_include_path;
+
+struct file_data
+{
+ const char *name;
+ size_t namelen;
+ char *buf;
+ size_t buflen;
+ int found;
+};
+
+static int
+pp_list_find (gl_list_t list, struct file_data *dptr)
+{
+ const void *p;
+ gl_list_iterator_t itr = gl_list_iterator (list);
+
+ while (gl_list_iterator_next (&itr, &p, NULL))
+ {
+ const char *dir = p;
+ size_t size = strlen (dir) + 1 + dptr->namelen + 1;
+ if (size > dptr->buflen)
+ {
+ dptr->buflen = size;
+ dptr->buf = xrealloc (dptr->buf, dptr->buflen);
+ }
+ strcpy (dptr->buf, dir);
+ strcat (dptr->buf, "/");
+ strcat (dptr->buf, dptr->name);
+ if (dptr->found = (access (dptr->buf, F_OK) == 0))
+ break;
+ }
+ gl_list_iterator_free (&itr);
+ return dptr->found;
+}
+
+gl_list_t
+pp_list_create ()
+{
+ return gl_list_create_empty(&gl_linked_list_implementation,
+ NULL,
+ NULL,
+ NULL,
+ false);
+}
+
+void
+gconf_include_path_setup_v (char **dirs)
+{
+ if (!include_path)
+ include_path = pp_list_create ();
+ std_include_path = pp_list_create ();
+ if (dirs)
+ {
+ int i;
+ for (i = 0; dirs[i]; i++)
+ /* FIXME: Element never freed */
+ gl_list_add_last (std_include_path, xstrdup (dirs[i]));
+ }
+}
+
+void
+gconf_include_path_setup (const char *dir, ...)
+{
+ const char *p;
+ char **argv = NULL;
+ size_t argc = 0;
+ size_t argi = 0;
+ va_list ap;
+
+ va_start (ap, dir);
+ p = dir;
+ while (1)
+ {
+ if (argi == argc)
+ {
+ if (argc == 0)
+ argc = 16;
+ argv = x2nrealloc (argv, &argc, sizeof (argv[0]));
+ }
+ argv[argi++] = (char*) p;
+ if (!p)
+ break;
+ p = va_arg (ap, const char*);
+ }
+ gconf_include_path_setup_v (argv);
+ free (argv);
+ va_end (ap);
+}
+
+void
+gconf_preproc_add_include_dir (char *dir)
+{
+ if (!include_path)
+ include_path = pp_list_create ();
+ gl_list_add_last (include_path, dir);
+}
+
+static Hash_table *incl_sources;
+
+/* Calculate the hash of a struct input_file_ident. */
+static size_t
+incl_hasher (void const *data, unsigned n_buckets)
+{
+ const struct input_file_ident *id = data;
+ return (id->i_node + id->device) % n_buckets;
+}
+
+/* Compare two input_file_idents for equality. */
+static bool
+incl_compare (void const *data1, void const *data2)
+{
+ const struct input_file_ident *id1 = data1;
+ const struct input_file_ident *id2 = data2;
+ return id1->device == id2->device && id1->i_node == id2->i_node;
+}
+
+static void
+incl_free (void *data)
+{
+ free (data);
+}
+
+static int
+source_lookup (struct stat *st)
+{
+ struct input_file_ident *sample = xmalloc (sizeof (*sample)), *id;
+
+ sample->i_node = st->st_ino;
+ sample->device = st->st_dev;
+
+ if (!((incl_sources
+ || (incl_sources = hash_initialize (0, 0,
+ incl_hasher,
+ incl_compare,
+ incl_free)))
+ && (id = hash_insert (incl_sources, sample))))
+ xalloc_die ();
+
+ if (id != sample)
+ {
+ free (sample);
+ return 1; /* Found */
+ }
+ return 0;
+}
+
+
+static int
+push_source (const char *name, int once)
+{
+ FILE *fp;
+ struct buffer_ctx *ctx;
+ struct stat st;
+ int rc = stat (name, &st);
+
+ if (context_stack)
+ {
+ if (rc)
+ {
+ gconf_error (&LOCUS, errno, _("Cannot stat `%s'"), name);
+ return 1;
+ }
+
+ if (LOCUS.file && STAT_ID_EQ (st, context_stack->id))
+ {
+ gconf_error (&LOCUS, 0, _("Recursive inclusion"));
+ return 1;
+ }
+
+ if ((ctx = ctx_lookup (&st)))
+ {
+ gconf_error (&LOCUS, 0, _("Recursive inclusion"));
+ if (ctx->prev)
+ gconf_error (&ctx->prev->locus, 0,
+ _("`%s' already included here"), name);
+ else
+ gconf_error (&LOCUS, 0,
+ _("`%s' already included at top level"), name);
+ return 1;
+ }
+ }
+ else if (rc)
+ {
+ gconf_error (NULL, errno, _("Cannot stat `%s'"), name);
+ return 1;
+ }
+
+ if (once && source_lookup (&st))
+ return -1;
+
+ fp = fopen (name, "r");
+ if (!fp)
+ {
+ gconf_error (&LOCUS, errno, _("Cannot open `%s'"), name);
+ return 1;
+ }
+
+ /* Push current context */
+ ctx = xmalloc (sizeof (*ctx));
+ ctx->locus.file = gconf_install_text (name);
+ ctx->locus.line = 1;
+ ctx->xlines = 0;
+ ctx->namelen = strlen (ctx->locus.file);
+ ctx->id.i_node = st.st_ino;
+ ctx->id.device = st.st_dev;
+ ctx->infile = fp;
+ ctx->prev = context_stack;
+ context_stack = ctx;
+
+ if (yy_flex_debug)
+ fprintf (stderr, "Processing file `%s'\n", name);
+
+ pp_line_stmt ();
+
+ return 0;
+}
+
+static int
+pop_source ()
+{
+ struct buffer_ctx *ctx;
+
+ if (!context_stack)
+ return 1;
+
+ fclose (INFILE);
+
+ /* Restore previous context */
+ ctx = context_stack->prev;
+ free (context_stack);
+ context_stack = ctx;
+
+ if (!context_stack)
+ {
+ if (yy_flex_debug)
+ fprintf (stderr, "End of input\n");
+ return 1;
+ }
+
+ LOCUS.line++;
+
+ if (yy_flex_debug)
+ fprintf (stderr, "Resuming file `%s' at line %lu\n",
+ LOCUS.file, (unsigned long) LOCUS.line);
+
+ pp_line_stmt ();
+
+ return 0;
+}
+
+static int
+try_file (const char *name, int allow_cwd, int err_not_found, char **newp)
+{
+ static char *cwd = ".";
+ struct file_data fd;
+
+ fd.name = name;
+ fd.namelen = strlen (name);
+ fd.buf = NULL;
+ fd.buflen = 0;
+ fd.found = 0;
+
+ if (!include_path)
+ gconf_include_path_setup (NULL);
+ if (allow_cwd)
+ {
+ gl_list_node_t node = gl_list_add_last (include_path, cwd);
+ pp_list_find (include_path, &fd);
+ gl_list_remove_node (include_path, node);
+ }
+ else
+ pp_list_find (include_path, &fd);
+
+ if (!fd.found)
+ {
+ pp_list_find (std_include_path, &fd);
+
+ if (!fd.found && err_not_found)
+ {
+ gconf_error (&LOCUS, 0, _("%s: No such file or directory"), name);
+ *newp = NULL;
+ }
+ }
+ if (fd.found)
+ *newp = fd.buf;
+ return fd.found;
+}
+
+static int
+parse_include (const char *text, int once)
+{
+ int argc;
+ char **argv;
+ char *tmp = NULL;
+ char *p = NULL;
+ int rc = 1;
+
+ if (argcv_get (text, "", NULL, &argc, &argv))
+ gconf_error (&LOCUS, 0, _("Cannot parse include line"));
+ else if (argc != 2)
+ gconf_error (&LOCUS, 0, _("invalid include statement"));
+ else
+ {
+ size_t len;
+ int allow_cwd;
+
+ p = argv[1];
+ len = strlen (p);
+
+ if (p[0] == '<' && p[len - 1] == '>')
+ {
+ allow_cwd = 0;
+ p[len - 1] = 0;
+ p++;
+ }
+ else
+ allow_cwd = 1;
+
+ if (p[0] != '/' && try_file (p, allow_cwd, 1, &tmp))
+ p = tmp;
+ }
+
+ if (p)
+ rc = push_source (p, once);
+ free (tmp);
+ argcv_free (argc, argv);
+ return rc;
+}
+
+int
+gconf_preproc_init (const char *name)
+{
+ return push_source (name, 0);
+}
+
+void
+gconf_preproc_done ()
+{
+ if (incl_sources)
+ hash_free (incl_sources);
+ free (linebuf);
+ free (putback_buffer);
+}
+
+int
+gconf_preproc_run (const char *config_file, const char *extpp)
+{
+ size_t i;
+ char buffer[512];
+
+ if (gconf_preproc_init (config_file))
+ return 1;
+ if (extpp)
+ {
+ FILE *outfile;
+ char *setup_file;
+ char *cmd;
+
+ if (try_file ("pp-setup", 1, 0, &setup_file))
+ {
+ asprintf (&cmd, "%s %s -", extpp, setup_file);
+ free (setup_file);
+ }
+ else
+ cmd = xstrdup (extpp);
+ //FIXME_DEBUG_F1 (2, "Running preprocessor: `%s'", cmd);
+ outfile = popen (cmd, "w");
+ if (!outfile)
+ {
+ gconf_error (NULL, errno,
+ _("Unable to start external preprocessor `%s'"), cmd);
+ free (cmd);
+ return 1;
+ }
+
+ while ((i = gconf_preproc_fill_buffer (buffer, sizeof buffer)))
+ fwrite (buffer, 1, i, outfile);
+ pclose (outfile);
+ free (cmd);
+ }
+ else
+ {
+ while ((i = gconf_preproc_fill_buffer (buffer, sizeof buffer)))
+ fwrite (buffer, 1, i, stdout);
+ }
+ gconf_preproc_done ();
+ return 0;
+}
+
+FILE *
+gconf_preproc_extrn_start (const char *file_name, pid_t *ppid)
+{
+ int pout[2];
+ pid_t pid;
+ int i;
+ FILE *fp = NULL;
+
+ //FIXME_DEBUG_F1 (2, "Running preprocessor: `%s'", ppcmd);
+
+ pipe (pout);
+ switch (pid = fork ())
+ {
+ /* The child branch. */
+ case 0:
+ if (pout[1] != 1)
+ {
+ close (1);
+ dup2 (pout[1], 1);
+ }
+
+ /* Close unneeded descripitors */
+ for (i = getdtablesize (); i > 2; i--)
+ close (i);
+
+ if (!gconf_log_to_stderr)
+ {
+ int p[2];
+ char *buf = NULL;
+ size_t size = 0;
+ FILE *fp;
+
+ signal (SIGCHLD, SIG_DFL);
+ pipe (p);
+ switch (pid = fork ())
+ {
+ /* Grandchild */
+ case 0:
+ if (p[1] != 2)
+ {
+ close (2);
+ dup2 (p[1], 2);
+ }
+ close (p[0]);
+
+ if (gconf_preproc_run (file_name, gconf_preprocessor))
+ exit (127);
+ exit (0);
+
+ case -1:
+ /* Fork failed */
+ if (gconf_log_setup_hook)
+ gconf_log_setup_hook ();
+ gconf_error (NULL, errno, _("Cannot run `%s'"),
+ gconf_preprocessor);
+ exit (127);
+
+ default:
+ /* Sub-master */
+ close (p[1]);
+ fp = fdopen (p[0], "r");
+ if (gconf_log_setup_hook)
+ gconf_log_setup_hook ();
+ while (getline (&buf, &size, fp) > 0)
+ gconf_error (NULL, 0, "%s", buf);
+ }
+ }
+ else
+ {
+ gconf_preproc_run (file_name, gconf_preprocessor);
+ }
+ exit (0);
+
+ case -1:
+ /* Fork failed */
+ gconf_error (NULL, errno, _("Cannot run `%s'"), gconf_preprocessor);
+ break;
+
+ default:
+ close (pout[1]);
+ fp = fdopen (pout[0], "r");
+ break;
+ }
+ *ppid = pid;
+ return fp;
+}
+
+void
+gconf_preproc_extrn_shutdown (pid_t pid)
+{
+ int status;
+ waitpid (pid, &status, 0);
+}
+
diff --git a/gconf/gconf-text.c b/gconf/gconf-text.c
new file mode 100644
index 0000000..0545293
--- /dev/null
+++ b/gconf/gconf-text.c
@@ -0,0 +1,73 @@
+/* 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 <string.h>
+#include <hash.h>
+#include <xalloc.h>
+
+static Hash_table *text_table;
+
+/* Calculate the hash of a string. */
+static size_t
+text_hasher (void const *data, unsigned n_buckets)
+{
+ return hash_string (data, n_buckets);
+}
+
+/* Compare two strings for equality. */
+static bool
+text_compare (void const *data1, void const *data2)
+{
+ return strcmp (data1, data2) == 0;
+}
+
+static void
+text_free (void *data)
+{
+ free (data);
+}
+
+/* Lookup a text. If it does not exist, create it. */
+char *
+gconf_install_text (const char *str)
+{
+ char *text, *s;
+
+ s = xstrdup (str);
+
+ if (!((text_table
+ || (text_table = hash_initialize (0, 0,
+ text_hasher,
+ text_compare,
+ text_free)))
+ && (text = hash_insert (text_table, s))))
+ xalloc_die ();
+
+ if (s != text)
+ free (s);
+ return text;
+}
+
+void
+gconf_destroy_text ()
+{
+ if (text_table)
+ hash_free (text_table);
+}
diff --git a/gconf/gconf.h b/gconf/gconf.h
new file mode 100644
index 0000000..8c04b6d
--- /dev/null
+++ b/gconf/gconf.h
@@ -0,0 +1,153 @@
+/* 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/>. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <gl_linked_list.h>
+
+typedef struct {
+ char *file;
+ int line;
+} gconf_locus_t;
+
+extern gconf_locus_t gconf_locus;
+
+enum gconf_data_type {
+ gconf_type_void,
+ gconf_type_string,
+ gconf_type_short,
+ gconf_type_ushort,
+ gconf_type_int,
+ gconf_type_uint,
+ gconf_type_long,
+ gconf_type_ulong,
+ gconf_type_size,
+/* gconf_type_off,*/
+ gconf_type_uintmax,
+ gconf_type_intmax,
+ gconf_type_time,
+ gconf_type_bool,
+ gconf_type_ipv4,
+ gconf_type_cidr,
+ gconf_type_host,
+ gconf_type_sockaddr,
+ gconf_type_section
+};
+
+#define GCONF_LIST 0x8000
+#define GCONF_TYPE_MASK 0x00ff
+#define GCONF_TYPE(c) ((c) & GCONF_TYPE_MASK)
+#define GCONF_IS_LIST(c) ((c) & GCONF_LIST)
+
+enum gconf_callback_command {
+ gconf_callback_section_begin,
+ gconf_callback_section_end,
+ gconf_callback_set_value
+};
+
+#define GCONF_TYPE_STRING 0
+#define GCONF_TYPE_LIST 1
+#define GCONF_TYPE_ARRAY 2
+
+typedef struct gconf_value {
+ int type;
+ union {
+ gl_list_t list;
+ const char *string;
+ struct {
+ size_t c;
+ struct gconf_value *v;
+ } arg;
+ } v;
+} gconf_value_t;
+
+typedef int (*gconf_callback_fn) (
+ enum gconf_callback_command cmd,
+ gconf_locus_t * /* locus */,
+ void * /* varptr */,
+ gconf_value_t * /* value */,
+ void * /* cb_data */
+ );
+
+struct gconf_keyword {
+ const char *ident;
+ const char *argname;
+ const char *docstring;
+ enum gconf_data_type type;
+ void *varptr;
+ size_t offset;
+ gconf_callback_fn callback;
+ void *callback_data;
+ struct gconf_keyword *kwd;
+};
+
+gconf_value_t *gconf_value_dup(gconf_value_t *input);
+
+extern void gconf_print_diag(gconf_locus_t *, int, int, const char*);
+
+void gconf_warning(gconf_locus_t *locus, int errcode, const char *fmt, ...);
+void gconf_error(gconf_locus_t *locus, int errcode, const char *fmt, ...);
+void gconf_set_keywords(struct gconf_keyword *kwd);
+void gconf_gram_trace(int n);
+void gconf_lex_trace (int n);
+
+int gconf_lex_begin(const char*);
+void gconf_lex_end(void);
+int gconf_parse (const char *name);
+
+void gconf_line_begin (void);
+void gconf_line_add (const char *text, size_t len);
+char *gconf_line_finish (void);
+
+extern gconf_locus_t gconf_current_locus;
+extern int gconf_error_count;
+extern int gconf_default_port;
+
+extern const char *gconf_preprocessor;
+extern bool gconf_log_to_stderr;
+extern void (*gconf_log_setup_hook) ();
+
+size_t gconf_preproc_fill_buffer (char *buf, size_t size);
+void gconf_preproc_add_include_dir (char *dir);
+int gconf_preproc_init (const char *name);
+void gconf_preproc_done (void);
+int gconf_preproc_run (const char *config_file, const char *extpp);
+
+FILE *gconf_preproc_extrn_start (const char *file, pid_t *ppid);
+void gconf_preproc_extrn_shutdown (pid_t pid);
+
+char *gconf_install_text (const char *str);
+void gconf_destroy_text (void);
+
+void gconf_include_path_setup (const char *dir, ...);
+void gconf_include_path_setup_v (char **dirs);
+
+const char *gconf_data_type_string (enum gconf_data_type type);
+void gconf_format_docstring (FILE *stream, const char *docstring,
+ unsigned level);
+void gconf_format_simple_statement (FILE *stream, struct gconf_keyword *kwp,
+ unsigned level);
+void gconf_format_block_statement (FILE *stream, struct gconf_keyword *kwp,
+ unsigned level);
+void gconf_format_statement_array (FILE *stream, struct gconf_keyword *kwp,
+ unsigned n,
+ unsigned level);
+
+
+
diff --git a/gconf/gnulib.modules b/gconf/gnulib.modules
new file mode 100644
index 0000000..c211bcd
--- /dev/null
+++ b/gconf/gnulib.modules
@@ -0,0 +1,13 @@
+hash
+xalloc
+linked-list
+getline
+inttostr
+inttypes
+obstack
+strtoimax
+strtoumax
+vasprintf
+# getdtablesize ??
+c-ctype
+stdbool \ No newline at end of file

Return to:

Send suggestions and report system problems to the System administrator.