diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-15 00:03:43 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-15 14:00:57 +0300 |
commit | fd64fa62bc68d8c2e0d693033e874fcc1f023544 (patch) | |
tree | 4e9f680539dc9ceb32ea6797c3f917586b2f757f /src | |
parent | f83e3229ea5c917dfb1bd1a59b1768fdcb882f9a (diff) | |
download | grecs-fd64fa62bc68d8c2e0d693033e874fcc1f023544.tar.gz grecs-fd64fa62bc68d8c2e0d693033e874fcc1f023544.tar.bz2 |
Improve wildcard matching.
* src/lookup.c (grecs_value_match): New function.
(grecs_match_buf_free): Ignore NULL argument.
(grecs_match): Implement single-occurrence wildcard (%). Match
values using fnmatch globbing patterns.
(grecs_match_first): Compress multiple contiguous *
Diffstat (limited to 'src')
-rw-r--r-- | src/lookup.c | 102 |
1 files changed, 98 insertions, 4 deletions
diff --git a/src/lookup.c b/src/lookup.c index d30d348..a557845 100644 --- a/src/lookup.c +++ b/src/lookup.c @@ -19,12 +19,13 @@ #endif #include <string.h> #include <errno.h> #include <ctype.h> #include "grecs.h" #include "wordsplit.h" +#include <fnmatch.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)) @@ -76,12 +77,79 @@ grecs_value_eq(struct grecs_value *a, struct grecs_value *b) case GRECS_TYPE_ARRAY: return _grecs_array_eq(a, b); } return 0; } + +static int +_grecs_list_match(struct grecs_value *pat, struct grecs_value *b, int flags) +{ + struct grecs_list_entry *aent, *bent; + if (grecs_list_size(pat->v.list) != grecs_list_size(b->v.list)) + return 0; + + for (aent = pat->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_match(aent->data, bent->data, flags)) + return 0; + } + /*notreached*/ + return 1; +} + +static int +_grecs_array_match(struct grecs_value *pat, struct grecs_value *b, int flags) +{ + size_t i; + + if (pat->v.arg.c > b->v.arg.c) + return 0; + + for (i = 0; i < pat->v.arg.c; i++) + if (!grecs_value_match(pat->v.arg.v[i], b->v.arg.v[i], flags)) + return 0; + return 1; +} + +int +grecs_value_match(struct grecs_value *pat, struct grecs_value *b, int flags) +{ + if (pat == 0 || b == 0) + return pat == b; + if (pat->type != b->type) { + if (pat->type != GRECS_TYPE_STRING) + return 0; + switch (b->type) { + case GRECS_TYPE_LIST: + b = grecs_list_index(b->v.list, 0); + break; + + case GRECS_TYPE_ARRAY: + b = b->v.arg.v[0]; + } + } + + switch (pat->type) { + case GRECS_TYPE_STRING: + if (pat->v.string == NULL) + return b->v.string == NULL; + return fnmatch(pat->v.string, b->v.string, flags) == 0; + + case GRECS_TYPE_LIST: + return _grecs_list_match(pat, b, flags); + + case GRECS_TYPE_ARRAY: + return _grecs_array_match(pat, b, flags); + } + return 0; +} struct grecs_match_buf { int argc; char **argv; int argi; @@ -101,14 +169,16 @@ grecs_match_buf_free_contents(struct grecs_match_buf *buf) free(buf->labelv); } void grecs_match_buf_free(struct grecs_match_buf *buf) { - grecs_match_buf_free_contents(buf); - free(buf); + if (buf) { + grecs_match_buf_free_contents(buf); + free(buf); + } } static struct grecs_value * parse_label(const char *str) { @@ -330,16 +400,18 @@ grecs_match(struct grecs_match_buf *buf) wcard = 1; if (buf->argi-- == 0) return 1; continue; } - if (strcmp(buf->argv[buf->argi], node->ident) == 0 + if ((ISWC(buf->argv[buf->argi], '%') || + strcmp(buf->argv[buf->argi], node->ident) == 0) /* FIXME: */ && (!buf->labelv[buf->argi] || - grecs_value_eq(buf->labelv[buf->argi], node->v.value))) { + grecs_value_match(buf->labelv[buf->argi], + node->v.value, 0))) { wcard = 0; node = node->up; if (buf->argi-- == 0) return !node || node->type == grecs_node_root; } else if (!wcard) @@ -362,12 +434,13 @@ grecs_match_next(struct grecs_match_buf *buf) } struct grecs_node * grecs_match_first(struct grecs_node *tree, const char *pattern, struct grecs_match_buf **pbuf) { + int i; struct grecs_node *node; struct grecs_match_buf *buf; if (tree->type != grecs_node_root) { errno = EINVAL; return NULL; @@ -378,12 +451,33 @@ grecs_match_first(struct grecs_node *tree, const char *pattern, if (split_cfg_path(pattern, &buf->argc, &buf->argv, &buf->labelv)) { free(buf); return NULL; } /* FIXME: Compress argv/argc by replacing contiguous sequences of *'s with a single *. */ + for (i = 0; i < buf->argc; i++) { + if (ISWC(buf->argv[i], '*')) { + int j; + + for (j = i + 1; + j < buf->argc && ISWC(buf->argv[j], '*'); j++) + free(buf->argv[j]); + j -= i; + if (j > 1) { + memmove(&buf->argv[i+1], &buf->argv[i+j], + (buf->argc - (i + j)) * + sizeof(buf->argv[0])); + memmove(&buf->labelv[i+1], &buf->labelv[i+j], + (buf->argc - (i + j)) * + sizeof(buf->labelv[0])); + buf->argc -= j - 1; + } + } + } + + buf->argi = 0; buf->node = grecs_tree_first_node(tree); *pbuf = buf; if (grecs_match(buf)) node = buf->node; else |