[Issue #24] Implemented extended shell pattern using a PCRE translator

This commit is contained in:
Snaipe 2015-05-03 00:41:58 +02:00
parent 4a04a8a703
commit bff5081611
5 changed files with 212 additions and 4 deletions

View file

@ -18,7 +18,7 @@ libcriterion_la_CFLAGS = \
libcriterion_la_LDFLAGS = $(COVERAGE_LDFLAGS) -version-info 1:0:0
# dirty but unless someone has a better alternative...
libcriterion_la_LIBADD = dependencies/csptr/src/libcsptr_la-*.lo
libcriterion_la_LIBADD = dependencies/csptr/src/libcsptr_la-*.lo -lpcre
EXTRA_DIST = config.rpath LICENSE
@ -59,6 +59,8 @@ libcriterion_la_SOURCES = \
src/i18n.h \
src/ordered-set.c \
src/posix-compat.c \
src/extmatch.c \
src/extmatch.h \
src/main.c
TARGET = $(PACKAGE)-$(VERSION)

View file

@ -14,7 +14,7 @@ TESTS_ENVIRONMENT = CRITERION_ALWAYS_SUCCEED=1
check_PROGRAMS := $(BIN_TESTS)
CFLAGS = -I$(top_srcdir)/include/ -std=c99 -Wall -Wextra -pedantic
LDADD = -L$(top_srcdir)/ -lcriterion
LDADD = -L$(top_srcdir)/ -lcriterion -lpcre
if ENABLE_RT_TESTS
BIN_TESTS += with-time

200
src/extmatch.c Normal file
View file

@ -0,0 +1,200 @@
#include <stddef.h>
#include <string.h>
#include <pcre.h>
#include <stdio.h>
#include "criterion/common.h"
struct context {
int depth;
char *dst;
const char *src;
char old, cur;
int eos;
};
void transform_impl(struct context *ctx);
static inline void transform_rec(struct context *ctx) {
struct context new_ctx = {
.depth = ctx->depth + 1,
.dst = ctx->dst,
.src = ctx->src,
.old = ctx->old,
.eos = ctx->eos,
};
transform_impl(&new_ctx);
ctx->dst = new_ctx.dst;
ctx->src = new_ctx.src;
ctx->old = new_ctx.old;
ctx->eos = new_ctx.eos;
}
static inline char read_char(struct context *ctx) {
char c = *ctx->src;
ctx->old = ctx->cur;
ctx->cur = c;
if (c == '\0')
ctx->eos = 1;
++ctx->src;
return c;
}
static inline char peek_char(struct context *ctx) {
return *ctx->src;
}
static inline void copy_char(struct context *ctx, char src) {
*ctx->dst = src;
++ctx->dst;
}
static inline void dup_char(struct context *ctx) {
copy_char(ctx, read_char(ctx));
}
static inline void copy_str(struct context *ctx, const char *src) {
size_t len = strlen(src);
strncpy(ctx->dst, src, len);
ctx->dst += len;
}
#define PREPREFIX 0
#define POSTPREFIX 1
#define PRESUFFIX 2
#define POSTSUFFIX 3
#define ELSESTR 4
typedef struct {
int (*validator)(struct context *ctx);
char *str;
} handler_arg;
static int active() { return 1; }
static int inactive() { return 0; }
static int is_eos(struct context *ctx) {
return peek_char(ctx) == '\0';
}
static inline void handle_special(struct context *ctx, handler_arg strs[5]) {
if (peek_char(ctx) == '(') {
if ((strs[0].validator ?: inactive)(ctx))
copy_str(ctx, strs[0].str);
dup_char(ctx);
if ((strs[1].validator ?: inactive)(ctx))
copy_str(ctx, strs[1].str);
transform_rec(ctx);
if ((strs[2].validator ?: inactive)(ctx))
copy_str(ctx,strs[2].str);
copy_char(ctx, ')');
if ((strs[3].validator ?: inactive)(ctx))
copy_str(ctx, strs[3].str);
} else if ((strs[4].validator ?: inactive)(ctx)) {
copy_str(ctx, strs[4].str);
}
}
# define Handler(Name, ...) \
static void Name(struct context *ctx, UNUSED char c) { \
handle_special(ctx, (handler_arg[5]) { \
__VA_ARGS__ \
}); \
}
# define _ active
Handler(handle_plus, [POSTSUFFIX] = {_, "+"}, [ELSESTR] = {_, "+" });
Handler(handle_star, [POSTSUFFIX] = {_, "*"}, [ELSESTR] = {_, ".*"});
Handler(handle_wild, [POSTSUFFIX] = {_, "?"}, [ELSESTR] = {_, "." });
Handler(handle_excl,
[POSTPREFIX] = {_, "?!"},
[PRESUFFIX] = {is_eos, "$" },
[POSTSUFFIX] = {_, ".*"},
[ELSESTR] = {_, "!" }
);
Handler(handle_at, [ELSESTR] = {_, "@"});
# undef _
static void handle_rbra(struct context *ctx, UNUSED char c) {
copy_char(ctx, c);
if (peek_char(ctx) == '!') {
read_char(ctx);
copy_char(ctx, '^');
}
}
static void escape_char(struct context *ctx, char c) {
copy_char(ctx, '\\');
copy_char(ctx, c);
}
static void escape_pipe(struct context *ctx, UNUSED char c) {
if (ctx->depth == 0)
copy_char(ctx, '\\');
copy_char(ctx, '|');
}
typedef void (*f_handler)(struct context *, char);
void transform_impl(struct context *ctx) {
static f_handler handlers[] = {
['+'] = handle_plus,
['*'] = handle_star,
['?'] = handle_wild,
['!'] = handle_excl,
['['] = handle_rbra,
['@'] = handle_at,
['.'] = escape_char,
['('] = escape_char,
[')'] = escape_char,
['|'] = escape_pipe,
};
for (char c = read_char(ctx); !ctx->eos; c = read_char(ctx)) {
f_handler handler = handlers[(unsigned char) c];
if (ctx->old == '\\')
handler = copy_char;
if (c == ')' && ctx->depth > 0)
return;
(handler ?: copy_char)(ctx, c);
if (ctx->eos)
return;
}
if (ctx->depth > 0) {
puts("pattern error: mismatching parenthesis");
exit(1);
}
}
static void transform(const char *pattern, char *result) {
struct context ctx = {
.src = pattern,
.dst = result,
};
copy_char(&ctx, '^');
transform_impl(&ctx);
copy_char(&ctx, '$');
copy_char(&ctx, '\0');
}
int extmatch(const char *pattern, const char *string) {
char regex[strlen(pattern) * 2];
transform(pattern, regex);
const char *errmsg;
int erroffset;
pcre *preg = pcre_compile(regex, 0, &errmsg, &erroffset, NULL);
if (preg) {
int res = pcre_exec(preg, NULL, string, strlen(string), 0, 0, NULL, 0);
pcre_free(preg);
return res;
}
printf("pattern error: %s\n", errmsg);
exit(1);
}

6
src/extmatch.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef EXTMATCH_H_
# define EXTMATCH_H_
int extmatch(const char *pattern, const char *string);
#endif /* !EXTMATCH_H_ */

View file

@ -33,7 +33,7 @@
#include "config.h"
#ifdef HAVE_FNMATCH
#include <fnmatch.h>
#include "extmatch.h"
#endif
#define IMPL_CALL_REPORT_HOOKS(Kind) \
@ -60,7 +60,7 @@ void disable_unmatching(struct criterion_test_set *set) {
continue;
FOREACH_SET(struct criterion_test *test, s->tests) {
if (fnmatch(criterion_options.pattern, test->data->identifier_, 0))
if (extmatch(criterion_options.pattern, test->data->identifier_))
test->data->disabled = true;
}
}