aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-15 00:03:43 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2011-05-15 14:00:57 +0300
commitfd64fa62bc68d8c2e0d693033e874fcc1f023544 (patch)
tree4e9f680539dc9ceb32ea6797c3f917586b2f757f /src
parentf83e3229ea5c917dfb1bd1a59b1768fdcb882f9a (diff)
downloadgrecs-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.c102
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

Return to:

Send suggestions and report system problems to the System administrator.