| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "rsync.h" |
| |
| |
| #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; |
| |
| |
| |
| 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 '\\': |
| |
| |
| p_ch = *++p; |
| |
| default: |
| if (t_ch != p_ch) |
| return FALSE; |
| continue; |
| case '?': |
| |
| if (t_ch == '/') |
| return FALSE; |
| continue; |
| case '*': |
| if (*++p == '*') { |
| while (*++p == '*') {} |
| special = TRUE; |
| } else |
| special = FALSE; |
| if (*p == '\0') { |
| |
| |
| 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 |
| |
| special = p_ch == NEGATE_CLASS? TRUE : FALSE; |
| if (special) { |
| |
| 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; |
| } else if (p_ch == '[' && p[1] == ':') { |
| const uchar *s; |
| int i; |
| for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} |
| if (!p_ch) |
| return ABORT_ALL; |
| i = p - s - 1; |
| if (i < 0 || p[-1] != ':') { |
| |
| 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 |
| return ABORT_ALL; |
| p_ch = 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; |
| } |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| int wildmatch(const char *pattern, const char *text) |
| { |
| static const uchar *nomore[1]; |
| #ifdef WILD_TEST_ITERATIONS |
| wildmatch_iteration_count = 0; |
| #endif |
| return dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE; |
| } |
| |
| |
| int iwildmatch(const char *pattern, const char *text) |
| { |
| static const uchar *nomore[1]; |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| 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; |
| } |