# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# tests of various aspects of pathname expansion, mostly dealing with bracket
# expressions
#
# Derived from tests contributed by Koichi Murase
LC_COLLATE=C
ORIG_DIR=$PWD
: ${TMPDIR:=/tmp} ${BUILD_DIR:=$ORIG_DIR}
trap 'rm -rf $TESTDIR $WORK_DIR' EXIT
WORK_DIR=${TMPDIR}/globtest-$$
mkdir $WORK_DIR || {
echo "glob-bracket: cannot create directory $WORK_DIR" >&2
exit 1
}
cd $WORK_DIR || {
echo "glob-bracket: cannot cd to directory $WORK_DIR" >&2
exit 1
}
eval $(grep -E '^(CC |SHOBJ_).*=' $BUILD_DIR/examples/loadables/Makefile | sed -e 's/[ ]*=[ ]*/="/' -e 's/\$@/strmatch/' -e 's/$/"/' )
if [ "$SHOBJ_STATUS" != "supported" ]; then
echo "glob-bracket: shared objects not supported, cannot continue" >&2
exit 2
fi
# we assume gcc as a default here
: ${CC:=gcc}
cat > fnmatch.c <<-EOF
#include
#include
#include
int main(int argc, char **argv) {
if (2 >= argc) {
fprintf(stderr, "usage: fnmatch string pattern\n");
exit(2);
}
#ifdef FNM_EXTMATCH
int flags = FNM_PATHNAME | FNM_PERIOD | FNM_EXTMATCH;
#else
int flags = FNM_PATHNAME | FNM_PERIOD;
#endif
if (fnmatch(argv[2], argv[1], flags) == 0)
return 0;
return 1;
}
EOF
$CC -O2 -o fnmatch fnmatch.c
rm -f fnmatch.c
if [ ! -f fnmatch ] ; then
echo "glob-bracket: cannot create fnmatch executable" >&2
exit 2
fi
cat > strmatch.c <<-EOF
#define BUILTIN_ENABLED 0x01
struct word_desc { char* word; int flags; };
struct word_list { struct word_list* next; struct word_desc* word; };
struct builtin {
const char* name;
int (*function)(struct word_list*);
int flags;
const char** long_doc;
const char* short_doc;
char* handle;
};
/*#include */
int strmatch(char *pattern, char *string, int flags);
#define FNM_PATHNAME (1 << 0)
#define FNM_NOESCAPE (1 << 1)
#define FNM_PERIOD (1 << 2)
#define FNM_LEADING_DIR (1 << 3)
#define FNM_CASEFOLD (1 << 4)
#define FNM_EXTMATCH (1 << 5)
#define FNM_FIRSTCHAR (1 << 6)
#define FNM_DOTDOT (1 << 7)
static int strmatch_builtin(struct word_list* list) {
char *str, *pat;
if (!list || !list->word) return 2;
str = list->word->word;
if (!list->next || !list->next->word) return 2;
pat = list->next->word->word;
if (strmatch (pat, str, FNM_PATHNAME | FNM_PERIOD | FNM_EXTMATCH) == 0)
return 0;
return 1;
}
static const char* strmatch_doc[] = { "This is a builtin to test the behavior of strmatch", 0 };
struct builtin strmatch_struct = { "strmatch", strmatch_builtin, BUILTIN_ENABLED, strmatch_doc, "strmatch string pattern", 0, };
EOF
${SHOBJ_CC} ${SHOBJ_CFLAGS} -c -o strmatch.o strmatch.c
rm -f strmatch.c
${SHOBJ_LD} ${SHOBJ_LDFLAGS} ${SHOBJ_XLDFLAGS} -o strmatch.so strmatch.o ${SHOBJ_LIBS}
rm -f strmatch.o
if [ ! -f strmatch.so ] ; then
echo "glob-bracket: cannot create strmatch loadable builtin" >&2
exit 2
fi
enable -f ./strmatch.so strmatch || {
echo "glob-bracket: cannot load strmatch builtin" >&2
exit 2
}
check_count=1
if [ -z "$BASH_TSTOUT" ]; then
yes=$'\033[32myes\033[m' no=$'\033[31mno\033[m'
else
yes=yes no=no
fi
function check {
# bash impl
if strmatch "$2" "$1"; then
local strmatch=$yes
else
local strmatch=$no
fi
# fnmatch
local expect=${3-}
if [[ ! $expect ]]; then
if $WORK_DIR/fnmatch "$2" "$1"; then
expect=$yes
else
expect=$no
fi
fi
printf '#%d: pat=%-20s str=%-16s %s/%s\n' "$((check_count++))" "$1" "$2" "$strmatch" "$expect"
}
function pcheck {
local GLOBIGNORE=$1
# bash impl
local -a f=(*/*/efg*)
if [[ $f == '*/*/efg*' ]]; then
local strmatch=$yes
else
local strmatch=$no
fi
# Linux fnmatch
local fnmatch=${2-}
if [[ ! $fnmatch ]]; then
if $WORK_DIR/fnmatch ab/cd/efg "$1"; then
fnmatch=$yes
else
fnmatch=$no
fi
fi
printf '#%d: pat=%-16s %s/%s\n' "$((check_count++))" "$1" "$strmatch" "$fnmatch"
}
TESTDIR=${TMPDIR}/pathtest-$$
TESTPATH=${TESTDIR}/ab/cd/efg
mkdir -p $TESTPATH
if [ -d "$TESTPATH" ] && cd "$TESTDIR"; then
echo '--- $GLOBIGNORE vs fnmatch(3) ---'
pcheck 'ab/cd/efg'
pcheck 'ab[/]cd/efg'
pcheck 'ab[/a]cd/efg'
pcheck 'ab[a/]cd/efg'
pcheck 'ab[!a]cd/efg'
pcheck 'ab[.-0]cd/efg'
pcheck '*/*/efg'
pcheck '*[/]*/efg'
pcheck '*[/a]*/efg'
pcheck '*[a/]*/efg'
pcheck '*[!a]*/efg'
pcheck '*[.-0]*/efg'
pcheck '*/*/efg'
pcheck '*[b]/*/efg'
pcheck '*[ab]/*/efg'
pcheck '*[ba]/*/efg'
pcheck '*[!a]/*/efg'
pcheck '*[a-c]/*/efg'
shopt -s extglob
pcheck 'ab@(/)cd/efg' "$yes"
pcheck '*@(/)cd/efg' "$no"
pcheck '*/cd/efg'
shopt -u extglob
cd "$WORK_DIR"
fi
echo
echo '---Tests for a slash in bracket expressions---'
check 'ab[/]ef' 'ab[/]ef' "$yes"
check 'ab[/]ef' 'ab/ef' "$no"
check 'ab[c/d]ef' 'ab[c/d]ef' "$yes"
check 'ab[c/d]ef' 'abcef' "$no"
check 'ab[.-/]ef' 'ab[.-/]ef' "$yes"
check 'ab[.-/]ef' 'ab.ef' "$no"
check 'ab[[=/=]]ef' 'ab[[=/=]]ef' "$yes"
check 'ab[[=/=]]ef' 'ab/ef' "$no"
check 'ab[[=c=]/]ef' 'ab[=/]ef' "$yes"
check 'ab[[=c=]/]ef' 'abcef' "$no"
check 'ab[[:alpha:]/]ef' 'ab[:/]ef' "$yes"
check 'ab[[:alpha:]/]ef' 'abxef' "$no"
check 'ab[/[abc]]ef' 'ab[/c]ef' "$yes"
check 'ab[/[abc]]ef' 'abc]ef' "$no"
check 'ab[c[=/=]]ef' 'ab[c[=/=]]ef' "$yes"
check 'ab[c[=/=]]ef' 'abc[=/=]ef' "$no"
check 'ab[c[=/=]]ef' 'abcef' "$no"
check 'a[b\/c]' 'a[b/c]' "$yes"
check 'a[b\/c]' 'ab' "$no"
check 'a[b\/c]' 'ac' "$no"
echo
echo '---Tests for incomplete bracket expressions---'
check 'ab[c' 'ab[c' "$yes"
check 'ab[c' 'abc' "$no"
check 'ab[c[=d=' 'ab[c[=d=' "$yes"
check 'ab[c[=d=' 'abc' "$no"
check 'ab[c[.d' 'ab[c[.d' "$yes"
check 'ab[c[.d' 'abc' "$no"
check 'ab[c[:alpha:' 'ab[c[:alpha:' "$yes"
check 'ab[c[:alpha:' 'abc' "$no"
check 'ab[c-' 'ab[c-' "$yes"
check 'ab[c-' 'abc' "$no"
check 'ab[c\' 'ab[c\' "$yes"
check 'ab[c\' 'abc' "$no"
check 'ab[[\' 'ab[[\' "$yes"
check 'ab[[\' 'ab[' "$no"
echo
echo '--- PATSCAN vs BRACKMATCH ---'
check '@([[.].])A])' ']' "$yes"
check '@([[.].])A])' '==]A])' "$no"
check '@([[.].])A])' 'AA])' "$no"
check '@([[=]=])A])' ']' "$no"
check '@([[=]=])A])' '==]A])' "$yes"
check '@([[=]=])A])' 'AA])' "$no"
echo
echo '--- BRACKMATCH: after match vs before match ---'
check '[[=]=]ab]' 'a' "$no"
check '[[.[=.]ab]' 'a' "$yes"
check '[[.[==].]ab]' 'a' "$yes"
echo
check '[a[=]=]b]' 'a' "$no"
check '[a[.[=.]b]' 'a' "$yes"
check '[a[.[==].]b]' 'a' "$yes"
echo
check '[a[=]=]b]' 'b' "$no"
check '[a[=]=]b]' 'a=]b]' "$yes"
check '[a[.[=.]b]' 'b' "$yes"
check '[a[.[=.]b]' 'ab]' "$no"
check '[a[.[==].]b]' 'b' "$yes"
check '[a[.[==].]b]' 'ab]' "$no"
echo
echo '--- incomplete POSIX brackets ---'
check 'x[a[:y]' 'x[' "$yes"
check 'x[a[:y]' 'x:' "$yes"
check 'x[a[:y]' 'xy' "$yes"
check 'x[a[:y]' 'x[ay' "$no"
echo
check 'x[a[.y]' 'x[' "$yes"
check 'x[a[.y]' 'x.' "$yes"
check 'x[a[.y]' 'xy' "$yes"
check 'x[a[.y]' 'x[ay' "$no"
echo
check 'x[a[=y]' 'x[' "$yes"
check 'x[a[=y]' 'x=' "$yes"
check 'x[a[=y]' 'xy' "$yes"
check 'x[a[=y]' 'x[ay' "$no"
echo
echo '--- MISC tests ---'
check 'a\' 'a\' "$yes"
cd $ORIG_DIR
exit 0