diff options
Diffstat (limited to 'src/preproc.c')
-rw-r--r-- | src/preproc.c | 812 |
1 files changed, 0 insertions, 812 deletions
diff --git a/src/preproc.c b/src/preproc.c deleted file mode 100644 index 4d69602..0000000 --- a/src/preproc.c +++ /dev/null @@ -1,812 +0,0 @@ -/* grecs - Gray's Extensible Configuration System - Copyright (C) 2007-2016 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 <wordsplit.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 <signal.h> -#include <glob.h> -#include <unistd.h> - - -int grecs_log_to_stderr = 1; -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 grecs_grecs__flex_debug; -static struct buffer_ctx *context_stack; -static char *linebufbase = NULL; -static size_t linebufsize = 0; - -#define INFILE context_stack->infile -#define LOCUS context_stack->locus -#define POINT context_stack->locus.beg - -static char *linebuf; -static size_t bufsize; -static char *putback_buffer; -static size_t putback_size; -static size_t putback_max; -static glob_t include_glob; -static size_t include_pos; -static int include_once; - -static int push_source (const char *name, int once); -static int pop_source (void); -static int parse_include (const char *text, int once); - -ssize_t -grecs_getline(char **pbuf, size_t *psize, FILE *fp) -{ - char *buf = *pbuf; - size_t size = *psize; - ssize_t off = 0; - - if (!buf) { - size = 1; - buf = grecs_malloc(size); - } - - do { - if (off == size - 1) { - size_t nsize = 2 * size; - if (nsize < size) - grecs_alloc_die(); - buf = grecs_realloc(buf, nsize); - size = nsize; - } - if (!fgets(buf + off, size - off, fp)) { - if (off == 0) - off = -1; - break; - } - off += strlen(buf + off); - } while (buf[off - 1] != '\n'); - - *pbuf = buf; - *psize = size; - return off; -} - -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 = grecs_realloc(putback_buffer, putback_max); - } - strcpy(putback_buffer, str); - putback_size = len - 1; -} - -static void -pp_line_stmt() -{ - size_t ls_size; - size_t pb_size; - - if (grecs_asprintf(&linebufbase, &linebufsize, - "#line %lu \"%s\" %lu\n", - (unsigned long) POINT.line, - POINT.file, (unsigned long) context_stack->xlines)) - grecs_alloc_die(); - - ls_size = strlen(linebufbase); - pb_size = putback_size + ls_size + 1; - - if (pb_size > putback_max) { - putback_max = pb_size; - putback_buffer = grecs_realloc(putback_buffer, putback_max); - } - - context_stack->xlines++; - strcpy(putback_buffer + putback_size, linebufbase); - 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 = grecs_realloc(linebuf, bufsize); - } - strcpy(linebuf, putback_buffer); - rc = putback_size; - putback_size = 0; - } - else if (!context_stack) - return 0; - else - rc = grecs_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') - POINT.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 struct grecs_list *grecs_usr_include_path; -static struct grecs_list *grecs_std_include_path; - -size_t -grecs_include_path_count(int flag) -{ - size_t count = 0; - if (flag & GRECS_STD_INCLUDE) - count += grecs_list_size(grecs_std_include_path); - if (flag & GRECS_USR_INCLUDE) - count += grecs_list_size(grecs_usr_include_path); - return count; -} - -static int -foreach_dir(struct grecs_list *list, int flag, - int (*fun)(int, const char *, void *), void *data) -{ - int rc = 0; - struct grecs_list_entry *ep; - - for (ep = list->head; rc == 0 && ep; ep = ep->next) - rc = fun(flag, ep->data, data); - return rc; -} - -int -grecs_foreach_include_dir(int flag, int (*fun)(int, const char *, void *), - void *data) -{ - int rc = 0; - - if (flag & GRECS_STD_INCLUDE) - rc = foreach_dir(grecs_std_include_path, GRECS_STD_INCLUDE, fun, data); - if (rc == 0 && (flag & GRECS_USR_INCLUDE)) - rc = foreach_dir(grecs_usr_include_path, GRECS_USR_INCLUDE, fun, data); - return rc; -} - - -struct file_data { - const char *name; - size_t namelen; - char *buf; - size_t buflen; - int found; -}; - -static int -pp_list_find(struct grecs_list *list, struct file_data *dptr) -{ - struct grecs_list_entry *ep; - - if (!list) - return 0; - for (ep = list->head; !dptr->found && ep; ep = ep->next) { - const char *dir = ep->data; - size_t size = strlen (dir) + 1 + dptr->namelen + 1; - if (size > dptr->buflen) { - dptr->buflen = size; - dptr->buf = grecs_realloc(dptr->buf, dptr->buflen); - } - strcpy(dptr->buf, dir); - strcat(dptr->buf, "/"); - strcat(dptr->buf, dptr->name); - dptr->found = access(dptr->buf, F_OK) == 0; - } - return dptr->found; -} - -static void -incl_free(void *data) -{ - grecs_free(data); -} - -void -grecs_include_path_clear() -{ - if (grecs_usr_include_path) - grecs_list_clear(grecs_usr_include_path); - if (grecs_std_include_path) - grecs_list_clear(grecs_std_include_path); -} - -void -grecs_include_path_setup_v(char **dirs) -{ - if (!grecs_usr_include_path) { - grecs_usr_include_path = grecs_list_create(); - grecs_usr_include_path->free_entry = incl_free; - } - grecs_std_include_path = grecs_list_create(); - grecs_std_include_path->free_entry = incl_free; - if (dirs) { - int i; - for (i = 0; dirs[i]; i++) - /* FIXME: Element never freed */ - grecs_list_append(grecs_std_include_path, - grecs_strdup(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; - else - argc += 16; - argv = grecs_realloc(argv, argc * sizeof(argv[0])); - } - argv[argi++] = (char*) p; - if (!p) - break; - p = va_arg(ap, const char*); - } - grecs_include_path_setup_v(argv); - grecs_free(argv); - va_end(ap); -} - -void -grecs_preproc_add_include_dir(char *dir) -{ - if (!grecs_usr_include_path) { - grecs_usr_include_path = grecs_list_create(); - grecs_usr_include_path->free_entry = incl_free; - } - grecs_list_append(grecs_usr_include_path, grecs_strdup(dir)); -} - -static struct grecs_symtab *incl_sources; - -/* Calculate the hash of a struct input_file_ident. */ -static unsigned -incl_hasher(void *data, unsigned long 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 int -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 int -incl_copy(void *dst, void *src) -{ - memcpy(dst, src, sizeof(struct input_file_ident)); - return 0; -} - -static int -source_lookup(struct stat *st) -{ - struct input_file_ident key; - int install = 1; - - if (!incl_sources) { - incl_sources = grecs_symtab_create( - sizeof(struct input_file_ident), - incl_hasher, - incl_compare, - incl_copy, - NULL,/*FIXME: alloc*/ - NULL); - if (!incl_sources) - grecs_alloc_die(); - } - - key.i_node = st->st_ino; - key.device = st->st_dev; - if (!grecs_symtab_lookup_or_install(incl_sources, &key, &install)) - grecs_alloc_die(); - return !install; -} - - -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 (POINT.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(context_stack ? &LOCUS : NULL, errno, - _("Cannot open `%s'"), name); - return 1; - } - - /* Push current context */ - ctx = grecs_malloc(sizeof(*ctx)); - ctx->locus.beg.file = grecs_install_text(name); - ctx->locus.beg.line = 1; - ctx->locus.beg.col = 0; - ctx->locus.end.file = NULL; - ctx->locus.end.line = ctx->locus.end.col = 0; - ctx->xlines = 0; - ctx->namelen = strlen(ctx->locus.beg.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 (grecs_grecs__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; - grecs_free(context_stack); - context_stack = ctx; - - if (include_pos < include_glob.gl_pathc) { - push_source(include_glob.gl_pathv[include_pos++], include_once); - return 0; - } else if (include_glob.gl_pathc) { - globfree(&include_glob); - include_pos = include_glob.gl_pathc = 0; - } - - if (!context_stack) { - if (grecs_grecs__flex_debug) - fprintf(stderr, "End of input\n"); - return 1; - } - - POINT.line++; - - if (grecs_grecs__flex_debug) - fprintf(stderr, "Resuming file `%s' at line %lu\n", - POINT.file, (unsigned long) POINT.line); - - pp_line_stmt(); - - return 0; -} - -char * -grecs_find_include_file(const char *name, int allow_cwd) -{ - static char *cwd = "."; - struct file_data fd; - - fd.name = name; - fd.namelen = strlen(name); - fd.buf = NULL; - fd.buflen = 0; - fd.found = 0; - - if (!grecs_usr_include_path) - grecs_include_path_setup(NULL); - if (allow_cwd) { - grecs_list_append(grecs_usr_include_path, cwd); - pp_list_find(grecs_usr_include_path, &fd); - grecs_list_remove_tail(grecs_usr_include_path); - } else - pp_list_find(grecs_usr_include_path, &fd); - - if (!fd.found) { - pp_list_find(grecs_std_include_path, &fd); - if (!fd.found) - return NULL; - } - return fd.buf; -} - -static int -isglob(const char *s) -{ - for (; *s; s++) { - if (strchr("*?[", *s)) - return 1; - } - return 0; -} - -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 (isglob(p)) { - switch (glob(p, 0, NULL, &include_glob)) { - case 0: - include_pos = 0; - include_once = once; - break; - case GLOB_NOSPACE: - grecs_alloc_die(); - case GLOB_NOMATCH: - break; - default: - grecs_error(&LOCUS, 0, _("read error")); - } - p = NULL; - } else if (p[0] != '/') { - char *q = p; - p = grecs_find_include_file(q, allow_cwd); - if (!p) - grecs_error(&LOCUS, 0, - _("%s: No such file or directory"), - q); - } - } - - if (p) - rc = push_source(p, once); - else if (include_pos < include_glob.gl_pathc) - rc = push_source(include_glob.gl_pathv[include_pos++], once); - - grecs_free(tmp); - wordsplit_free(&ws); - return rc; -} - -int -grecs_preproc_init(const char *name) -{ - return push_source(name, 0); -} - -void -grecs_preproc_done() -{ - grecs_symtab_free(incl_sources); - incl_sources = NULL; - - grecs_free(linebuf); - linebuf = NULL; - bufsize = 0; - - grecs_free(putback_buffer); - putback_buffer = NULL; - putback_size = putback_max = 0; - - free(linebufbase); /* Allocated via standard malloc/realloc */ - linebufbase = NULL; - linebufsize = 0; -} - -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 = NULL; - - setup_file = grecs_find_include_file("pp-setup", 1); - if (setup_file) { - size_t size = 0; - if (grecs_asprintf(&cmd, &size, - "%s %s -", extpp, setup_file)) - grecs_alloc_die(); - grecs_free(setup_file); - } else - cmd = grecs_strdup(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); - grecs_free(cmd); - return 1; - } - - while ((i = grecs_preproc_fill_buffer(buffer, sizeof buffer))) - fwrite(buffer, 1, i, outfile); - pclose(outfile); - grecs_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);*/ - - if (pipe(pout)) { - grecs_error(NULL, errno, "pipe"); - return NULL; - } - switch (pid = fork()) { - /* The child branch. */ - case 0: - if (pout[1] != 1) { - if (dup2(pout[1], 1) == -1) { - grecs_error(NULL, errno, "dup2"); - exit(127); - } - } - - /* 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); - if (pipe(p)) { - grecs_error(NULL, errno, "pipe"); - exit(127); - } - switch (pid = fork()) { - /* Grandchild */ - case 0: - if (p[1] != 2 && dup2(p[1], 2) == -1) { - grecs_error(NULL, errno, "dup2"); - exit(127); - } - 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 (grecs_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); -} - |