From 98f4578378d54a589d651b1919fcb328a03a55ce Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Sat, 29 Dec 2012 19:21:13 +0200 Subject: [PATCH] usual/fnmatch: fnmatch compat --- Makefile | 1 + doc/mainpage.dox | 1 + m4/usual.m4 | 3 +- test/Makefile | 2 +- test/force_compat.sed | 1 + test/test_common.c | 1 + test/test_common.h | 1 + test/test_fnmatch.c | 215 ++++++++++++++++++++++++++++++++++ usual/fnmatch.c | 267 ++++++++++++++++++++++++++++++++++++++++++ usual/fnmatch.h | 62 ++++++++++ 10 files changed, 552 insertions(+), 2 deletions(-) create mode 100644 test/test_fnmatch.c create mode 100644 usual/fnmatch.c create mode 100644 usual/fnmatch.h diff --git a/Makefile b/Makefile index bb342cf..83e2993 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,7 @@ libusual_la_SOURCES = usual/config.h.in \ usual/err.h usual/err.c \ usual/event.h usual/event.c \ usual/fileutil.h usual/fileutil.c \ + usual/fnmatch.h usual/fnmatch.c \ usual/getopt.h usual/getopt.c \ usual/hashing/crc32.h usual/hashing/crc32.c \ usual/hashing/lookup3.h usual/hashing/lookup3.c \ diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 31ac12b..b106d6d 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -41,6 +41,7 @@ * Socket compat and helper functions * String compat and helper functions * Time compat and helper functions + * fnmatch compat * Data Structures * Binary Tree * Crit-Bit Tree diff --git a/m4/usual.m4 b/m4/usual.m4 index ada4827..734580b 100644 --- a/m4/usual.m4 +++ b/m4/usual.m4 @@ -170,7 +170,7 @@ AC_CHECK_HEADERS([arpa/inet.h netinet/in.h netinet/tcp.h]) AC_CHECK_HEADERS([sys/param.h sys/uio.h pwd.h grp.h]) AC_CHECK_HEADERS([sys/wait.h sys/mman.h syslog.h netdb.h dlfcn.h]) AC_CHECK_HEADERS([err.h pthread.h endian.h sys/endian.h byteswap.h]) -AC_CHECK_HEADERS([malloc.h regex.h getopt.h]) +AC_CHECK_HEADERS([malloc.h regex.h getopt.h fnmatch.h]) dnl ucred.h may have prereqs AC_CHECK_HEADERS([ucred.h sys/ucred.h], [], [], [ #ifdef HAVE_SYS_TYPES_H @@ -231,6 +231,7 @@ AC_CHECK_FUNCS(err errx warn warnx getprogname setprogname) AC_CHECK_FUNCS(posix_memalign memalign valloc) AC_CHECK_FUNCS(getopt getopt_long getopt_long_only) AC_CHECK_FUNCS(fls flsl flsll ffs ffsl ffsll) +AC_CHECK_FUNCS(fnmatch) ### Functions provided only on win32 AC_CHECK_FUNCS(localtime_r gettimeofday recvmsg sendmsg usleep getrusage) ### Functions used by libusual itself diff --git a/test/Makefile b/test/Makefile index 9f36289..c232ec7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -13,7 +13,7 @@ regtest_system_SOURCES = \ test_cxalloc.c test_bits.c test_base.c test_netdb.c \ test_cfparser.c test_endian.c test_hashtab.c test_mdict.c \ test_shlist.c test_time.c test_hashing.c test_fileutil.c \ - test_socket.c test_getopt.c test_ctype.c \ + test_socket.c test_getopt.c test_ctype.c test_fnmatch.c \ test_common.h tinytest.h tinytest_macros.h regtest_compat_SOURCES := $(regtest_system_SOURCES) diff --git a/test/force_compat.sed b/test/force_compat.sed index 6ae3313..6b43849 100644 --- a/test/force_compat.sed +++ b/test/force_compat.sed @@ -10,3 +10,4 @@ /INET_PTON/s,^,//, /GETOPT/s,^,//, /CTYPE_ON_CHAR/s,^,//, +/FNMATCH/s,^,//, diff --git a/test/test_common.c b/test/test_common.c index bfbbd94..5c407cc 100644 --- a/test/test_common.c +++ b/test/test_common.c @@ -12,6 +12,7 @@ struct testgroup_t groups[] = { { "hashing/", hashing_tests }, { "endian/", endian_tests }, { "string/", string_tests }, + { "fnmatch/", fnmatch_tests }, { "ctype/", ctype_tests }, { "heap/", heap_tests }, { "hashtab/", hashtab_tests }, diff --git a/test/test_common.h b/test/test_common.h index 2c5ff45..375fa71 100644 --- a/test/test_common.h +++ b/test/test_common.h @@ -33,3 +33,4 @@ extern struct testcase_t fileutil_tests[]; extern struct testcase_t socket_tests[]; extern struct testcase_t getopt_tests[]; extern struct testcase_t ctype_tests[]; +extern struct testcase_t fnmatch_tests[]; diff --git a/test/test_fnmatch.c b/test/test_fnmatch.c new file mode 100644 index 0000000..25d26dc --- /dev/null +++ b/test/test_fnmatch.c @@ -0,0 +1,215 @@ + +#include + +#include +#include "test_common.h" + +/* + * POSIX syntax. + */ + +static void test_fnmatch_posix(void *p) +{ + /* literal */ + int_check(0, fnmatch("", "", 0)); + int_check(0, fnmatch("a", "a", 0)); + int_check(0, fnmatch("abc", "abc", 0)); + int_check(1, fnmatch("", "b", 0)); + int_check(1, fnmatch("a", "", 0)); + int_check(1, fnmatch("a", "b", 0)); + + /* single wildcard */ + int_check(0, fnmatch("a?", "ax", 0)); + int_check(0, fnmatch("??", "ax", 0)); + int_check(1, fnmatch("?", "ax", 0)); + int_check(1, fnmatch("???", "ax", 0)); + + /* wildcard */ + int_check(0, fnmatch("a*", "ax", 0)); + int_check(0, fnmatch("*", "", 0)); + int_check(0, fnmatch("*", "qwe", 0)); + int_check(0, fnmatch("ab*ab", "abxxab", 0)); + int_check(1, fnmatch("ab*ab", "abxxabc", 0)); + + /* wildcard+ */ + int_check(0, fnmatch("ab*ab*", "abxxabc", 0)); + int_check(0, fnmatch("ab*ab*c", "abxxabc", 0)); + int_check(0, fnmatch("ab*ab*c", "abxxabxc", 0)); + int_check(0, fnmatch("??*??", "abxxab", 0)); + int_check(0, fnmatch("*??", "abxxab", 0)); + int_check(0, fnmatch("??*", "abxxab", 0)); + int_check(0, fnmatch("a**c", "abc", 0)); + + /* classes */ + int_check(0, fnmatch("[abc]", "b", 0)); + int_check(1, fnmatch("[abc]", "x", 0)); + int_check(0, fnmatch("[a-c]", "b", 0)); + int_check(1, fnmatch("[a-c]", "x", 0)); + int_check(0, fnmatch("[b-b]", "b", 0)); + int_check(1, fnmatch("[!abc]", "b", 0)); + int_check(1, fnmatch("[!a-c]", "b", 0)); + int_check(0, fnmatch("[!a-c]", "x", 0)); + int_check(0, fnmatch("[*?[][*?[][*?[]", "*?[", 0)); + int_check(0, fnmatch("[[:alpha:]][![:alpha:]]", "a9", 0)); + int_check(0, fnmatch("[[:alnum:]][![:alnum:]]", "9-", 0)); + int_check(0, fnmatch("[[:blank:]][![:blank:]]", " -", 0)); + int_check(0, fnmatch("[[:cntrl:]][![:cntrl:]]", "\tx", 0)); + int_check(0, fnmatch("[[:digit:]][![:digit:]]", "9a", 0)); + int_check(0, fnmatch("[[:graph:]][![:graph:]]", "a\t", 0)); + int_check(0, fnmatch("[[:lower:]][![:lower:]]", "aA", 0)); + int_check(0, fnmatch("[[:print:]][![:print:]]", "a\t", 0)); + int_check(0, fnmatch("[[:punct:]][![:punct:]]", ".x", 0)); + int_check(0, fnmatch("[[:space:]][![:space:]]", " x", 0)); + int_check(0, fnmatch("[[:upper:]][![:upper:]]", "Ff", 0)); + int_check(0, fnmatch("[[:xdigit:]][![:xdigit:]]", "Fx", 0)); + int_check(0, fnmatch("[", "[", 0)); + int_check(0, fnmatch("[f", "[f", 0)); + + /* escaping */ + int_check(1, fnmatch("\\a\\?", "ax", 0)); + int_check(0, fnmatch("\\a\\?", "a?", 0)); + int_check(1, fnmatch("\\a\\*", "ax", 0)); + int_check(0, fnmatch("\\a\\*", "a*", 0)); + int_check(0, fnmatch("\\[a]", "[a]", 0)); + int_check(0, fnmatch("\\\\", "\\", 0)); + int_check(0, fnmatch("\\$\\'\\\"\\<\\>", "$'\"<>", 0)); + int_check(1, fnmatch("a\\", "a", 0)); + int_check(1, fnmatch("a\\", "a\\", 0)); + int_check(0, fnmatch("a\\", "a\\", FNM_NOESCAPE)); + int_check(0, fnmatch("\\[a]", "\\a", FNM_NOESCAPE)); + int_check(0, fnmatch("\\*b", "\\aab", FNM_NOESCAPE)); + + /* FNM_PATHNAME */ + int_check(0, fnmatch("ab*c", "ab/c", 0)); + int_check(1, fnmatch("ab*c", "ab/c", FNM_PATHNAME)); + int_check(1, fnmatch("ab?c", "ab/c", FNM_PATHNAME)); + int_check(1, fnmatch("ab[/]c", "ab/c", FNM_PATHNAME)); + int_check(0, fnmatch("/*/", "//", FNM_PATHNAME)); + int_check(1, fnmatch("a[b/c]d", "a/d", FNM_PATHNAME)); + int_check(0, fnmatch("abd", "abd", FNM_PATHNAME)); + int_check(1, fnmatch("a[b/c]d", "a[b/c]d", FNM_PATHNAME)); + + /* FNM_PERIOD */ + int_check(0, fnmatch(".foo", ".foo", 0)); + int_check(0, fnmatch("?foo", ".foo", 0)); + int_check(0, fnmatch("[.]foo", ".foo", 0)); + int_check(0, fnmatch("[!abc]foo", ".foo", 0)); + int_check(0, fnmatch("*foo", ".foo", 0)); + int_check(0, fnmatch(".foo", ".foo", FNM_PERIOD)); + int_check(1, fnmatch("*foo", ".foo", FNM_PERIOD)); + int_check(1, fnmatch("?foo", ".foo", FNM_PERIOD)); + int_check(0, fnmatch("*/?foo", "sub/.foo", FNM_PERIOD)); + int_check(1, fnmatch("*.foo", ".foo", FNM_PERIOD)); + int_check(1, fnmatch("[.]foo", ".foo", FNM_PERIOD)); + + /* FNM_PATHNAME | FNM_PERIOD */ + int_check(1, fnmatch("*/?foo", "sub/.foo", FNM_PERIOD|FNM_PATHNAME)); + int_check(1, fnmatch("*/[.]foo", "sub/.foo", FNM_PERIOD|FNM_PATHNAME)); + int_check(1, fnmatch("*/*.c", "sub/.foo.c", FNM_PERIOD|FNM_PATHNAME)); + int_check(1, fnmatch("*/*", "sub/.foo.c", FNM_PERIOD|FNM_PATHNAME)); + int_check(0, fnmatch("*/*.c", "sub/foo..c", FNM_PERIOD|FNM_PATHNAME)); + int_check(1, fnmatch("*/*.foo", "sub/.foo", FNM_PERIOD|FNM_PATHNAME)); + + /* escapes in brackets ~ posix */ + int_check(0, fnmatch("[A\\]]", "\\]", FNM_NOESCAPE)); + int_check(0, fnmatch("[a\\-x]", "_", FNM_NOESCAPE)); +end:; +} + +/* + * GNU syntax. + */ + +static void test_fnmatch_gnu(void *p) +{ + /* FNM_CASEFOLD */ + int_check(1, fnmatch("aaAA", "AaAa", 0)); + int_check(1, fnmatch("[b][b][B][B][a-c][A-C][a-c][A-C]", "bBbBbbBB", 0)); + int_check(0, fnmatch("aaAA", "AaAa", FNM_CASEFOLD)); + int_check(0, fnmatch("[b][b][B][B][a-c][A-C][a-c][A-C]", "bBbBbbBB", FNM_CASEFOLD)); + + /* FNM_LEADING_DIR */ + int_check(0, fnmatch("a", "a", FNM_LEADING_DIR|FNM_PATHNAME)); + int_check(0, fnmatch("a", "a/b", FNM_LEADING_DIR|FNM_PATHNAME)); + int_check(0, fnmatch("a/b", "a/b/c/d", FNM_LEADING_DIR|FNM_PATHNAME)); + int_check(0, fnmatch("a/*/*", "a/b/c/d", FNM_LEADING_DIR|FNM_PATHNAME)); + int_check(0, fnmatch("*", "/a", FNM_LEADING_DIR|FNM_PATHNAME)); + /* seems wrong to allow it */ + int_check(0, fnmatch("a", "a/b", FNM_LEADING_DIR)); + + /* escapes in brackets ~ gnu */ + int_check(0, fnmatch("[A\\]][A\\]]", "]A", 0)); + int_check(1, fnmatch("[a\\-x]", "_", 0)); + int_check(0, fnmatch("[\\!x]", "!", 0)); + int_check(1, fnmatch("[\\!x]", "\\", 0)); + int_check(0, fnmatch("[\\[:alnum:]", ":", 0)); +end:; +} + +/* + * DoS possibilities. + */ + +static void test_fnmatch_weird(void *p) +{ + char pat[4096]; + char str[4096]; + int i; + + memset(pat, 0, sizeof(pat)); + memset(str, 0, sizeof(str)); + + memset(pat, '*', 1500); + memset(str, 'a', 1500); + int_check(0, fnmatch(pat, str, 0)); + + pat[10] = 'a'; + pat[1200] = 'b'; + int_check(0, fnmatch(pat, "ab", 0)); + + for (i = 0; i < 1200; i++) { + char c = 'a' + (i%26); + pat[i*2] = c; + pat[i*2+1] = '*'; + str[i*2] = c; + str[i*2+1] = c; + } + pat[i*2] = 0; + str[i*2] = 0; + int_check(0, fnmatch(pat, str, 0)); + + for (i = 0; i < 2000; i++) { + pat[i*2] = '*'; + pat[i*2+1] = '?'; + str[i*2] = 'a'; + str[i*2+1] = 'b'; + } + str[i*2] = 0; + pat[i*2] = 0; + int_check(0, fnmatch(pat, str, 0)); + pat[i*2] = 'a'; + pat[i*2 + 1] = 0; + int_check(1, fnmatch(pat, str, 0)); + pat[i*2] = 'b'; + int_check(0, fnmatch(pat, str, 0)); + pat[i*2] = '*'; + pat[3] = 'x'; + str[2000] = 'x'; + int_check(0, fnmatch(pat, str, 0)); + + memset(pat, '?', sizeof(pat)); + memset(str, 'x', sizeof(str)); + str[4000] = 0; + pat[2000] = 0; + pat[0] = '*'; + int_check(0, fnmatch(pat, str, 0)); +end:; +} + +struct testcase_t fnmatch_tests[] = { + { "posix", test_fnmatch_posix }, + { "gnu", test_fnmatch_gnu }, + { "weird", test_fnmatch_weird }, + END_OF_TESTCASES +}; + diff --git a/usual/fnmatch.c b/usual/fnmatch.c new file mode 100644 index 0000000..8f11430 --- /dev/null +++ b/usual/fnmatch.c @@ -0,0 +1,267 @@ +/* + * fnmatch.c + * + * Copyright (c) 2012 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Differences from POSIX: + * - ^ can be used in place of ! + * - \ is escape in bracket expression, unless FNM_NOESCAPE is given. + * - FNM_CASEFOLD + * - FNM_LEADING_DIR + */ + +#include +#include + +#include + +#ifdef NEED_USUAL_FNMATCH + +/* compare chars with case folding */ +static inline bool cmp_fold(wchar_t c1, wchar_t c2, int flags) +{ + if (c1 == c2) + return true; + if (flags & FNM_CASEFOLD) { + if (iswupper(c1) && iswlower(c2)) + return c1 == (wchar_t)towupper(c2); + else if (iswlower(c1) && iswupper(c2)) + return c1 == (wchar_t)towlower(c2); + } + return false; +} + +/* compare char to range with case folding */ +static inline bool range_fold(wchar_t c, wchar_t r1, wchar_t r2, int flags) +{ + if (c >= r1 && c <= r2) + return true; + if (flags & FNM_CASEFOLD) { + /* convert only if it makes sense */ + if (iswupper(c) && iswlower(r1) && iswlower(r2)) { + c = towlower(c); + if (c >= r1 && c <= r2) + return true; + } else if (iswlower(c) && iswupper(r1) && iswupper(r2)) { + c = towupper(c); + if (c >= r1 && c <= r2) + return true; + } + } + return false; +} + +/* match bracket expression */ +static const wchar_t *match_class(const wchar_t *pat, wchar_t c, int flags) +{ + const wchar_t *p = pat; + const wchar_t *start; + bool neg = false; + bool match = false; + bool fallback_ok = true; + const wchar_t *n1, *n2; + wctype_t wct; + + /* negation */ + if (*p == '!' || *p == '^') { + neg = true; + p++; + } + start = p; +loop: + /* named class, equivalence class or collating symbol */ + if (p[0] == '[' && (p[1] == ':' || p[1] == '.' || p[1] == '=')) { + n1 = p + 2; + n2 = wcschr(n1, p[1]); + if (!n2 || n2[1] != ']') + goto parse_fail; + if (p[1] != ':') + return NULL; + p = n2 + 2; + wct = wctype_wcsn(n1, n2-n1); + if (wct == (wctype_t)0) + return NULL; + if (iswctype(c, wct)) + match = true; + fallback_ok = false; + /* skip rest */ + goto loop; + } +parse_fail: + + /* unexpected pattern end */ + if (p[0] == '\0') { + /* only open bracket exists, take it as literal */ + if (fallback_ok && c == '[') + return pat - 1; + return NULL; + } + + /* closing bracket */ + if (p[0] == ']' && p != start) + return (match ^ neg) ? p : NULL; + + /* escape next char */ + if (p[0] == '\\' && !(flags & FNM_NOESCAPE)) { + if (p[1] == '\0') + return NULL; + p++; + } + + /* its either simple range or char */ + if (p[1] == '-' && p[2] != ']' && p[2] != '\0') { + wchar_t r1 = p[0]; + wchar_t r2 = p[2]; + if (r2 == '\\' && !(flags & FNM_NOESCAPE)) { + p++; + r2 = p[2]; + if (r2 == '\0') + return NULL; + } + if (range_fold(c, r1, r2, flags)) + match = true; + p += 3; + } else { + if (cmp_fold(c, p[0], flags)) + match = true; + p++; + } + goto loop; +} + +/* + * FNM_PATHNAME disallows wildcard match for '/', + * FNM_PERIOD disallows wildcard match for leading '.', + * check for string end also. + */ +static bool disallow_wildcard(const wchar_t *s, const wchar_t *str, int flags) +{ + if (*s == '\0') + return true; + if (*s == '/') + return (flags & FNM_PATHNAME); + if (*s == '.' && (flags & FNM_PERIOD)) { + if (s == str) + return true; + if (s[-1] == '/' && (flags & FNM_PATHNAME)) + return true; + } + return false; +} + +/* + * Non-recursive fnmatch(), based on globmatch() by + */ +static int wfnmatch(const wchar_t *pat, const wchar_t *str, int flags) +{ + const wchar_t *p = pat; + const wchar_t *s = str; + const wchar_t *retry_p = NULL; + const wchar_t *skip_s = NULL; +loop: + switch (*p) { + case '*': + /* match any number of chars from this position on */ + retry_p = p + 1; + skip_s = s; + /* dot after '*' must not match leading dot */ + if (p[1] == '.' && disallow_wildcard(s, str, flags)) + return FNM_NOMATCH; + break; + case '?': + /* match any char */ + if (disallow_wildcard(s, str, flags)) + goto nomatch_retry; + s++; + break; + case '[': + /* match character class */ + if (disallow_wildcard(s, str, flags)) + goto nomatch_retry; + p = match_class(p + 1, *s, flags); + if (p == NULL) + goto nomatch_retry; + s++; + break; + case '\\': + /* escape next char */ + if (!(flags & FNM_NOESCAPE)) { + p++; + if (*p == '\0') + return FNM_NOMATCH; + } + default: + /* match single char */ + if (*s == '/' && *p == '\0' && (flags & FNM_LEADING_DIR)) + return 0; + if (!cmp_fold(*p, *s, flags)) + goto nomatch_retry; + if (*s == '\0') + return 0; + s++; + } + p++; + goto loop; + +nomatch_retry: + /* eat chars with '*', if possible */ + if (retry_p == NULL || *s == '\0') + return FNM_NOMATCH; + s = skip_s++; + p = retry_p; + if (*s == '\0') + return (*p == '\0') ? 0 : FNM_NOMATCH; + if (disallow_wildcard(s, str, flags)) + return FNM_NOMATCH; + s++; + goto loop; +} + +/* + * Convert locale-specific encoding to wchar_t string + */ +int fnmatch(const char *pat, const char *str, int flags) +{ + const wchar_t *wpat, *wstr; + wchar_t pbuf[128]; + wchar_t sbuf[128]; + int plen = strlen(pat); + int slen = strlen(str); + int res; + + /* convert encoding */ + wpat = mbstr_decode(pat, plen, NULL, pbuf, sizeof(pbuf) / sizeof(wchar_t), false); + if (!wpat) + return (errno == EILSEQ) ? FNM_NOMATCH : -1; + wstr = mbstr_decode(str, slen, NULL, sbuf, sizeof(sbuf) / sizeof(wchar_t), true); + if (!wstr) + return -1; + + /* run actual fnmatch */ + res = wfnmatch(wpat, wstr, flags); + + /* free buffers */ + if (wstr != sbuf) + free(wstr); + if (wpat != pbuf) + free(wpat); + + return res; +} + +#endif + diff --git a/usual/fnmatch.h b/usual/fnmatch.h new file mode 100644 index 0000000..7b73dc1 --- /dev/null +++ b/usual/fnmatch.h @@ -0,0 +1,62 @@ +/* + * fnmatch.h + * + * Copyright (c) 2012 Marko Kreen + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file + * Theme include for strings. + */ + +#ifndef _USUAL_FNMATCH_H_ +#define _USUAL_FNMATCH_H_ + +#include + +#ifdef HAVE_FNMATCH_H +#include +#else +#define NEED_USUAL_FNMATCH +#endif + +#ifdef NEED_USUAL_FNMATCH +#define fnmatch(p,s,f) usual_fnmatch(p,s,f) + +/** Do not allow wildcard to match '/' */ +#define FNM_PATHNAME 1 +/** Treat '\\' as literal value */ +#define FNM_NOESCAPE 2 +/** Do not allow wildcard to match leading '.' */ +#define FNM_PERIOD 4 +/** (GNU) Match case-insensitively */ +#define FNM_CASEFOLD 8 +/** (GNU) Match leading directory in path */ +#define FNM_LEADING_DIR 16 + +/* (GNU) random alias */ +#define FNM_FILE_NAME FNM_PATHNAME + +/** Returned on no match */ +#define FNM_NOMATCH 1 + +/** + * Compat: fnmatch() + */ +int fnmatch(const char *pat, const char *str, int flags); + +#endif /* NEED_USUAL_FNMATCH */ + +#endif /* !_USUAL_FNMATCH_H_ */ -- 2.39.5