diff options
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 @@ -22,6 +22,7 @@ #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) @@ -79,6 +80,73 @@ grecs_value_eq(struct grecs_value *a, struct grecs_value *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 { @@ -104,8 +172,10 @@ grecs_match_buf_free_contents(struct grecs_match_buf *buf) 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); + } } @@ -333,10 +403,12 @@ grecs_match(struct grecs_match_buf *buf) 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) @@ -365,6 +437,7 @@ 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; @@ -381,6 +454,27 @@ grecs_match_first(struct grecs_node *tree, const char *pattern, } /* 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; |