diff --git a/Makefile.am b/Makefile.am index d9b19548..f755ce5e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1172,16 +1172,18 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/README # -# Common code for libarchive frontends (cpio, tar) +# Common code for libarchive frontends (cat, cpio, tar, unzip) # noinst_HEADERS+= \ libarchive_fe/lafe_err.h \ + libarchive_fe/lafe_fnmatch.h \ libarchive_fe/lafe_getline.h \ libarchive_fe/lafe_platform.h \ libarchive_fe/line_reader.h \ libarchive_fe/passphrase.h libarchive_fe_la_SOURCES= \ libarchive_fe/lafe_err.c \ + libarchive_fe/lafe_fnmatch.c \ libarchive_fe/lafe_getline.c \ libarchive_fe/line_reader.c \ libarchive_fe/passphrase.c diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 1fdd2bf7..9e1c72f6 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -159,6 +159,7 @@ libarchive_host_src_files := endif libarchive_fe_src_files := libarchive_fe/lafe_err.c \ + libarchive_fe/lafe_fnmatch.c \ libarchive_fe/lafe_getline.c \ libarchive_fe/line_reader.c \ libarchive_fe/passphrase.c diff --git a/libarchive_fe/lafe_fnmatch.c b/libarchive_fe/lafe_fnmatch.c new file mode 100644 index 00000000..be10fa5b --- /dev/null +++ b/libarchive_fe/lafe_fnmatch.c @@ -0,0 +1,317 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Copyright (c) 2011 The FreeBSD Foundation + * + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lafe_platform.h" + +#ifndef HAVE_FNMATCH +/* + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + */ + +/* + * Some notes on multibyte character support: + * 1. Patterns with illegal byte sequences match nothing. + * 2. Illegal byte sequences in the "string" argument are handled by treating + * them as single-byte characters with a value of the first byte of the + * sequence cast to wchar_t. + * 3. Multibyte conversion state objects (mbstate_t) are passed around and + * used for most, but not all, conversions. Further work will be required + * to support state-dependent encodings. + */ + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_WCHAR_H +#include +#endif +#ifdef HAVE_WCTYPE_H +#include +#endif + +#include "lafe_fnmatch.h" + +#define EOS '\0' + +#define RANGE_MATCH 1 +#define RANGE_NOMATCH 0 +#define RANGE_ERROR (-1) + +static int rangematch(const char *, wchar_t, int, const char **, mbstate_t *); +static int fnmatch1(const char *, const char *, const char *, int, mbstate_t, + mbstate_t); + +int +fnmatch(const char *pattern, const char *string, int flags) +{ + static const mbstate_t initial; + + return (fnmatch1(pattern, string, string, flags, initial, initial)); +} + +static int +fnmatch1(const char *pattern, const char *string, const char *stringstart, + int flags, mbstate_t patmbs, mbstate_t strmbs) +{ + const char *bt_pattern, *bt_string; + mbstate_t bt_patmbs, bt_strmbs; + const char *newp; + char c; + wchar_t pc, sc; + size_t pclen, sclen; + + bt_pattern = bt_string = NULL; + for (;;) { + pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs); + if (pclen == (size_t)-1 || pclen == (size_t)-2) + return (FNM_NOMATCH); + pattern += pclen; + sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs); + if (sclen == (size_t)-1 || sclen == (size_t)-2) { + sc = (unsigned char)*string; + sclen = 1; + memset(&strmbs, 0, sizeof(strmbs)); + } + switch (pc) { + case EOS: + if ((flags & FNM_LEADING_DIR) && sc == '/') + return (0); + if (sc == EOS) + return (0); + goto backtrack; + case '?': + if (sc == EOS) + return (FNM_NOMATCH); + if (sc == '/' && (flags & FNM_PATHNAME)) + goto backtrack; + if (sc == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + goto backtrack; + string += sclen; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (sc == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + goto backtrack; + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + else if (c == '/' && flags & FNM_PATHNAME) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* + * First try the shortest match for the '*' that + * could work. We can forget any earlier '*' since + * there is no way having it match more characters + * can help us, given that we are already here. + */ + bt_pattern = pattern, bt_patmbs = patmbs; + bt_string = string, bt_strmbs = strmbs; + break; + case '[': + if (sc == EOS) + return (FNM_NOMATCH); + if (sc == '/' && (flags & FNM_PATHNAME)) + goto backtrack; + if (sc == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + goto backtrack; + + switch (rangematch(pattern, sc, flags, &newp, + &patmbs)) { + case RANGE_ERROR: + goto norm; + case RANGE_MATCH: + pattern = newp; + break; + case RANGE_NOMATCH: + goto backtrack; + } + string += sclen; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, + &patmbs); + if (pclen == 0 || pclen == (size_t)-1 || + pclen == (size_t)-2) + return (FNM_NOMATCH); + pattern += pclen; + } + /* FALLTHROUGH */ + default: + norm: + string += sclen; + if (pc == sc) + ; + else if ((flags & FNM_CASEFOLD) && + (towlower(pc) == towlower(sc))) + ; + else { + backtrack: + /* + * If we have a mismatch (other than hitting + * the end of the string), go back to the last + * '*' seen and have it match one additional + * character. + */ + if (bt_pattern == NULL) + return (FNM_NOMATCH); + sclen = mbrtowc(&sc, bt_string, MB_LEN_MAX, + &bt_strmbs); + if (sclen == (size_t)-1 || + sclen == (size_t)-2) { + sc = (unsigned char)*bt_string; + sclen = 1; + memset(&bt_strmbs, 0, + sizeof(bt_strmbs)); + } + if (sc == EOS) + return (FNM_NOMATCH); + if (sc == '/' && flags & FNM_PATHNAME) + return (FNM_NOMATCH); + bt_string += sclen; + pattern = bt_pattern, patmbs = bt_patmbs; + string = bt_string, strmbs = bt_strmbs; + } + break; + } + } + /* NOTREACHED */ +} + +static int +rangematch(const char *pattern, wchar_t test, int flags, const char **newp, + mbstate_t *patmbs) +{ + int negate, ok; + wchar_t s1[2], s2[2], t[2]; + size_t pclen; + const char *origpat; + + s1[1] = L'\0'; + s2[1] = L'\0'; + + t[0] = test; + t[1] = L'\0'; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ((negate = (*pattern == '!' || *pattern == '^'))) + ++pattern; + + if (flags & FNM_CASEFOLD) + t[0] = towlower(t[0]); + + /* + * A right bracket shall lose its special meaning and represent + * itself in a bracket expression if it occurs first in the list. + * -- POSIX.2 2.8.3.2 + */ + ok = 0; + origpat = pattern; + for (;;) { + if (*pattern == ']' && pattern > origpat) { + pattern++; + break; + } else if (*pattern == '\0') { + return (RANGE_ERROR); + } else if (*pattern == '/' && (flags & FNM_PATHNAME)) { + return (RANGE_NOMATCH); + } else if (*pattern == '\\' && !(flags & FNM_NOESCAPE)) + pattern++; + pclen = mbrtowc(s1, pattern, MB_LEN_MAX, patmbs); + if (pclen == (size_t)-1 || pclen == (size_t)-2) + return (RANGE_NOMATCH); + pattern += pclen; + + if (flags & FNM_CASEFOLD) + s1[0] = towlower(s1[0]); + + if (*pattern == '-' && *(pattern + 1) != EOS && + *(pattern + 1) != ']') { + if (*++pattern == '\\' && !(flags & FNM_NOESCAPE)) + if (*pattern != EOS) + pattern++; + pclen = mbrtowc(s2, pattern, MB_LEN_MAX, patmbs); + if (pclen == (size_t)-1 || pclen == (size_t)-2) + return (RANGE_NOMATCH); + pattern += pclen; + if (s2[0] == EOS) + return (RANGE_ERROR); + + if (flags & FNM_CASEFOLD) + s2[0] = towlower(s2[0]); + + if (wcscoll(s1, t) && wcscoll(t, s2) <= 0) + ok = 1; + } else if (s1[0] == t[0]) + ok = 1; + } + + *newp = pattern; + return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); +} +#endif diff --git a/libarchive_fe/lafe_fnmatch.h b/libarchive_fe/lafe_fnmatch.h new file mode 100644 index 00000000..e599056a --- /dev/null +++ b/libarchive_fe/lafe_fnmatch.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef LAFE_FNMATCH_H_INCLUDED +#define LAFE_FNMATCH_H_INCLUDED + +#include "lafe_platform.h" + +#ifndef HAVE_FNMATCH + +#define FNM_NOMATCH 1 /* Match failed. */ + +#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +#define FNM_PERIOD 0x04 /* Period must be matched by period. */ + +#define FNM_NOSYS (-1) /* Reserved. */ + +#define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ +#define FNM_CASEFOLD 0x10 /* Case insensitive search. */ +#define FNM_IGNORECASE FNM_CASEFOLD +#define FNM_FILE_NAME FNM_PATHNAME + +int fnmatch(const char *pattern, const char *string, int flags); +#endif /* !HAVE_FNMATCH */ + +#endif /* !LAFE_FNMATCH_H_INCLUDED */ diff --git a/unzip/CMakeLists.txt b/unzip/CMakeLists.txt index 9fe21eee..10582b70 100644 --- a/unzip/CMakeLists.txt +++ b/unzip/CMakeLists.txt @@ -16,6 +16,8 @@ IF(ENABLE_UNZIP) la_queue.h ../libarchive_fe/lafe_err.c ../libarchive_fe/lafe_err.h + ../libarchive_fe/lafe_fnmatch.c + ../libarchive_fe/lafe_fnmatch.h ../libarchive_fe/lafe_getline.c ../libarchive_fe/lafe_getline.h ../libarchive_fe/lafe_platform.h diff --git a/unzip/bsdunzip.c b/unzip/bsdunzip.c index 21d24fca..d4c34e63 100644 --- a/unzip/bsdunzip.c +++ b/unzip/bsdunzip.c @@ -9,6 +9,7 @@ #include "bsdunzip_platform.h" #include "la_queue.h" +#include "lafe_fnmatch.h" #include "lafe_getline.h" #ifdef HAVE_SYS_STAT_H #include @@ -304,12 +305,8 @@ match_pattern(struct pattern_list *list, const char *str) struct pattern *entry; STAILQ_FOREACH(entry, list, link) { -#ifdef HAVE_FNMATCH if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0) return (1); -#else -#error "Unsupported platform: fnmatch() is required" -#endif } return (0); }