123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- /*
- ** Do shell-style pattern matching for ?, \, [], and * characters.
- ** It is 8bit clean.
- **
- ** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
- ** Rich $alz is now <rsalz@bbn.com>.
- **
- ** Modified by Wayne Davison to special-case '/' matching, to make '**'
- ** work differently than '*', and to fix the character-class code.
- */
- #include "rsync.h"
- /* What character marks an inverted character class? */
- #define NEGATE_CLASS '!'
- #define NEGATE_CLASS2 '^'
- #define FALSE 0
- #define TRUE 1
- #define ABORT_ALL -1
- #define ABORT_TO_STARSTAR -2
- #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
- && *(class) == *(litmatch) \
- && strncmp((char*)class, litmatch, len) == 0)
- #if defined STDC_HEADERS || !defined isascii
- # define ISASCII(c) 1
- #else
- # define ISASCII(c) isascii(c)
- #endif
- #ifdef isblank
- # define ISBLANK(c) (ISASCII(c) && isblank(c))
- #else
- # define ISBLANK(c) ((c) == ' ' || (c) == '\t')
- #endif
- #ifdef isgraph
- # define ISGRAPH(c) (ISASCII(c) && isgraph(c))
- #else
- # define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
- #endif
- #define ISPRINT(c) (ISASCII(c) && isprint(c))
- #define ISDIGIT(c) (ISASCII(c) && isdigit(c))
- #define ISALNUM(c) (ISASCII(c) && isalnum(c))
- #define ISALPHA(c) (ISASCII(c) && isalpha(c))
- #define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
- #define ISLOWER(c) (ISASCII(c) && islower(c))
- #define ISPUNCT(c) (ISASCII(c) && ispunct(c))
- #define ISSPACE(c) (ISASCII(c) && isspace(c))
- #define ISUPPER(c) (ISASCII(c) && isupper(c))
- #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
- #ifdef WILD_TEST_ITERATIONS
- int wildmatch_iteration_count;
- #endif
- static int force_lower_case = 0;
- /* Match pattern "p" against the a virtually-joined string consisting
- * of "text" and any strings in array "a". */
- static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
- {
- uchar p_ch;
- #ifdef WILD_TEST_ITERATIONS
- wildmatch_iteration_count++;
- #endif
- for ( ; (p_ch = *p) != '\0'; text++, p++) {
- int matched, special;
- uchar t_ch, prev_ch;
- while ((t_ch = *text) == '\0') {
- if (*a == NULL) {
- if (p_ch != '*')
- return ABORT_ALL;
- break;
- }
- text = *a++;
- }
- if (force_lower_case && ISUPPER(t_ch))
- t_ch = tolower(t_ch);
- switch (p_ch) {
- case '\\':
- /* Literal match with following character. Note that the test
- * in "default" handles the p[1] == '\0' failure case. */
- p_ch = *++p;
- /* FALLTHROUGH */
- default:
- if (t_ch != p_ch)
- return FALSE;
- continue;
- case '?':
- /* Match anything but '/'. */
- if (t_ch == '/')
- return FALSE;
- continue;
- case '*':
- if (*++p == '*') {
- while (*++p == '*') {}
- special = TRUE;
- } else
- special = FALSE;
- if (*p == '\0') {
- /* Trailing "**" matches everything. Trailing "*" matches
- * only if there are no more slash characters. */
- if (!special) {
- do {
- if (strchr((char*)text, '/') != NULL)
- return FALSE;
- } while ((text = *a++) != NULL);
- }
- return TRUE;
- }
- while (1) {
- if (t_ch == '\0') {
- if ((text = *a++) == NULL)
- break;
- t_ch = *text;
- continue;
- }
- if ((matched = dowild(p, text, a)) != FALSE) {
- if (!special || matched != ABORT_TO_STARSTAR)
- return matched;
- } else if (!special && t_ch == '/')
- return ABORT_TO_STARSTAR;
- t_ch = *++text;
- }
- return ABORT_ALL;
- case '[':
- p_ch = *++p;
- #ifdef NEGATE_CLASS2
- if (p_ch == NEGATE_CLASS2)
- p_ch = NEGATE_CLASS;
- #endif
- /* Assign literal TRUE/FALSE because of "matched" comparison. */
- special = p_ch == NEGATE_CLASS? TRUE : FALSE;
- if (special) {
- /* Inverted character class. */
- p_ch = *++p;
- }
- prev_ch = 0;
- matched = FALSE;
- do {
- if (!p_ch)
- return ABORT_ALL;
- if (p_ch == '\\') {
- p_ch = *++p;
- if (!p_ch)
- return ABORT_ALL;
- if (t_ch == p_ch)
- matched = TRUE;
- } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
- p_ch = *++p;
- if (p_ch == '\\') {
- p_ch = *++p;
- if (!p_ch)
- return ABORT_ALL;
- }
- if (t_ch <= p_ch && t_ch >= prev_ch)
- matched = TRUE;
- p_ch = 0; /* This makes "prev_ch" get set to 0. */
- } else if (p_ch == '[' && p[1] == ':') {
- const uchar *s;
- int i;
- for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
- if (!p_ch)
- return ABORT_ALL;
- i = p - s - 1;
- if (i < 0 || p[-1] != ':') {
- /* Didn't find ":]", so treat like a normal set. */
- p = s - 2;
- p_ch = '[';
- if (t_ch == p_ch)
- matched = TRUE;
- continue;
- }
- if (CC_EQ(s,i, "alnum")) {
- if (ISALNUM(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "alpha")) {
- if (ISALPHA(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "blank")) {
- if (ISBLANK(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "cntrl")) {
- if (ISCNTRL(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "digit")) {
- if (ISDIGIT(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "graph")) {
- if (ISGRAPH(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "lower")) {
- if (ISLOWER(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "print")) {
- if (ISPRINT(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "punct")) {
- if (ISPUNCT(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "space")) {
- if (ISSPACE(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "upper")) {
- if (ISUPPER(t_ch))
- matched = TRUE;
- } else if (CC_EQ(s,i, "xdigit")) {
- if (ISXDIGIT(t_ch))
- matched = TRUE;
- } else /* malformed [:class:] string */
- return ABORT_ALL;
- p_ch = 0; /* This makes "prev_ch" get set to 0. */
- } else if (t_ch == p_ch)
- matched = TRUE;
- } while (prev_ch = p_ch, (p_ch = *++p) != ']');
- if (matched == special || t_ch == '/')
- return FALSE;
- continue;
- }
- }
- do {
- if (*text)
- return FALSE;
- } while ((text = *a++) != NULL);
- return TRUE;
- }
- /* Match literal string "s" against the a virtually-joined string consisting
- * of "text" and any strings in array "a". */
- static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
- {
- for ( ; *s != '\0'; text++, s++) {
- while (*text == '\0') {
- if ((text = *a++) == NULL)
- return FALSE;
- }
- if (*text != *s)
- return FALSE;
- }
- do {
- if (*text)
- return FALSE;
- } while ((text = *a++) != NULL);
- return TRUE;
- }
- /* Return the last "count" path elements from the concatenated string.
- * We return a string pointer to the start of the string, and update the
- * array pointer-pointer to point to any remaining string elements. */
- static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
- {
- const uchar*const *a = *a_ptr;
- const uchar*const *first_a = a;
- while (*a)
- a++;
- while (a != first_a) {
- const uchar *s = *--a;
- s += strlen((char*)s);
- while (--s >= *a) {
- if (*s == '/' && !--count) {
- *a_ptr = a+1;
- return s+1;
- }
- }
- }
- if (count == 1) {
- *a_ptr = a+1;
- return *a;
- }
- return NULL;
- }
- /* Match the "pattern" against the "text" string. */
- int wildmatch(const char *pattern, const char *text)
- {
- static const uchar *nomore[1]; /* A NULL pointer. */
- #ifdef WILD_TEST_ITERATIONS
- wildmatch_iteration_count = 0;
- #endif
- return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
- }
- /* Match the "pattern" against the forced-to-lower-case "text" string. */
- int iwildmatch(const char *pattern, const char *text)
- {
- static const uchar *nomore[1]; /* A NULL pointer. */
- int ret;
- #ifdef WILD_TEST_ITERATIONS
- wildmatch_iteration_count = 0;
- #endif
- force_lower_case = 1;
- ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
- force_lower_case = 0;
- return ret;
- }
- /* Match pattern "p" against the a virtually-joined string consisting
- * of all the pointers in array "texts" (which has a NULL pointer at the
- * end). The int "where" can be 0 (normal matching), > 0 (match only
- * the trailing N slash-separated filename components of "texts"), or < 0
- * (match the "pattern" at the start or after any slash in "texts"). */
- int wildmatch_array(const char *pattern, const char*const *texts, int where)
- {
- const uchar *p = (const uchar*)pattern;
- const uchar*const *a = (const uchar*const*)texts;
- const uchar *text;
- int matched;
- #ifdef WILD_TEST_ITERATIONS
- wildmatch_iteration_count = 0;
- #endif
- if (where > 0)
- text = trailing_N_elements(&a, where);
- else
- text = *a++;
- if (!text)
- return FALSE;
- if ((matched = dowild(p, text, a)) != TRUE && where < 0
- && matched != ABORT_ALL) {
- while (1) {
- if (*text == '\0') {
- if ((text = (uchar*)*a++) == NULL)
- return FALSE;
- continue;
- }
- if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
- && matched != ABORT_TO_STARSTAR)
- break;
- }
- }
- return matched == TRUE;
- }
- /* Match literal string "s" against the a virtually-joined string consisting
- * of all the pointers in array "texts" (which has a NULL pointer at the
- * end). The int "where" can be 0 (normal matching), or > 0 (match
- * only the trailing N slash-separated filename components of "texts"). */
- int litmatch_array(const char *string, const char*const *texts, int where)
- {
- const uchar *s = (const uchar*)string;
- const uchar*const *a = (const uchar* const*)texts;
- const uchar *text;
- if (where > 0)
- text = trailing_N_elements(&a, where);
- else
- text = *a++;
- if (!text)
- return FALSE;
- return doliteral(s, text, a) == TRUE;
- }
|