summaryrefslogtreecommitdiffabout
path: root/src/preproc.c
authorSergey Poznyakoff <gray@gnu.org.ua>2009-04-20 11:48:39 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2009-04-20 11:48:39 (GMT)
commit0f081a2643ba0a7f7a5dcfb6a5977d9da1d2b6db (patch) (side-by-side diff)
treeb62e9404cd5dfdfa471ec1202e183f9ca92187e5 /src/preproc.c
parent0983c9ab7a6ea5b3592a297e029a935cc0e4bebc (diff)
downloadgrecs-0f081a2643ba0a7f7a5dcfb6a5977d9da1d2b6db.tar.gz
grecs-0f081a2643ba0a7f7a5dcfb6a5977d9da1d2b6db.tar.bz2
Diverge from Wydawca gconf/ subdirectory into a separate project
Diffstat (limited to 'src/preproc.c') (more/less context) (ignore whitespace changes)
-rw-r--r--src/preproc.c728
1 files changed, 728 insertions, 0 deletions
diff --git a/src/preproc.c b/src/preproc.c
new file mode 100644
index 0000000..8ae088d
--- a/dev/null
+++ b/src/preproc.c
@@ -0,0 +1,728 @@
+/* grecs - Gray's Extensible Configuration System
+ Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff
+
+ Grecs is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ Grecs is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Grecs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <grecs.h>
+#include <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 <wordsplit.h>
+
+#if ENABLE_NLS
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) msgid
+#endif
+
+bool grecs_log_to_stderr = true;
+void (*grecs_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 */
+ grecs_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
+grecs_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 *grecs_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 (!dptr->found && 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);
+ dptr->found = access (dptr->buf, F_OK) == 0;
+ }
+ 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
+grecs_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
+grecs_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*);
+ }
+ grecs_include_path_setup_v (argv);
+ free (argv);
+ va_end (ap);
+}
+
+void
+grecs_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)
+ {
+ grecs_error (&LOCUS, errno, _("Cannot stat `%s'"), name);
+ return 1;
+ }
+
+ if (LOCUS.file && STAT_ID_EQ (st, context_stack->id))
+ {
+ grecs_error (&LOCUS, 0, _("Recursive inclusion"));
+ return 1;
+ }
+
+ if ((ctx = ctx_lookup (&st)))
+ {
+ grecs_error (&LOCUS, 0, _("Recursive inclusion"));
+ if (ctx->prev)
+ grecs_error (&ctx->prev->locus, 0,
+ _("`%s' already included here"), name);
+ else
+ grecs_error (&LOCUS, 0,
+ _("`%s' already included at top level"), name);
+ return 1;
+ }
+ }
+ else if (rc)
+ {
+ grecs_error (NULL, errno, _("Cannot stat `%s'"), name);
+ return 1;
+ }
+
+ if (once && source_lookup (&st))
+ return -1;
+
+ fp = fopen (name, "r");
+ if (!fp)
+ {
+ grecs_error (&LOCUS, errno, _("Cannot open `%s'"), name);
+ return 1;
+ }
+
+ /* Push current context */
+ ctx = xmalloc (sizeof (*ctx));
+ ctx->locus.file = grecs_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)
+ grecs_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)
+ {
+ grecs_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)
+{
+ struct wordsplit ws;
+ char *tmp = NULL;
+ char *p = NULL;
+ int rc = 1;
+
+ if (wordsplit (text, &ws, WRDSF_DEFFLAGS))
+ grecs_error (&LOCUS, 0, _("Cannot parse include line"));
+ else if (ws.ws_wordc != 2)
+ {
+ wordsplit_free (&ws);
+ grecs_error (&LOCUS, 0, _("invalid include statement"));
+ }
+ else
+ {
+ size_t len;
+ int allow_cwd;
+
+ p = ws.ws_wordv[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);
+ wordsplit_free (&ws);
+ return rc;
+}
+
+int
+grecs_preproc_init (const char *name)
+{
+ return push_source (name, 0);
+}
+
+void
+grecs_preproc_done ()
+{
+ if (incl_sources)
+ hash_free (incl_sources);
+ free (linebuf);
+ free (putback_buffer);
+}
+
+int
+grecs_preproc_run (const char *config_file, const char *extpp)
+{
+ size_t i;
+ char buffer[512];
+
+ if (grecs_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)
+ {
+ grecs_error (NULL, errno,
+ _("Unable to start external preprocessor `%s'"), cmd);
+ free (cmd);
+ return 1;
+ }
+
+ while ((i = grecs_preproc_fill_buffer (buffer, sizeof buffer)))
+ fwrite (buffer, 1, i, outfile);
+ pclose (outfile);
+ free (cmd);
+ }
+ else
+ {
+ while ((i = grecs_preproc_fill_buffer (buffer, sizeof buffer)))
+ fwrite (buffer, 1, i, stdout);
+ }
+ grecs_preproc_done ();
+ return 0;
+}
+
+FILE *
+grecs_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 (!grecs_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 (grecs_preproc_run (file_name, grecs_preprocessor))
+ exit (127);
+ exit (0);
+
+ case -1:
+ /* Fork failed */
+ if (grecs_log_setup_hook)
+ grecs_log_setup_hook ();
+ grecs_error (NULL, errno, _("Cannot run `%s'"),
+ grecs_preprocessor);
+ exit (127);
+
+ default:
+ /* Sub-master */
+ close (p[1]);
+ fp = fdopen (p[0], "r");
+ if (grecs_log_setup_hook)
+ grecs_log_setup_hook ();
+ while (getline (&buf, &size, fp) > 0)
+ grecs_error (NULL, 0, "%s", buf);
+ }
+ }
+ else
+ {
+ grecs_preproc_run (file_name, grecs_preprocessor);
+ }
+ exit (0);
+
+ case -1:
+ /* Fork failed */
+ grecs_error (NULL, errno, _("Cannot run `%s'"), grecs_preprocessor);
+ break;
+
+ default:
+ close (pout[1]);
+ fp = fdopen (pout[0], "r");
+ break;
+ }
+ *ppid = pid;
+ return fp;
+}
+
+void
+grecs_preproc_extrn_shutdown (pid_t pid)
+{
+ int status;
+ waitpid (pid, &status, 0);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.