aboutsummaryrefslogtreecommitdiff
path: root/src/lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lookup.c')
-rw-r--r--src/lookup.c272
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;
+}
+

Return to:

Send suggestions and report system problems to the System administrator.