extended glob matcher with brzozowski derivative (#111)

This commit is contained in:
a1lu 2016-07-23 22:32:57 +02:00 committed by Franklin Mathieu
parent 0c9c76599d
commit 4d6d741357
17 changed files with 1228 additions and 390 deletions

View file

@ -18,8 +18,6 @@ if (I18N AND GETTEXT_FOUND AND LIBINTL_LIB_FOUND)
set (ENABLE_NLS 1)
endif ()
cr_find_package (PCRE PKGCONFIG libpcre)
# Check for functions
check_function_exists(strtok_s HAVE_STRTOK_S)

View file

@ -30,10 +30,6 @@ set(ENV{CRITERION_NO_EARLY_EXIT} "1")
set(ENV{CRITERION_JOBS} "1")
set(ENV{CRITERION_DISABLE_TIME_MEASUREMENTS} "1")
if (NOT ENABLE_PATTERN_TESTS)
set(ENV{CRITERION_TESTS_PATTERN} "off")
endif ()
if (WIN32)
if (ENV{MINGW} STREQUAL "")
set (MINGW_HOME "C:/MinGW")

View file

@ -1,37 +0,0 @@
# Copyright (C) 2007-2009 LuaDist.
# Created by Peter Kapec <kapecp@gmail.com>
# Redistribution and use of this file is allowed according to the terms of the MIT license.
# For details see the COPYRIGHT file distributed with LuaDist.
# Note:
# Searching headers and libraries is very simple and is NOT as powerful as scripts
# distributed with CMake, because LuaDist defines directories to search for.
# Everyone is encouraged to contact the author with improvements. Maybe this file
# becomes part of CMake distribution sometimes.
# - Find pcre
# Find the native PCRE headers and libraries.
#
# PCRE_INCLUDE_DIRS - where to find pcre.h, etc.
# PCRE_LIBRARIES - List of libraries when using pcre.
# PCRE_FOUND - True if pcre found.
# Look for the header file.
FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h)
# Look for the library.
FIND_LIBRARY(PCRE_LIBRARY NAMES pcre)
# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
# Copy the results to the output variables.
IF(PCRE_FOUND)
SET(PCRE_LIBRARIES ${PCRE_LIBRARY})
SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
ELSE(PCRE_FOUND)
SET(PCRE_LIBRARIES)
SET(PCRE_INCLUDE_DIRS)
ENDIF(PCRE_FOUND)
MARK_AS_ADVANCED(PCRE_INCLUDE_DIRS PCRE_LIBRARIES)

View file

@ -79,7 +79,7 @@ macro (cr_find_package _PKG)
if (${_PKG_UP}_PKG_FOUND)
if (${_PKG_UP}_PKG_LIBRARY_DIRS)
link_directories(${PCRE_PKG_LIBRARY_DIRS})
link_directories(${${_PKG_UP}_PKG_LIBRARY_DIRS})
endif ()
set (${_PKG_UP}_LIBRARIES ${${_PKG_UP}_PKG_LIBRARIES})
set (${_PKG_UP}_INCLUDE_DIRS ${${_PKG_UP}_PKG_INCLUDE_DIRS})

View file

@ -97,7 +97,6 @@ cr_link_libraries(criterion rt IF HAVE_CLOCK_GETTIME)
cr_link_libraries(criterion anl IF HAVE_GETADDRINFO_A)
cr_link_libraries(criterion ws2_32 mswsock IF WIN32)
cr_link_package(criterion PCRE)
cr_link_package(criterion LIBINTL)
if (COVERALLS)

View file

@ -43,7 +43,6 @@ Shell Wildcard Pattern
----------------------
Extglob patterns in criterion are matched against a test's string identifier.
This feature is only available on \*nix systems where ``PCRE`` is provided.
In the table below, a ``pattern-list`` is a list of patterns separated by ``|``.
Any extglob pattern can be constructed by combining any of the following

View file

@ -57,11 +57,9 @@ set(SCRIPTS
list
fail_fast
help
pattern
)
if (HAVE_PCRE)
set(SCRIPTS ${SCRIPTS} pattern)
endif ()
add_custom_target(criterion_samples)
add_dependencies(criterion_tests criterion_samples)

View file

@ -69,6 +69,10 @@ set(SOURCE_FILES
src/protocol/connect.h
src/common.h
src/config.h
src/string/extglobmatch.c
src/string/extglobmatch.h
src/string/brz.c
src/string/brz.h
dependencies/nanopb/pb_common.c
dependencies/nanopb/pb_common.h
@ -84,12 +88,6 @@ if (THEORIES)
)
endif ()
if (PCRE_FOUND)
set (SOURCE_FILES ${SOURCE_FILES}
src/string/extmatch.c
src/string/extmatch.h
)
endif ()
set(INTERFACE_FILES
include/criterion/assert.h

View file

@ -2,7 +2,6 @@
# define CONFIG_H_IN_
#cmakedefine ENABLE_NLS @ENABLE_NLS@
#cmakedefine HAVE_PCRE @HAVE_PCRE@
#cmakedefine ENABLE_VALGRIND_ERRORS @ENABLE_VALGRIND_ERRORS@
#cmakedefine MINGW_DEFINE_OFF_T @MINGW_DEFINE_OFF_T@
#cmakedefine01 HAVE_STRTOK_S

View file

@ -54,9 +54,7 @@
#include "common.h"
#include "client.h"
#ifdef HAVE_PCRE
#include "string/extmatch.h"
#endif
#include "string/extglobmatch.h"
typedef const char *const msg_t;
@ -226,25 +224,23 @@ void run_test_child(struct criterion_test *test,
s_pipe_handle *g_worker_pipe;
#ifdef HAVE_PCRE
void disable_unmatching(struct criterion_test_set *set) {
if (!compile_pattern(criterion_options.pattern)) {
exit(3);
}
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
if ((s->suite.data && s->suite.data->disabled) || !s->tests)
continue;
FOREACH_SET(struct criterion_test *test, s->tests) {
const char *errmsg;
int ret = extmatch(criterion_options.pattern, test->data->identifier_, &errmsg);
if (ret == -10) {
printf("pattern error: %s\n", errmsg);
exit(1);
} else if (ret < 0) {
int ret = match(test->data->identifier_);
if (ret == 0) {
test->data->disabled = true;
}
}
}
free_pattern();
}
#endif
struct criterion_test_set *criterion_initialize(void) {
init_i18n();
@ -420,11 +416,7 @@ cleanup:
int criterion_run_all_tests(struct criterion_test_set *set) {
if (criterion_options.pattern) {
#ifdef HAVE_PCRE
disable_unmatching(set);
#else
criterion_perror("Did not filter tests by pattern: Criterion was compiled without extglob support.\n");
#endif
}
set_runner_process();

700
src/string/brz.c Normal file
View file

@ -0,0 +1,700 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 George Spelvim <linux@horizon.com>
* Copyright (c) 2016 Matthias "ailu" Günzel <a1lu@arcor.de>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "brz.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define copy_glob(g) (g)->copy(g)
#define derive_glob(g, chr) (g)->derive(g,chr)
static int matches(glob const *self, char const *string);
static int is_true(glob const *self);
static int is_false(glob const *self);
static int nullable_char(glob const* self);
static int nullable_or(glob const* self);
static int nullable_seq(glob const* self);
static int nullable_first(glob const* self);
static int nullable_not(glob const* self);
static glob *derive_empty(glob const *self, char const chr);
static glob *derive_blank(glob const *self, char const chr);
static glob *derive_char(glob const *self, char const chr);
static glob *derive_cset(glob const *self, char const chr);
static glob *derive_or(glob const *self, char const chr);
static glob *derive_seq(glob const *self, char const chr);
static glob *derive_plus(glob const *self, char const chr);
static glob *derive_at(glob const *self, char const chr);
static glob *derive_not(glob const *self, char const chr);
static glob *derive_and(glob const *self, char const chr);
static glob *copy_zero(glob const *cpy);
static glob *copy_one(glob const *cpy);
static glob *copy_two(glob const *cpy);
#ifdef _DEBUG_BRZ_
#define print_glob_v(g) do{printf(#g" "); (g)->print(g),puts("");}while(0)
#define print_glob(g) do{(g)->print(g);}while(0)
static void print_empty(glob const *self);
static void print_blank(glob const *self);
static void print_char(glob const *self);
static void print_cset(glob const *self);
static void print_or(glob const *self);
static void print_and(glob const *self);
static void print_star(glob const *self);
static void print_plus(glob const *self);
static void print_at(glob const *self);
static void print_opt(glob const *self);
static void print_seq(glob const *self);
static void print_not(glob const *self);
#endif
void free_glob(glob *tmp) {
glob* first = (glob*)tmp->first;
glob* second = (glob*)tmp->second;
if (tmp->type != CHAR && tmp->type != CHARSET && first) {
free_glob(first);
} else if (tmp->type == CHARSET) {
free(tmp->cset);
}
if (second) {
free_glob(second);
}
free(tmp);
}
static glob *new_glob() {
glob* tmp = malloc(sizeof(glob));
if (tmp) {
tmp->derive = NULL;
tmp->nullable = NULL;
tmp->first = NULL;
tmp->second = NULL;
tmp->matches = matches;
tmp->type = 0;
}
else {
fprintf(stderr, "Could not allocate glob object.\n");
exit(1);
}
return tmp;
}
static int matches(glob const *self, char const *string) {
if (strlen(string) == 0) {
return self->nullable(self);
} else {
int ret;
#ifdef _DEBUG_BRZ_
printf("\nstring: ");
puts(string);
#endif
glob *tmp = derive_glob(self, *string);
#ifdef _DEBUG_BRZ_
print_glob_v(tmp);
#endif
ret = tmp->matches(tmp,++string);
free_glob(tmp);
return ret;
}
}
/*
* Empty Set
* not nullable
* no derivative
*/
glob *glob_empty() {
glob *tmp = new_glob();
tmp->type = EMPTY;
tmp->nullable = is_false;
tmp->derive = derive_empty;
#ifdef _DEBUG_BRZ_
tmp->print = print_empty;
#endif
tmp->copy = copy_zero;
return tmp;
}
/*
* Empty string
* is nullable
* derivative is Empty set
*/
glob *glob_blank() {
glob *tmp = new_glob();
tmp->type = BLANK;;
tmp->nullable = is_true;
tmp->derive = derive_blank;
#ifdef _DEBUG_BRZ_
tmp->print = print_blank;
#endif
tmp->copy = copy_zero;
return tmp;
}
/*
* single chacacter
* nullable for ? and *
* derivative: Empty string if chr == c
* Empty set otherwise
*/
glob *glob_char(char const c) {
glob *tmp = new_glob();
tmp->type = CHAR;
tmp->nullable = nullable_char;
tmp->derive = derive_char;
tmp->c = c;
#ifdef _DEBUG_BRZ_
tmp->print = print_char;
#endif
tmp->copy = copy_zero;
return tmp;
}
/*
* single character
* not nullable
* derivative: Empty string if chr in [..] or not in [!..]
* Empty set otherwise
*/
glob *glob_cset(char *cset) {
glob *tmp = new_glob();
tmp->type = CHARSET;
tmp->nullable = is_false;
tmp->derive = derive_cset;
tmp->cset = strdup(cset);
#ifdef _DEBUG_BRZ_
tmp->print = print_cset;
#endif
tmp->copy = copy_one;
return tmp;
}
/*
* Choice: E | F
* nullable: nullable (E) | nullable(F)
* derivative: der(E) | der(F)
*
* Optimization:
* (1) E | Empty -> E
*/
glob *glob_or(glob const *first, glob const *second) {
if (first->type == EMPTY){
return copy_glob(second);
} else if (second->type == EMPTY) {
return copy_glob(first);
} else {
glob *tmp = new_glob();
tmp->type = OR;
tmp->nullable = nullable_or;
tmp->derive = derive_or;
tmp->first = copy_glob(first);
tmp->second = copy_glob(second);
#ifdef _DEBUG_BRZ_
tmp->print = print_or;
#endif
tmp->copy = copy_two;
return tmp;
}
}
/*
* Intersection: E & F
* nullable: nullable (E) & nullable(F)
* derivative: der(E) & der(F)
*
* Optimization:
* (1) E & Empty -> Empty
*/
glob *glob_and(glob const *first, glob const *second) {
if (first->type == EMPTY || second->type == EMPTY) {
return glob_empty();
} else {
glob *tmp = new_glob();
tmp->type = AND;
tmp->nullable = nullable_seq;
tmp->derive = derive_and;
tmp->first = copy_glob(first);
tmp->second = copy_glob(second);
#ifdef _DEBUG_BRZ_
tmp->print = print_and;
#endif
tmp->copy = copy_two;
return tmp;
}
}
glob *glob_star(glob const *pattern) {
glob *tmp = new_glob();
tmp->type = STAR;
tmp->nullable = is_true;
tmp->derive = derive_plus;
tmp->first = copy_glob(pattern);
#ifdef _DEBUG_BRZ_
tmp->print = print_star;
#endif
tmp->copy = copy_one;
return tmp;
}
/*
* Repetition one or more
* +(pat)
* nullable: nullable(pat)
* derivative: der(pat) Star(pat)
*
*/
glob *glob_plus(glob const *pattern) {
glob *tmp = glob_star(pattern);
tmp->type = PLUS;
tmp->nullable = nullable_first;
#ifdef _DEBUG_BRZ_
tmp->print = print_plus;
#endif
return tmp;
}
/*
* Repetition one time
* @(pat)
* nullable: nullable(pat)
* derivative: der(pat)
*
*/
glob *glob_at(glob const *pattern) {
if (pattern->type == EMPTY) {
return glob_empty();
} else if (pattern->type == BLANK) {
return glob_blank();
} else {
glob *tmp = glob_opt(pattern);
tmp->type = AT;
tmp->nullable = nullable_first;
#ifdef _DEBUG_BRZ_
tmp->print = print_at;
#endif
return tmp;
}
}
/*
* Repetition one or zero
* ?(pat)
* nullable: true
* derivative: der(pat)
*
*/
glob *glob_opt(glob const *pattern) {
glob *tmp = new_glob();
tmp->type = OPT;
tmp->nullable = is_true;
tmp->derive = derive_at;
tmp->first = copy_glob(pattern);
#ifdef _DEBUG_BRZ_
tmp->print = print_opt;
#endif
tmp->copy = copy_one;
return tmp;
}
/*
* Not the pattern
* !(pat)
* nullable: not nullable(pat)
* derivative: not der(pat)
*
*/
glob *glob_not(glob const *pattern) {
glob *tmp = new_glob();
tmp->type = NOT;
tmp->nullable = nullable_not;
tmp->derive = derive_not;
tmp->first = copy_glob(pattern);
#ifdef _DEBUG_BRZ_
tmp->print = print_not;
#endif
tmp->copy = copy_one;
return tmp;
}
/*
* Sequence E F
* nullable: nullable(E) nullable(F)
* derivative: nullable(E): Or( Seq(der(E), F), der(F) )
* else: Seq( der(E), F )
* Optimization:
* (1) Empty E -> Empty
* (2) E Empty -> Empty
* (3) Blank E -> E
*/
glob *glob_seq(glob const *first, glob const *second) {
if (first->type == EMPTY || second->type == EMPTY) {
return glob_empty();
}else if(first->type == BLANK) {
return copy_glob(second);
} else {
glob *tmp = new_glob();
tmp->type = SEQ;
tmp->nullable = nullable_seq;
tmp->derive = derive_seq;
tmp->first = copy_glob(first);
tmp->second = copy_glob(second);
#ifdef _DEBUG_BRZ_
tmp->print = print_seq;
#endif
tmp->copy = copy_two;
return tmp;
}
}
static int is_false(glob const *self) {
(void)(self);
return 0;
}
static int is_true(glob const *self) {
(void)(self);
return 1;
}
static int nullable_char(glob const* self) {
if (self->c == '?' || self->c == '*') {
return 1;
} else {
return 0;
}
}
static int nullable_or(glob const* self) {
glob const *first, *second;
first = self->first;
second = self->second;
return first->nullable(first) || second->nullable(second);
}
static int nullable_seq(glob const* self) {
glob const *first, *second;
first = self->first;
second = self->second;
return first->nullable(first) && second->nullable(second);
}
static int nullable_first(glob const* self) {
glob const *first;
first = self->first;
return first->nullable(first);
}
static int nullable_not(glob const* self) {
return !nullable_first(self);
}
static glob *derive_empty(glob const *self, char const chr) {
(void)(chr);
(void)(self);
return glob_empty();
}
static glob *derive_blank(glob const *self, char const chr) {
(void)(self);
(void)(chr);
return glob_empty();
}
static glob *derive_char(glob const *self, char const chr)
{
if (self->c == chr || self->c == '?') {
return glob_blank();
} else if (self->c == '*') {
glob *ret = self->copy(self);
return ret;
}
return glob_empty();
}
static glob *derive_cset(glob const *self, char const chr) {
/* Character class */
char *pat = self->cset;
int match = 0, inverted = (*pat == '!');
char const *class = pat + inverted;
unsigned char a = *class++;
/*
* Iterate over each span in the character class.
* A span is either a single character a, or a
* range a-b. The first span may begin with ']'.
*/
do {
unsigned char b = a;
if (a == '\0') /* Malformed */
return glob_empty();
if (class[0] == '-' && class[1] != ']') {
b = class[1];
if (b == '\0') /* malformed */
return glob_empty();
class += 2;
}
if (a > b) {
unsigned char tmp = a;
a = b;
b = tmp;
}
match |= (a <= chr && chr <= b);
} while ((a = *class++) && !match);
if (match == inverted)
return glob_empty();
else
return glob_blank();
}
static glob *derive_and(glob const *self, char const chr) {
glob const *first, *second;
first = self->first;
second = self->second;
glob *d1 = derive_glob(first, chr);
glob *d2 = derive_glob(second, chr);
glob* ret;
ret = glob_and(d1, d2);
free_glob(d1);
free_glob(d2);
return ret;
}
static glob *derive_or(glob const *self, char const chr) {
glob const *first, *second;
first = self->first;
second = self->second;
glob *d1 = derive_glob(first, chr);
glob *d2 = derive_glob(second, chr);
glob* ret = glob_or(d1, d2);
free_glob(d1);
free_glob(d2);
return ret;
}
static glob *derive_plus(glob const *self, char const chr) {
glob *ret;
glob const *pat;
pat = self->pattern;
glob *d = derive_glob(pat, chr);
glob *s = glob_star(pat);
ret = glob_seq(d, s);
free_glob(s);
free_glob(d);
return ret;
}
static glob *derive_seq(glob const *self, char const chr) {
glob *ret;
glob const *first, *second;
first = self->first;
second = self->second;
if (first->nullable(first)) { // A nullable -> Or( Seq(d1, sec), d2)
glob *d1 = derive_glob(first, chr);
glob *d2 = derive_glob(second, chr);
glob *s = glob_seq(d1, second);
ret = glob_or(s, d2);
free_glob(s);
free_glob(d1);
free_glob(d2);
} else { // A not nullable
glob *derive = derive_glob(first, chr);
ret = glob_seq(derive, second);
free_glob(derive);
}
return ret;
}
static glob *derive_at(glob const *self, char const chr) {
glob const *pat;
pat = self->pattern;
glob *d = derive_glob(pat, chr);
glob *ret;
ret = glob_at(d);
free_glob(d);
return ret;
}
static glob *derive_not(glob const *self, char const chr) {
glob const *pat;
pat = self->pattern;
glob *d = derive_glob(pat, chr);
glob *ret = glob_not(d);
free_glob(d);
return ret;
}
static glob *copy_zero(glob const *cpy) {
glob *tmp = new_glob();
*tmp = *cpy;
return tmp;
}
static glob *copy_one(glob const *cpy) {
glob const *first = cpy->first;
glob *tmp = new_glob();
*tmp = *cpy;
if (cpy->type != CHARSET) {
tmp->first = copy_glob(first);
} else {
tmp->cset = strdup(cpy->cset);
}
return tmp;
}
static glob *copy_two(glob const *cpy)
{
glob const *first = cpy->first;
glob const *sec = cpy->second;
glob *tmp = new_glob();
*tmp = *cpy;
tmp->first = copy_glob(first);
tmp->second = copy_glob(sec);
return tmp;
}
#ifdef _DEBUG_BRZ_
static void print_empty(glob const *self)
{
(void)(self);
printf("Empty");
}
static void print_blank(glob const *self)
{
(void)(self);
printf("Blank");
}
static void print_char(glob const *self)
{
printf("%c",self->c);
}
static void print_cset(glob const *self)
{
printf("[%s]",self->cset);
}
static void print_or(glob const *self)
{
glob const *first = self->first;
glob const *sec = self->second;
printf("(");
print_glob(first);
printf(" | ");
print_glob(sec);
printf(")");
}
static void print_and(glob const *self)
{
glob const *first = self->first;
glob const *sec = self->second;
printf("(");
print_glob(first);
printf(" & ");
print_glob(sec);
printf(")");
}
static void print_seq(glob const *self)
{
glob const *first = self->first;
glob const *sec = self->second;
print_glob(first);
printf(" . ");
print_glob(sec);
}
static void print_star(glob const *self)
{
glob const *first = self->first;
printf("*(");
print_glob(first);
printf(")");
}
static void print_plus(glob const *self)
{
glob const *first = self->first;
printf("+(");
print_glob(first);
printf(")");
}
static void print_at(glob const *self)
{
glob const *first = self->first;
printf("@(");
print_glob(first);
printf(")");
}
static void print_opt(glob const *self)
{
glob const *first = self->first;
printf("?(");
print_glob(first);
printf(")");
}
static void print_not(glob const *self)
{
glob const *first = self->first;
printf("!(");
print_glob(first);
printf(")");
}
#endif

68
src/string/brz.h Normal file
View file

@ -0,0 +1,68 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Matthias "ailu" Günzel <a1lu@arcor.de>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
struct stc_glob;
typedef int (*match_func)(struct stc_glob const *self, char const *string);
typedef struct stc_glob *(*derive_func)(struct stc_glob const *self, char const chr);
typedef int (*nullable_func)(struct stc_glob const *self);
typedef struct stc_glob *(*copy_func)(struct stc_glob const *cpy);
typedef void (*printFunc)(struct stc_glob const *self);
typedef enum
{EMPTY = 0, BLANK, CHAR, CHARSET, OR, AND, STAR, PLUS, AT, OPT, SEQ, NOT}glob_type;
typedef struct stc_glob {
glob_type type;
match_func matches;
derive_func derive;
nullable_func nullable;
copy_func copy;
#ifdef _DEBUG_BRZ_
printFunc print;
#endif
union {
char c;
char *cset;
struct stc_glob const *first;
struct stc_glob const *pattern;
};
union {
struct stc_glob const *second;
};
}glob;
void free_glob(glob *tmp);
glob *glob_empty();
glob *glob_blank();
glob *glob_char(char c);
glob *glob_cset(char* c);
glob *glob_or(glob const *first, glob const *second);
glob *glob_and(glob const *first, glob const *second);
glob *glob_star(glob const *pattern);
glob *glob_plus(glob const *pattern);
glob *glob_at(glob const *pattern);
glob *glob_opt(glob const *pattern);
glob *glob_not(glob const *pattern);
glob *glob_seq(glob const *first, glob const *second);

268
src/string/extglobmatch.c Normal file
View file

@ -0,0 +1,268 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2007 Russ Cox.
* Copyright (c) 2016 Matthias "ailu" Günzel <a1lu@arcor.de>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "extglobmatch.h"
#include "brz.h"
#define cat_one() do{ \
if (atoms > 1) { \
--atoms; \
glob *b = *--frag; \
glob *a = *--frag; \
*frag++ = glob_seq(a,b); \
free_glob(a); \
free_glob(b); \
} \
} while(0)
#define cat_all() do{ \
while (--atoms > 0) { \
glob *b = *--frag; \
glob *a = *--frag; \
*frag++ = glob_seq(a,b); \
free_glob(a); \
free_glob(b); \
} \
}while(0)
static glob* match_glob = NULL;
static void cleanup(glob **start, glob **end) {
while (start != end)
{
free_glob(*start);
++start;
}
}
static void print_error(char const c) {
fprintf(stderr, "pattern error: Unexpected character '%c'\n", c);
}
static glob* handle_operator(char op, glob const* g)
{
glob* tmp = NULL;
switch (op) {
case '!': tmp = glob_not(g);
break;
case '*': tmp = glob_star(g);
break;
case '+': tmp = glob_plus(g);
break;
case '@': tmp = glob_at(g);
break;
case '?': tmp = glob_opt(g);
break;
}
assert(tmp);
return tmp;
}
#define push_frag(f) *frag++ = f
#define pop_frag() *--frag
#define ARRAY_SIZE 100
static glob *compile(char const *pat) {
glob *frags[ARRAY_SIZE]={0};
glob **frag=frags;
struct paren {
int alter;
int atoms;
char op;
} paren[ARRAY_SIZE], *pa;
int alter = 0;
int atoms = 0;
pa = paren;
assert(pat);
for (;;) {
unsigned char p = *pat++;
if(!p)
break;
switch(p) {
case '!':
case '?':
case '@':
case '*':
case '+':
if (*pat != '(')
goto character;
if (pa >= paren + ARRAY_SIZE) {
fprintf(stderr,"pattern error: to many nested globs.\n");
goto error;
}
cat_one();
*pa = (struct paren) {
.alter = alter,
.atoms = atoms,
.op = p
};
pa++;
alter = 0;
atoms = 0;
p = *pat++;
break;
case '(':
print_error(p);
goto error;
case ')': {
glob *tmp, *a;
if (pa == paren || !atoms) {
print_error(p);
goto error;
}
--pa;
cat_all();
a = pop_frag();
tmp = handle_operator(pa->op, a);
free_glob(a);
push_frag(tmp);
for (; alter > 0; --alter) {
glob *b = pop_frag();
glob *a = pop_frag();
if (pa->op == '!')
push_frag(glob_and(a, b));
else
push_frag(glob_or(a, b));
--atoms;
free_glob(a);
free_glob(b);
}
atoms = pa->atoms;
alter = pa->alter;
atoms++;
}
break;
case '[': {
char *strPat;
char const *end;
cat_one();
end = strchr(pat, ']');
if (!end) {
fprintf(stderr,"pattern error: Mismatching brackets.\n");
goto error;
}
strPat = strdup(pat);
strPat[end-pat]='\0';
push_frag(glob_cset(strPat));
++atoms;
free(strPat);
pat = end + 1;
break;
}
case '|': {
glob *a;
glob *tmp;
char op;
if (atoms == 0 || pa == paren) {
print_error(p);
goto error;
}
cat_all();
a = pop_frag();
op = (pa-1)->op;
tmp = handle_operator(op, a);
free_glob(a);
push_frag(tmp);
++alter;
}
break;
case '\\':
p = *pat++;
/* FALLTHROUGH */
default: /* literal character */
character: cat_one();
push_frag(glob_char(p));
++atoms;
}
}
if (pa != paren) {
fprintf(stderr,"pattern error: Mismatching parenthesis\n");
goto error;
}
cat_all();
return frags[0];
error:
cleanup(frags, frag);
return NULL;
}
int compile_pattern(char const* pattern){
match_glob = compile(pattern);
return match_glob ? 1 : 0;
}
int match(char const* string){
return match_glob->matches(match_glob, string);
}
void free_pattern(){
free_glob(match_glob);
}
#ifdef _DEBUG_BRZ_
void print_pattern() {
match_glob->print(match_glob);
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("usage: %s \"pattern\" \"string\"\n",argv[0]);
return 1;
}
printf("argv: %s\n", argv[1]);
if(compile_pattern(argv[1]))
{
print_pattern();
puts("");
puts("");
printf("matched: %d\n", match(argv[2]));
free_pattern();
return 0;
}
return 1;
}
#endif

View file

@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
* Copyright © 2015-2016 Franklin "Snaipe" Mathieu <http://snai.pe/>
* Copyright © 2016 Matthias "ailu" Günzel <a1lu@arcor.de>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -21,9 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef EXTMATCH_H_
# define EXTMATCH_H_
#ifndef EXTGLOBMATCH_H
#define EXTGLOBMATCH_H
int extmatch(const char *pattern, const char *string, const char **errmsg);
int compile_pattern(char const* pattern);
int match(char const* string);
void free_pattern();
#endif /* !EXTMATCH_H_ */
#endif /* end of include guard: EXTGLOBMATCH_H */

View file

@ -1,285 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright © 2015-2016 Franklin "Snaipe" Mathieu <http://snai.pe/>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stddef.h>
#include <string.h>
#include <pcre.h>
#include <setjmp.h>
#include <stdio.h>
#include "criterion/internal/common.h"
#include "common.h"
struct context {
int depth;
char *dst;
const char *src;
char old, cur;
int eos;
const char **errmsg;
jmp_buf *jmp;
};
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,
.errmsg = ctx->errmsg,
.jmp = ctx->jmp,
};
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(CR_UNUSED struct context *ctx) { return 1; }
static int inactive(CR_UNUSED struct context *ctx) { 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 (DEF(strs[0].validator, inactive)(ctx))
copy_str(ctx, strs[0].str);
dup_char(ctx);
if (DEF(strs[1].validator, inactive)(ctx))
copy_str(ctx, strs[1].str);
transform_rec(ctx);
if (DEF(strs[2].validator, inactive)(ctx))
copy_str(ctx,strs[2].str);
copy_char(ctx, ')');
if (DEF(strs[3].validator, inactive)(ctx))
copy_str(ctx, strs[3].str);
} else if (DEF(strs[4].validator, inactive)(ctx)) {
copy_str(ctx, strs[4].str);
}
}
# define Handler(Name, ...) \
static void Name(struct context *ctx, CR_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, CR_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, CR_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,
[255] = NULL,
};
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 ? handler : copy_char)(ctx, c);
if (ctx->eos)
return;
}
if (ctx->depth > 0) {
*ctx->errmsg = "mismatching parenthesis";
longjmp(*ctx->jmp, -1); // abort operation
}
}
static int transform(const char *pattern, char *result, const char **errmsg) {
jmp_buf jmp;
struct context ctx = {
.src = pattern,
.dst = result,
.errmsg = errmsg,
.jmp = &jmp,
};
if (!setjmp(*ctx.jmp)) {
copy_char(&ctx, '^');
transform_impl(&ctx);
copy_char(&ctx, '$');
copy_char(&ctx, '\0');
return 0;
}
return -1;
}
/*
* let T be the transformation function,
* let diff be the function that yields the greatest character difference
* before and after its parameter has been transformed by T.
*
* T('*') = '.*'; diff('*') = 1
* T('!()') = '(?!).*' or '(?!$).*'; diff('!()') = 4
* T('@') = '' or '@'; diff('@') = 0
* T('|') = '|' or '\|'; diff('|') = 1
* T('.') = '\.'; diff('.') = 1
* T('(') = '\('; diff('(') = 1
* T(')') = '\)'; diff(')') = 1
* for every other 1 character string s, we have T(s) = s; hence diff(s) = 0
*
* let num be the function that yields the number of occurences of a string.
* let spec be the set {(s, num(s)) | s}
* s, length(T(s)) = length(s) + Σ((c_i, n_i) spec, n_i * diff(c_i))
*
* let S = {'*', '!()', '|', '.', '(', ')'}.
* since s S, diff(s) = 0, we can simplify the above equation as:
*
* s, length(T(s)) = length(s) + num('*') + num('|') + num('.')
* + num('(') + num(')') + 4 * num('!()').
*
* We must now find the maximal length L such as s, L >= length(T(s))
*
* It is immediately apparent that the largest string will depend on the number
* of occurrences of '!()'. Hence, s, let u(s) be a string that is a repeating
* sequence of '!()' padded by at most two '.', such as length(u(s)) == length(s),
*
* let N = floor(length(u(s)) / 3),
* let Q = length(u(s)) mod 3,
* hence num('!()') = N.
*
* s | length(s) = length(u(s)),
* length(T(s)) <= length(T(u(s)))
* <= length(u(s)) | the original length
* + 4 * N | the expansion of all '!()'
* + Q * diff('.') | the expansion of Q '.'
* <= 3 * N + Q + 4 * N + Q
* <= 7 * N + 4
* <= 7 * floor(length(u(s)) / 3) + 4
* <= 7 * floor(length(s) / 3) + 4
*
*/
static inline size_t max_length(size_t len) {
return 7 * len / 3 + 4;
}
int extmatch(const char *pattern, const char *string, const char **errmsg) {
char regex[max_length(strlen(pattern)) + 1];
if (transform(pattern, regex, errmsg) != -1) {
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;
}
}
return -10;
}

View file

@ -45,7 +45,6 @@ if (NOT MSVC) # we disable the scripted tests when building with MSVC
COMMAND "${CMAKE_COMMAND}"
-DPROJECT_BINARY_DIR="${PROJECT_BINARY_DIR}"
-DCRAM_PATH="${CMAKE_CURRENT_SOURCE_DIR}/cram"
-DENABLE_PATTERN_TESTS="${HAVE_PCRE}"
-P "${CMAKE_MODULE_PATH}/Cram.cmake"
)

View file

@ -1,10 +1,4 @@
Only run on linux
$ if [ "$CRITERION_TESTS_PATTERN" = "off" ]; then
> exit 80
> fi
Selecting misc::passing
Testing normal globs
$ simple.c.bin --pattern '*/passing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
@ -14,7 +8,82 @@ Selecting misc::passing
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Selecting misc::failing
$ simple.c.bin --pattern '*/pa?sing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/passing?' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/*' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::failing (esc)
[\x1b[0;34m----\x1b[0m] \x1b[0;1msimple.c\x1b[0m:\x1b[0;31m4\x1b[0m: Assertion failed: The expression 0 is false. (esc)
[\x1b[0;31mFAIL\x1b[0m] misc::failing (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m2\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0;31m1\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Testing character classes
$ simple.c.bin --pattern 'misc/[pf]assing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/[p-s]assing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/[!f]ailing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::passing: Test is disabled (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m0\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/[p-f]ailing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::failing (esc)
[\x1b[0;34m----\x1b[0m] \x1b[0;1msimple.c\x1b[0m:\x1b[0;31m4\x1b[0m: Assertion failed: The expression 0 is false. (esc)
[\x1b[0;31mFAIL\x1b[0m] misc::failing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::passing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0;31m1\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/[!azerty]assing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Testing extended globs
$ simple.c.bin --pattern '!(*/passing)' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
@ -25,7 +94,23 @@ Selecting misc::failing
[\x1b[0;33mSKIP\x1b[0m] misc::passing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0;31m1\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Selecting both misc::passing and misc::failing
$ simple.c.bin --pattern '@(misc)/passing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern '*(misc)/pa+(s)ing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/[pf]a@(ss|il)ing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
@ -37,17 +122,57 @@ Selecting both misc::passing and misc::failing
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m2\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0;31m1\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern '!(*/failing|*/fail)' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/?(passing)' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/?(passing|failing)' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::failing (esc)
[\x1b[0;34m----\x1b[0m] \x1b[0;1msimple.c\x1b[0m:\x1b[0;31m4\x1b[0m: Assertion failed: The expression 0 is false. (esc)
[\x1b[0;31mFAIL\x1b[0m] misc::failing (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m2\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0;31m1\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
$ simple.c.bin --pattern 'misc/*!(passing)' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::failing (esc)
[\x1b[0;34m----\x1b[0m] \x1b[0;1msimple.c\x1b[0m:\x1b[0;31m4\x1b[0m: Assertion failed: The expression 0 is false. (esc)
[\x1b[0;31mFAIL\x1b[0m] misc::failing (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
[\x1b[0;32mPASS\x1b[0m] misc::passing (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m2\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0;31m1\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Testing nested extglob patterns
$ simple.c.bin --pattern '@(+(nest)ed))' --verbose
$ simple.c.bin --pattern '@(+(nest)ed)' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::passing: Test is disabled (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m0\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Testing one or more
$ simple.c.bin --pattern '?(*(a|b))' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
@ -55,15 +180,7 @@ Testing one or more
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m0\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Testing malformed pattern
$ simple.c.bin --pattern '?(malformed' --verbose
pattern error: mismatching parenthesis
[1]
Testing range negation
$ simple.c.bin --pattern 'misc/[!azerty]assing' --verbose
$ simple.c.bin --pattern 'misc/pa@(s|*(s))ing' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;34mRUN \x1b[0m] misc::passing (esc)
@ -71,20 +188,47 @@ Testing range negation
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m1\x1b[0;1m | Passing: \x1b[0;32m1\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
Testing unparenthesized pipe
Testing malformed pattern
$ simple.c.bin --pattern '?(malformed' --verbose
pattern error: Mismatching parenthesis
[3]
$ simple.c.bin --pattern '(malformed' --verbose
pattern error: Unexpected character '('
[3]
$ simple.c.bin --pattern 'misc/passing)' --verbose
pattern error: Unexpected character ')'
[3]
$ simple.c.bin --pattern 'misc/[' --verbose
pattern error: Mismatching brackets.
[3]
$ simple.c.bin --pattern 'misc/[a-' --verbose
pattern error: Mismatching brackets.
[3]
$ simple.c.bin --pattern '|pipe' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::passing: Test is disabled (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m0\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)
pattern error: Unexpected character '|'
[3]
Testing special char escaping
$ simple.c.bin --pattern '\!(escaped' --verbose
$ simple.c.bin --pattern '\!(escaped)' --verbose
pattern error: Unexpected character '('
[3]
$ simple.c.bin --pattern '\!escaped' --verbose
[\x1b[0;34m----\x1b[0m] Criterion v2.2.1 (esc)
[\x1b[0;34m====\x1b[0m] Running \x1b[0;34m2\x1b[0m tests from \x1b[0;33mmisc\x1b[0m: (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::passing: Test is disabled (esc)
[\x1b[0;33mSKIP\x1b[0m] misc::failing: Test is disabled (esc)
[\x1b[0;34m====\x1b[0m] \x1b[0;1mSynthesis: Tested: \x1b[0;34m0\x1b[0;1m | Passing: \x1b[0;32m0\x1b[0;1m | Failing: \x1b[0m0\x1b[0;1m | Crashing: \x1b[0m0\x1b[0;1m \x1b[0m (esc)