diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-03 17:49:29 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-03 21:23:21 +0300 |
commit | 3d679b3df641f59fb81ca1651799f4e2965ed67e (patch) | |
tree | 5a614ee25cff44d015ee9e6f6920e2ba19379bba /src/lookup.c | |
parent | 24ec67c9f6375d34d88e79981ed8abbe15a78169 (diff) | |
download | grecs-3d679b3df641f59fb81ca1651799f4e2965ed67e.tar.gz grecs-3d679b3df641f59fb81ca1651799f4e2965ed67e.tar.bz2 |
Switch to the two-layer model. Add testsuite.
The configuration file parser creates a syntax tree. This step
does not require any knowledge about which keywords are allowed.
The user can then either use that tree directly, or post-process
it using parser tables. The latter approach is equivalent to
previous versions of grecs.
Diffstat (limited to 'src/lookup.c')
-rw-r--r-- | src/lookup.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/src/lookup.c b/src/lookup.c new file mode 100644 index 0000000..fdf1ae1 --- /dev/null +++ b/src/lookup.c @@ -0,0 +1,272 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2011 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 <string.h> +#include <errno.h> +#include <ctype.h> +#include "grecs.h" +#include "wordsplit.h" + +static int +_grecs_list_eq(struct grecs_value *a, struct grecs_value *b) +{ + struct grecs_list_entry *aent, *bent; + if (grecs_list_size(a->v.list) != grecs_list_size(b->v.list)) + return 0; + + for (aent = a->v.list->head, bent = b->v.list->head;; + aent = aent->next, bent = bent->next) { + if (!aent) + return bent == NULL; + if (!bent) + return 0; + if (!grecs_value_eq(aent->data, bent->data)) + return 0; + } + /*notreached*/ + return 1; +} + +static int +_grecs_array_eq(struct grecs_value *a, struct grecs_value *b) +{ + size_t i; + + if (a->v.arg.c != b->v.arg.c) + return 0; + + for (i = 0; i < a->v.arg.c; i++) + if (!grecs_value_eq(&a->v.arg.v[i], &b->v.arg.v[i])) + return 0; + return 1; +} + +/* Return 1 if configuration value A equals B */ +int +grecs_value_eq(struct grecs_value *a, struct grecs_value *b) +{ + if (a->type != b->type) + return 0; + switch (a->type) { + case GRECS_TYPE_STRING: + if (a->v.string == NULL) + return b->v.string == NULL; + return strcmp(a->v.string, b->v.string) == 0; + + case GRECS_TYPE_LIST: + return _grecs_list_eq(a, b); + + case GRECS_TYPE_ARRAY: + return _grecs_array_eq(a, b); + } + return 0; +} + + +static int +split_cfg_path(const char *path, int *pargc, char ***pargv) +{ + int argc; + char **argv; + char *delim = "."; + char static_delim[2] = { 0, 0 }; + + if (path[0] == '\\') { + argv = calloc(2, sizeof (*argv)); + if (!argv) + return ENOMEM; + argv[0] = strdup(path + 1); + if (!argv[0]) { + free(argv); + return ENOMEM; + } + argv[1] = NULL; + argc = 1; + } else { + struct wordsplit ws; + + if (ispunct(path[0])) { + delim = static_delim; + delim[0] = path[0]; + path++; + } + ws.ws_delim = delim; + + if (wordsplit(path, &ws, WRDSF_DEFFLAGS|WRDSF_DELIM)) + return errno; + argc = ws.ws_wordc; + argv = ws.ws_wordv; + ws.ws_wordc = 0; + ws.ws_wordv = NULL; + wordsplit_free(&ws); + } + + *pargc = argc; + *pargv = argv; + + return 0; +} + +static void +free_value_mem(struct grecs_value *p) +{ + switch (p->type) { + case GRECS_TYPE_STRING: + free((char*)p->v.string); + break; + + case GRECS_TYPE_LIST: + /* FIXME */ + break; + + case GRECS_TYPE_ARRAY: { + size_t i; + for (i = 0; i < p->v.arg.c; i++) + free_value_mem(&p->v.arg.v[i]); + } + } +} + +static void +destroy_value(void *p) +{ + struct grecs_value*val = p; + if (val) { + free_value_mem(val); + free(val); + } +} + +static struct grecs_value * +parse_label(const char *str) +{ + struct grecs_value *val = NULL; + size_t i; + struct wordsplit ws; + size_t len = strlen (str); + + if (len > 1 && str[0] == '(' && str[len-1] == ')') { + struct grecs_list *lst; + + ws.ws_delim = ","; + if (wordsplit_len (str + 1, len - 2, &ws, + WRDSF_DEFFLAGS|WRDSF_DELIM| + WRDSF_WS)) { + return NULL; + } + + lst = grecs_list_create(); + lst->free_entry = destroy_value; + for (i = 0; i < ws.ws_wordc; i++) { + struct grecs_value *p = grecs_malloc(sizeof(*p)); + p->type = GRECS_TYPE_STRING; + p->v.string = ws.ws_wordv[i]; + grecs_list_append(lst, p); + } + val = grecs_malloc(sizeof(*val)); + val->type = GRECS_TYPE_LIST; + val->v.list = lst; + } else { + if (wordsplit(str, &ws, WRDSF_DEFFLAGS)) + return NULL; + val = grecs_malloc(sizeof(*val)); + if (ws.ws_wordc == 1) { + val->type = GRECS_TYPE_STRING; + val->v.string = ws.ws_wordv[0]; + } else { + val->type = GRECS_TYPE_ARRAY; + val->v.arg.c = ws.ws_wordc; + val->v.arg.v = grecs_malloc(ws.ws_wordc * + sizeof(val->v.arg.v[0])); + for (i = 0; i < ws.ws_wordc; i++) { + val->v.arg.v[i].type = GRECS_TYPE_STRING; + val->v.arg.v[i].v.string = ws.ws_wordv[i]; + } + } + ws.ws_wordc = 0; + wordsplit_free(&ws); + } + return val; +} + + +struct find_closure { + int argc; + char **argv; + int tag; + struct grecs_value *label; + struct grecs_node *node; +}; + +static void +parse_tag(struct find_closure *fptr) +{ + char *p = strchr(fptr->argv[fptr->tag], '='); + if (p) { + *p++ = 0; + fptr->label = parse_label(p); + } + else + fptr->label = NULL; +} + +static enum grecs_tree_recurse_res +node_finder(enum grecs_tree_recurse_op op, struct grecs_node *node, void *data) +{ + struct find_closure *fdptr = data; + + if (op == grecs_tree_recurse_post) + return grecs_tree_recurse_ok; + + if (strcmp(fdptr->argv[fdptr->tag], node->ident) == 0 + && (!fdptr->label || grecs_value_eq(fdptr->label, &node->value))) { + fdptr->tag++; + if (fdptr->tag == fdptr->argc) { + fdptr->node = node; + return grecs_tree_recurse_stop; + } + parse_tag(fdptr); + return grecs_tree_recurse_ok; + } + + return node->type == grecs_node_block ? + grecs_tree_recurse_skip : grecs_tree_recurse_ok; +} + +struct grecs_node * +grecs_find_node(struct grecs_node *node, const char *path) +{ + int rc, i; + struct find_closure clos; + + rc = split_cfg_path(path, &clos.argc, &clos.argv); + if (!clos.argc) + return NULL; + clos.tag = 0; + clos.label = NULL; + clos.node = NULL; + parse_tag(&clos); + grecs_tree_recurse(node, node_finder, &clos); + for (i = 0; i < clos.argc; i++) + free(clos.argv[i]); + free(clos.argv); + destroy_value(clos.label); + return clos.node; +} + |