xref: /netbsd-src/external/bsd/libarchive/dist/libarchive/archive_pathmatch.c (revision 65e637ab3a9cc7c3e7749c941a1011ecd65517e6)
140b1a6e6Sjoerg /*-
240b1a6e6Sjoerg  * Copyright (c) 2003-2007 Tim Kientzle
340b1a6e6Sjoerg  * All rights reserved.
440b1a6e6Sjoerg  *
540b1a6e6Sjoerg  * Redistribution and use in source and binary forms, with or without
640b1a6e6Sjoerg  * modification, are permitted provided that the following conditions
740b1a6e6Sjoerg  * are met:
840b1a6e6Sjoerg  * 1. Redistributions of source code must retain the above copyright
940b1a6e6Sjoerg  *    notice, this list of conditions and the following disclaimer
1040b1a6e6Sjoerg  *    in this position and unchanged.
1140b1a6e6Sjoerg  * 2. Redistributions in binary form must reproduce the above copyright
1240b1a6e6Sjoerg  *    notice, this list of conditions and the following disclaimer in the
1340b1a6e6Sjoerg  *    documentation and/or other materials provided with the distribution.
1440b1a6e6Sjoerg  *
1540b1a6e6Sjoerg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
1640b1a6e6Sjoerg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1740b1a6e6Sjoerg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1840b1a6e6Sjoerg  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
1940b1a6e6Sjoerg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2040b1a6e6Sjoerg  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2140b1a6e6Sjoerg  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2240b1a6e6Sjoerg  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2340b1a6e6Sjoerg  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2440b1a6e6Sjoerg  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2540b1a6e6Sjoerg  */
2640b1a6e6Sjoerg 
2740b1a6e6Sjoerg #include "archive_platform.h"
2840b1a6e6Sjoerg 
2940b1a6e6Sjoerg #ifdef HAVE_STRING_H
3040b1a6e6Sjoerg #include <string.h>
3140b1a6e6Sjoerg #endif
3240b1a6e6Sjoerg #ifdef HAVE_WCHAR_H
3340b1a6e6Sjoerg #include <wchar.h>
3440b1a6e6Sjoerg #endif
3540b1a6e6Sjoerg 
3640b1a6e6Sjoerg #include "archive_pathmatch.h"
3740b1a6e6Sjoerg 
3840b1a6e6Sjoerg /*
3940b1a6e6Sjoerg  * Check whether a character 'c' is matched by a list specification [...]:
4040b1a6e6Sjoerg  *    * Leading '!' or '^' negates the class.
4140b1a6e6Sjoerg  *    * <char>-<char> is a range of characters
4240b1a6e6Sjoerg  *    * \<char> removes any special meaning for <char>
4340b1a6e6Sjoerg  *
4440b1a6e6Sjoerg  * Some interesting boundary cases:
4540b1a6e6Sjoerg  *   a-d-e is one range (a-d) followed by two single characters - and e.
4640b1a6e6Sjoerg  *   \a-\d is same as a-d
4740b1a6e6Sjoerg  *   a\-d is three single characters: a, d, -
4840b1a6e6Sjoerg  *   Trailing - is not special (so [a-] is two characters a and -).
4940b1a6e6Sjoerg  *   Initial - is not special ([a-] is same as [-a] is same as [\\-a])
5040b1a6e6Sjoerg  *   This function never sees a trailing \.
5140b1a6e6Sjoerg  *   [] always fails
5240b1a6e6Sjoerg  *   [!] always succeeds
5340b1a6e6Sjoerg  */
5440b1a6e6Sjoerg static int
pm_list(const char * start,const char * end,const char c,int flags)5540b1a6e6Sjoerg pm_list(const char *start, const char *end, const char c, int flags)
5640b1a6e6Sjoerg {
5740b1a6e6Sjoerg 	const char *p = start;
5840b1a6e6Sjoerg 	char rangeStart = '\0', nextRangeStart;
5940b1a6e6Sjoerg 	int match = 1, nomatch = 0;
6040b1a6e6Sjoerg 
6140b1a6e6Sjoerg 	/* This will be used soon... */
6240b1a6e6Sjoerg 	(void)flags; /* UNUSED */
6340b1a6e6Sjoerg 
6440b1a6e6Sjoerg 	/* If this is a negated class, return success for nomatch. */
6540b1a6e6Sjoerg 	if ((*p == '!' || *p == '^') && p < end) {
6640b1a6e6Sjoerg 		match = 0;
6740b1a6e6Sjoerg 		nomatch = 1;
6840b1a6e6Sjoerg 		++p;
6940b1a6e6Sjoerg 	}
7040b1a6e6Sjoerg 
7140b1a6e6Sjoerg 	while (p < end) {
7240b1a6e6Sjoerg 		nextRangeStart = '\0';
7340b1a6e6Sjoerg 		switch (*p) {
7440b1a6e6Sjoerg 		case '-':
7540b1a6e6Sjoerg 			/* Trailing or initial '-' is not special. */
7640b1a6e6Sjoerg 			if ((rangeStart == '\0') || (p == end - 1)) {
7740b1a6e6Sjoerg 				if (*p == c)
7840b1a6e6Sjoerg 					return (match);
7940b1a6e6Sjoerg 			} else {
8040b1a6e6Sjoerg 				char rangeEnd = *++p;
8140b1a6e6Sjoerg 				if (rangeEnd == '\\')
8240b1a6e6Sjoerg 					rangeEnd = *++p;
8340b1a6e6Sjoerg 				if ((rangeStart <= c) && (c <= rangeEnd))
8440b1a6e6Sjoerg 					return (match);
8540b1a6e6Sjoerg 			}
8640b1a6e6Sjoerg 			break;
8740b1a6e6Sjoerg 		case '\\':
8840b1a6e6Sjoerg 			++p;
8940b1a6e6Sjoerg 			/* Fall through */
9040b1a6e6Sjoerg 		default:
9140b1a6e6Sjoerg 			if (*p == c)
9240b1a6e6Sjoerg 				return (match);
9340b1a6e6Sjoerg 			nextRangeStart = *p; /* Possible start of range. */
9440b1a6e6Sjoerg 		}
9540b1a6e6Sjoerg 		rangeStart = nextRangeStart;
9640b1a6e6Sjoerg 		++p;
9740b1a6e6Sjoerg 	}
9840b1a6e6Sjoerg 	return (nomatch);
9940b1a6e6Sjoerg }
10040b1a6e6Sjoerg 
10140b1a6e6Sjoerg static int
pm_list_w(const wchar_t * start,const wchar_t * end,const wchar_t c,int flags)10240b1a6e6Sjoerg pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)
10340b1a6e6Sjoerg {
10440b1a6e6Sjoerg 	const wchar_t *p = start;
10540b1a6e6Sjoerg 	wchar_t rangeStart = L'\0', nextRangeStart;
10640b1a6e6Sjoerg 	int match = 1, nomatch = 0;
10740b1a6e6Sjoerg 
10840b1a6e6Sjoerg 	/* This will be used soon... */
10940b1a6e6Sjoerg 	(void)flags; /* UNUSED */
11040b1a6e6Sjoerg 
11140b1a6e6Sjoerg 	/* If this is a negated class, return success for nomatch. */
11240b1a6e6Sjoerg 	if ((*p == L'!' || *p == L'^') && p < end) {
11340b1a6e6Sjoerg 		match = 0;
11440b1a6e6Sjoerg 		nomatch = 1;
11540b1a6e6Sjoerg 		++p;
11640b1a6e6Sjoerg 	}
11740b1a6e6Sjoerg 
11840b1a6e6Sjoerg 	while (p < end) {
11940b1a6e6Sjoerg 		nextRangeStart = L'\0';
12040b1a6e6Sjoerg 		switch (*p) {
12140b1a6e6Sjoerg 		case L'-':
12240b1a6e6Sjoerg 			/* Trailing or initial '-' is not special. */
12340b1a6e6Sjoerg 			if ((rangeStart == L'\0') || (p == end - 1)) {
12440b1a6e6Sjoerg 				if (*p == c)
12540b1a6e6Sjoerg 					return (match);
12640b1a6e6Sjoerg 			} else {
12740b1a6e6Sjoerg 				wchar_t rangeEnd = *++p;
12840b1a6e6Sjoerg 				if (rangeEnd == L'\\')
12940b1a6e6Sjoerg 					rangeEnd = *++p;
13040b1a6e6Sjoerg 				if ((rangeStart <= c) && (c <= rangeEnd))
13140b1a6e6Sjoerg 					return (match);
13240b1a6e6Sjoerg 			}
13340b1a6e6Sjoerg 			break;
13440b1a6e6Sjoerg 		case L'\\':
13540b1a6e6Sjoerg 			++p;
13640b1a6e6Sjoerg 			/* Fall through */
13740b1a6e6Sjoerg 		default:
13840b1a6e6Sjoerg 			if (*p == c)
13940b1a6e6Sjoerg 				return (match);
14040b1a6e6Sjoerg 			nextRangeStart = *p; /* Possible start of range. */
14140b1a6e6Sjoerg 		}
14240b1a6e6Sjoerg 		rangeStart = nextRangeStart;
14340b1a6e6Sjoerg 		++p;
14440b1a6e6Sjoerg 	}
14540b1a6e6Sjoerg 	return (nomatch);
14640b1a6e6Sjoerg }
14740b1a6e6Sjoerg 
14840b1a6e6Sjoerg /*
14940b1a6e6Sjoerg  * If s is pointing to "./", ".//", "./././" or the like, skip it.
15040b1a6e6Sjoerg  */
15140b1a6e6Sjoerg static const char *
pm_slashskip(const char * s)15240b1a6e6Sjoerg pm_slashskip(const char *s) {
15340b1a6e6Sjoerg 	while ((*s == '/')
15440b1a6e6Sjoerg 	    || (s[0] == '.' && s[1] == '/')
15540b1a6e6Sjoerg 	    || (s[0] == '.' && s[1] == '\0'))
15640b1a6e6Sjoerg 		++s;
15740b1a6e6Sjoerg 	return (s);
15840b1a6e6Sjoerg }
15940b1a6e6Sjoerg 
16040b1a6e6Sjoerg static const wchar_t *
pm_slashskip_w(const wchar_t * s)16140b1a6e6Sjoerg pm_slashskip_w(const wchar_t *s) {
16240b1a6e6Sjoerg 	while ((*s == L'/')
16340b1a6e6Sjoerg 	    || (s[0] == L'.' && s[1] == L'/')
16440b1a6e6Sjoerg 	    || (s[0] == L'.' && s[1] == L'\0'))
16540b1a6e6Sjoerg 		++s;
16640b1a6e6Sjoerg 	return (s);
16740b1a6e6Sjoerg }
16840b1a6e6Sjoerg 
16940b1a6e6Sjoerg static int
pm(const char * p,const char * s,int flags)17040b1a6e6Sjoerg pm(const char *p, const char *s, int flags)
17140b1a6e6Sjoerg {
17240b1a6e6Sjoerg 	const char *end;
17340b1a6e6Sjoerg 
17440b1a6e6Sjoerg 	/*
17540b1a6e6Sjoerg 	 * Ignore leading './', './/', '././', etc.
17640b1a6e6Sjoerg 	 */
17740b1a6e6Sjoerg 	if (s[0] == '.' && s[1] == '/')
17840b1a6e6Sjoerg 		s = pm_slashskip(s + 1);
17940b1a6e6Sjoerg 	if (p[0] == '.' && p[1] == '/')
18040b1a6e6Sjoerg 		p = pm_slashskip(p + 1);
18140b1a6e6Sjoerg 
18240b1a6e6Sjoerg 	for (;;) {
18340b1a6e6Sjoerg 		switch (*p) {
18440b1a6e6Sjoerg 		case '\0':
18540b1a6e6Sjoerg 			if (s[0] == '/') {
18640b1a6e6Sjoerg 				if (flags & PATHMATCH_NO_ANCHOR_END)
18740b1a6e6Sjoerg 					return (1);
18840b1a6e6Sjoerg 				/* "dir" == "dir/" == "dir/." */
18940b1a6e6Sjoerg 				s = pm_slashskip(s);
19040b1a6e6Sjoerg 			}
19140b1a6e6Sjoerg 			return (*s == '\0');
19240b1a6e6Sjoerg 		case '?':
19340b1a6e6Sjoerg 			/* ? always succeeds, unless we hit end of 's' */
19440b1a6e6Sjoerg 			if (*s == '\0')
19540b1a6e6Sjoerg 				return (0);
19640b1a6e6Sjoerg 			break;
19740b1a6e6Sjoerg 		case '*':
19840b1a6e6Sjoerg 			/* "*" == "**" == "***" ... */
19940b1a6e6Sjoerg 			while (*p == '*')
20040b1a6e6Sjoerg 				++p;
20140b1a6e6Sjoerg 			/* Trailing '*' always succeeds. */
20240b1a6e6Sjoerg 			if (*p == '\0')
20340b1a6e6Sjoerg 				return (1);
20440b1a6e6Sjoerg 			while (*s) {
20540b1a6e6Sjoerg 				if (archive_pathmatch(p, s, flags))
20640b1a6e6Sjoerg 					return (1);
20740b1a6e6Sjoerg 				++s;
20840b1a6e6Sjoerg 			}
20940b1a6e6Sjoerg 			return (0);
21040b1a6e6Sjoerg 		case '[':
21140b1a6e6Sjoerg 			/*
21240b1a6e6Sjoerg 			 * Find the end of the [...] character class,
21340b1a6e6Sjoerg 			 * ignoring \] that might occur within the class.
21440b1a6e6Sjoerg 			 */
21540b1a6e6Sjoerg 			end = p + 1;
21640b1a6e6Sjoerg 			while (*end != '\0' && *end != ']') {
21740b1a6e6Sjoerg 				if (*end == '\\' && end[1] != '\0')
21840b1a6e6Sjoerg 					++end;
21940b1a6e6Sjoerg 				++end;
22040b1a6e6Sjoerg 			}
22140b1a6e6Sjoerg 			if (*end == ']') {
22240b1a6e6Sjoerg 				/* We found [...], try to match it. */
22340b1a6e6Sjoerg 				if (!pm_list(p + 1, end, *s, flags))
22440b1a6e6Sjoerg 					return (0);
22540b1a6e6Sjoerg 				p = end; /* Jump to trailing ']' char. */
22640b1a6e6Sjoerg 				break;
22740b1a6e6Sjoerg 			} else
22840b1a6e6Sjoerg 				/* No final ']', so just match '['. */
22940b1a6e6Sjoerg 				if (*p != *s)
23040b1a6e6Sjoerg 					return (0);
23140b1a6e6Sjoerg 			break;
23240b1a6e6Sjoerg 		case '\\':
23340b1a6e6Sjoerg 			/* Trailing '\\' matches itself. */
23440b1a6e6Sjoerg 			if (p[1] == '\0') {
23540b1a6e6Sjoerg 				if (*s != '\\')
23640b1a6e6Sjoerg 					return (0);
23740b1a6e6Sjoerg 			} else {
23840b1a6e6Sjoerg 				++p;
23940b1a6e6Sjoerg 				if (*p != *s)
24040b1a6e6Sjoerg 					return (0);
24140b1a6e6Sjoerg 			}
24240b1a6e6Sjoerg 			break;
24340b1a6e6Sjoerg 		case '/':
24440b1a6e6Sjoerg 			if (*s != '/' && *s != '\0')
24540b1a6e6Sjoerg 				return (0);
24640b1a6e6Sjoerg 			/* Note: pattern "/\./" won't match "/";
24740b1a6e6Sjoerg 			 * pm_slashskip() correctly stops at backslash. */
24840b1a6e6Sjoerg 			p = pm_slashskip(p);
24940b1a6e6Sjoerg 			s = pm_slashskip(s);
25040b1a6e6Sjoerg 			if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))
25140b1a6e6Sjoerg 				return (1);
25240b1a6e6Sjoerg 			--p; /* Counteract the increment below. */
25340b1a6e6Sjoerg 			--s;
25440b1a6e6Sjoerg 			break;
25540b1a6e6Sjoerg 		case '$':
25640b1a6e6Sjoerg 			/* '$' is special only at end of pattern and only
25740b1a6e6Sjoerg 			 * if PATHMATCH_NO_ANCHOR_END is specified. */
25840b1a6e6Sjoerg 			if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
25940b1a6e6Sjoerg 				/* "dir" == "dir/" == "dir/." */
26040b1a6e6Sjoerg 				return (*pm_slashskip(s) == '\0');
26140b1a6e6Sjoerg 			}
26240b1a6e6Sjoerg 			/* Otherwise, '$' is not special. */
26340b1a6e6Sjoerg 			/* FALL THROUGH */
26440b1a6e6Sjoerg 		default:
26540b1a6e6Sjoerg 			if (*p != *s)
26640b1a6e6Sjoerg 				return (0);
26740b1a6e6Sjoerg 			break;
26840b1a6e6Sjoerg 		}
26940b1a6e6Sjoerg 		++p;
27040b1a6e6Sjoerg 		++s;
27140b1a6e6Sjoerg 	}
27240b1a6e6Sjoerg }
27340b1a6e6Sjoerg 
27440b1a6e6Sjoerg static int
pm_w(const wchar_t * p,const wchar_t * s,int flags)27540b1a6e6Sjoerg pm_w(const wchar_t *p, const wchar_t *s, int flags)
27640b1a6e6Sjoerg {
27740b1a6e6Sjoerg 	const wchar_t *end;
27840b1a6e6Sjoerg 
27940b1a6e6Sjoerg 	/*
28040b1a6e6Sjoerg 	 * Ignore leading './', './/', '././', etc.
28140b1a6e6Sjoerg 	 */
28240b1a6e6Sjoerg 	if (s[0] == L'.' && s[1] == L'/')
28340b1a6e6Sjoerg 		s = pm_slashskip_w(s + 1);
28440b1a6e6Sjoerg 	if (p[0] == L'.' && p[1] == L'/')
28540b1a6e6Sjoerg 		p = pm_slashskip_w(p + 1);
28640b1a6e6Sjoerg 
28740b1a6e6Sjoerg 	for (;;) {
28840b1a6e6Sjoerg 		switch (*p) {
28940b1a6e6Sjoerg 		case L'\0':
29040b1a6e6Sjoerg 			if (s[0] == L'/') {
29140b1a6e6Sjoerg 				if (flags & PATHMATCH_NO_ANCHOR_END)
29240b1a6e6Sjoerg 					return (1);
29340b1a6e6Sjoerg 				/* "dir" == "dir/" == "dir/." */
29440b1a6e6Sjoerg 				s = pm_slashskip_w(s);
29540b1a6e6Sjoerg 			}
29640b1a6e6Sjoerg 			return (*s == L'\0');
29740b1a6e6Sjoerg 		case L'?':
29840b1a6e6Sjoerg 			/* ? always succeeds, unless we hit end of 's' */
29940b1a6e6Sjoerg 			if (*s == L'\0')
30040b1a6e6Sjoerg 				return (0);
30140b1a6e6Sjoerg 			break;
30240b1a6e6Sjoerg 		case L'*':
30340b1a6e6Sjoerg 			/* "*" == "**" == "***" ... */
30440b1a6e6Sjoerg 			while (*p == L'*')
30540b1a6e6Sjoerg 				++p;
30640b1a6e6Sjoerg 			/* Trailing '*' always succeeds. */
30740b1a6e6Sjoerg 			if (*p == L'\0')
30840b1a6e6Sjoerg 				return (1);
30940b1a6e6Sjoerg 			while (*s) {
31040b1a6e6Sjoerg 				if (archive_pathmatch_w(p, s, flags))
31140b1a6e6Sjoerg 					return (1);
31240b1a6e6Sjoerg 				++s;
31340b1a6e6Sjoerg 			}
31440b1a6e6Sjoerg 			return (0);
31540b1a6e6Sjoerg 		case L'[':
31640b1a6e6Sjoerg 			/*
31740b1a6e6Sjoerg 			 * Find the end of the [...] character class,
31840b1a6e6Sjoerg 			 * ignoring \] that might occur within the class.
31940b1a6e6Sjoerg 			 */
32040b1a6e6Sjoerg 			end = p + 1;
32140b1a6e6Sjoerg 			while (*end != L'\0' && *end != L']') {
32240b1a6e6Sjoerg 				if (*end == L'\\' && end[1] != L'\0')
32340b1a6e6Sjoerg 					++end;
32440b1a6e6Sjoerg 				++end;
32540b1a6e6Sjoerg 			}
32640b1a6e6Sjoerg 			if (*end == L']') {
32740b1a6e6Sjoerg 				/* We found [...], try to match it. */
32840b1a6e6Sjoerg 				if (!pm_list_w(p + 1, end, *s, flags))
32940b1a6e6Sjoerg 					return (0);
33040b1a6e6Sjoerg 				p = end; /* Jump to trailing ']' char. */
33140b1a6e6Sjoerg 				break;
33240b1a6e6Sjoerg 			} else
33340b1a6e6Sjoerg 				/* No final ']', so just match '['. */
33440b1a6e6Sjoerg 				if (*p != *s)
33540b1a6e6Sjoerg 					return (0);
33640b1a6e6Sjoerg 			break;
33740b1a6e6Sjoerg 		case L'\\':
33840b1a6e6Sjoerg 			/* Trailing '\\' matches itself. */
33940b1a6e6Sjoerg 			if (p[1] == L'\0') {
34040b1a6e6Sjoerg 				if (*s != L'\\')
34140b1a6e6Sjoerg 					return (0);
34240b1a6e6Sjoerg 			} else {
34340b1a6e6Sjoerg 				++p;
34440b1a6e6Sjoerg 				if (*p != *s)
34540b1a6e6Sjoerg 					return (0);
34640b1a6e6Sjoerg 			}
34740b1a6e6Sjoerg 			break;
34840b1a6e6Sjoerg 		case L'/':
34940b1a6e6Sjoerg 			if (*s != L'/' && *s != L'\0')
35040b1a6e6Sjoerg 				return (0);
35140b1a6e6Sjoerg 			/* Note: pattern "/\./" won't match "/";
35240b1a6e6Sjoerg 			 * pm_slashskip() correctly stops at backslash. */
35340b1a6e6Sjoerg 			p = pm_slashskip_w(p);
35440b1a6e6Sjoerg 			s = pm_slashskip_w(s);
35540b1a6e6Sjoerg 			if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))
35640b1a6e6Sjoerg 				return (1);
35740b1a6e6Sjoerg 			--p; /* Counteract the increment below. */
35840b1a6e6Sjoerg 			--s;
35940b1a6e6Sjoerg 			break;
36040b1a6e6Sjoerg 		case L'$':
36140b1a6e6Sjoerg 			/* '$' is special only at end of pattern and only
36240b1a6e6Sjoerg 			 * if PATHMATCH_NO_ANCHOR_END is specified. */
36340b1a6e6Sjoerg 			if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
36440b1a6e6Sjoerg 				/* "dir" == "dir/" == "dir/." */
36540b1a6e6Sjoerg 				return (*pm_slashskip_w(s) == L'\0');
36640b1a6e6Sjoerg 			}
36740b1a6e6Sjoerg 			/* Otherwise, '$' is not special. */
36840b1a6e6Sjoerg 			/* FALL THROUGH */
36940b1a6e6Sjoerg 		default:
37040b1a6e6Sjoerg 			if (*p != *s)
37140b1a6e6Sjoerg 				return (0);
37240b1a6e6Sjoerg 			break;
37340b1a6e6Sjoerg 		}
37440b1a6e6Sjoerg 		++p;
37540b1a6e6Sjoerg 		++s;
37640b1a6e6Sjoerg 	}
37740b1a6e6Sjoerg }
37840b1a6e6Sjoerg 
37940b1a6e6Sjoerg /* Main entry point. */
38040b1a6e6Sjoerg int
__archive_pathmatch(const char * p,const char * s,int flags)38140b1a6e6Sjoerg __archive_pathmatch(const char *p, const char *s, int flags)
38240b1a6e6Sjoerg {
38340b1a6e6Sjoerg 	/* Empty pattern only matches the empty string. */
38440b1a6e6Sjoerg 	if (p == NULL || *p == '\0')
38540b1a6e6Sjoerg 		return (s == NULL || *s == '\0');
386*65e637abSchristos 	else if (s == NULL)
387*65e637abSchristos 		return (0);
38840b1a6e6Sjoerg 
38940b1a6e6Sjoerg 	/* Leading '^' anchors the start of the pattern. */
39040b1a6e6Sjoerg 	if (*p == '^') {
39140b1a6e6Sjoerg 		++p;
39240b1a6e6Sjoerg 		flags &= ~PATHMATCH_NO_ANCHOR_START;
39340b1a6e6Sjoerg 	}
39440b1a6e6Sjoerg 
39540b1a6e6Sjoerg 	if (*p == '/' && *s != '/')
39640b1a6e6Sjoerg 		return (0);
39740b1a6e6Sjoerg 
39840b1a6e6Sjoerg 	/* Certain patterns anchor implicitly. */
39940b1a6e6Sjoerg 	if (*p == '*' || *p == '/') {
40040b1a6e6Sjoerg 		while (*p == '/')
40140b1a6e6Sjoerg 			++p;
40240b1a6e6Sjoerg 		while (*s == '/')
40340b1a6e6Sjoerg 			++s;
40440b1a6e6Sjoerg 		return (pm(p, s, flags));
40540b1a6e6Sjoerg 	}
40640b1a6e6Sjoerg 
40740b1a6e6Sjoerg 	/* If start is unanchored, try to match start of each path element. */
40840b1a6e6Sjoerg 	if (flags & PATHMATCH_NO_ANCHOR_START) {
40940b1a6e6Sjoerg 		for ( ; s != NULL; s = strchr(s, '/')) {
41040b1a6e6Sjoerg 			if (*s == '/')
41140b1a6e6Sjoerg 				s++;
41240b1a6e6Sjoerg 			if (pm(p, s, flags))
41340b1a6e6Sjoerg 				return (1);
41440b1a6e6Sjoerg 		}
41540b1a6e6Sjoerg 		return (0);
41640b1a6e6Sjoerg 	}
41740b1a6e6Sjoerg 
41840b1a6e6Sjoerg 	/* Default: Match from beginning. */
41940b1a6e6Sjoerg 	return (pm(p, s, flags));
42040b1a6e6Sjoerg }
42140b1a6e6Sjoerg 
42240b1a6e6Sjoerg int
__archive_pathmatch_w(const wchar_t * p,const wchar_t * s,int flags)42340b1a6e6Sjoerg __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
42440b1a6e6Sjoerg {
42540b1a6e6Sjoerg 	/* Empty pattern only matches the empty string. */
42640b1a6e6Sjoerg 	if (p == NULL || *p == L'\0')
42740b1a6e6Sjoerg 		return (s == NULL || *s == L'\0');
428*65e637abSchristos 	else if (s == NULL)
429*65e637abSchristos 		return (0);
43040b1a6e6Sjoerg 
43140b1a6e6Sjoerg 	/* Leading '^' anchors the start of the pattern. */
43240b1a6e6Sjoerg 	if (*p == L'^') {
43340b1a6e6Sjoerg 		++p;
43440b1a6e6Sjoerg 		flags &= ~PATHMATCH_NO_ANCHOR_START;
43540b1a6e6Sjoerg 	}
43640b1a6e6Sjoerg 
43740b1a6e6Sjoerg 	if (*p == L'/' && *s != L'/')
43840b1a6e6Sjoerg 		return (0);
43940b1a6e6Sjoerg 
44040b1a6e6Sjoerg 	/* Certain patterns anchor implicitly. */
44140b1a6e6Sjoerg 	if (*p == L'*' || *p == L'/') {
44240b1a6e6Sjoerg 		while (*p == L'/')
44340b1a6e6Sjoerg 			++p;
44440b1a6e6Sjoerg 		while (*s == L'/')
44540b1a6e6Sjoerg 			++s;
44640b1a6e6Sjoerg 		return (pm_w(p, s, flags));
44740b1a6e6Sjoerg 	}
44840b1a6e6Sjoerg 
44940b1a6e6Sjoerg 	/* If start is unanchored, try to match start of each path element. */
45040b1a6e6Sjoerg 	if (flags & PATHMATCH_NO_ANCHOR_START) {
45140b1a6e6Sjoerg 		for ( ; s != NULL; s = wcschr(s, L'/')) {
45240b1a6e6Sjoerg 			if (*s == L'/')
45340b1a6e6Sjoerg 				s++;
45440b1a6e6Sjoerg 			if (pm_w(p, s, flags))
45540b1a6e6Sjoerg 				return (1);
45640b1a6e6Sjoerg 		}
45740b1a6e6Sjoerg 		return (0);
45840b1a6e6Sjoerg 	}
45940b1a6e6Sjoerg 
46040b1a6e6Sjoerg 	/* Default: Match from beginning. */
46140b1a6e6Sjoerg 	return (pm_w(p, s, flags));
46240b1a6e6Sjoerg }
463