THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
7.8KB

  1. /*
  2. ** Do shell-style pattern matching for ?, \, [], and * characters.
  3. ** It is 8bit clean.
  4. **
  5. ** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
  6. ** Rich $alz is now <rsalz@bbn.com>.
  7. **
  8. ** Modified by Wayne Davison to special-case '/' matching, to make '**'
  9. ** work differently than '*', and to fix the character-class code.
  10. */
  11. #include "cache.h"
  12. #include "wildmatch.h"
  13. typedef unsigned char uchar;
  14. /* What character marks an inverted character class? */
  15. #define NEGATE_CLASS '!'
  16. #define NEGATE_CLASS2 '^'
  17. #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
  18. && *(class) == *(litmatch) \
  19. && strncmp((char*)class, litmatch, len) == 0)
  20. #if defined STDC_HEADERS || !defined isascii
  21. # define ISASCII(c) 1
  22. #else
  23. # define ISASCII(c) isascii(c)
  24. #endif
  25. #ifdef isblank
  26. # define ISBLANK(c) (ISASCII(c) && isblank(c))
  27. #else
  28. # define ISBLANK(c) ((c) == ' ' || (c) == '\t')
  29. #endif
  30. #ifdef isgraph
  31. # define ISGRAPH(c) (ISASCII(c) && isgraph(c))
  32. #else
  33. # define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
  34. #endif
  35. #define ISPRINT(c) (ISASCII(c) && isprint(c))
  36. #define ISDIGIT(c) (ISASCII(c) && isdigit(c))
  37. #define ISALNUM(c) (ISASCII(c) && isalnum(c))
  38. #define ISALPHA(c) (ISASCII(c) && isalpha(c))
  39. #define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
  40. #define ISLOWER(c) (ISASCII(c) && islower(c))
  41. #define ISPUNCT(c) (ISASCII(c) && ispunct(c))
  42. #define ISSPACE(c) (ISASCII(c) && isspace(c))
  43. #define ISUPPER(c) (ISASCII(c) && isupper(c))
  44. #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
  45. /* Match pattern "p" against "text" */
  46. static int dowild(const uchar *p, const uchar *text, unsigned int flags)
  47. {
  48. uchar p_ch;
  49. const uchar *pattern = p;
  50. for ( ; (p_ch = *p) != '\0'; text++, p++) {
  51. int matched, match_slash, negated;
  52. uchar t_ch, prev_ch;
  53. if ((t_ch = *text) == '\0' && p_ch != '*')
  54. return WM_ABORT_ALL;
  55. if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
  56. t_ch = tolower(t_ch);
  57. if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
  58. p_ch = tolower(p_ch);
  59. switch (p_ch) {
  60. case '\\':
  61. /* Literal match with following character. Note that the test
  62. * in "default" handles the p[1] == '\0' failure case. */
  63. p_ch = *++p;
  64. /* FALLTHROUGH */
  65. default:
  66. if (t_ch != p_ch)
  67. return WM_NOMATCH;
  68. continue;
  69. case '?':
  70. /* Match anything but '/'. */
  71. if ((flags & WM_PATHNAME) && t_ch == '/')
  72. return WM_NOMATCH;
  73. continue;
  74. case '*':
  75. if (*++p == '*') {
  76. const uchar *prev_p = p - 2;
  77. while (*++p == '*') {}
  78. if (!(flags & WM_PATHNAME))
  79. /* without WM_PATHNAME, '*' == '**' */
  80. match_slash = 1;
  81. else if ((prev_p < pattern || *prev_p == '/') &&
  82. (*p == '\0' || *p == '/' ||
  83. (p[0] == '\\' && p[1] == '/'))) {
  84. /*
  85. * Assuming we already match 'foo/' and are at
  86. * <star star slash>, just assume it matches
  87. * nothing and go ahead match the rest of the
  88. * pattern with the remaining string. This
  89. * helps make foo/<*><*>/bar (<> because
  90. * otherwise it breaks C comment syntax) match
  91. * both foo/bar and foo/a/bar.
  92. */
  93. if (p[0] == '/' &&
  94. dowild(p + 1, text, flags) == WM_MATCH)
  95. return WM_MATCH;
  96. match_slash = 1;
  97. } else /* WM_PATHNAME is set */
  98. match_slash = 0;
  99. } else
  100. /* without WM_PATHNAME, '*' == '**' */
  101. match_slash = flags & WM_PATHNAME ? 0 : 1;
  102. if (*p == '\0') {
  103. /* Trailing "**" matches everything. Trailing "*" matches
  104. * only if there are no more slash characters. */
  105. if (!match_slash) {
  106. if (strchr((char*)text, '/') != NULL)
  107. return WM_NOMATCH;
  108. }
  109. return WM_MATCH;
  110. } else if (!match_slash && *p == '/') {
  111. /*
  112. * _one_ asterisk followed by a slash
  113. * with WM_PATHNAME matches the next
  114. * directory
  115. */
  116. const char *slash = strchr((char*)text, '/');
  117. if (!slash)
  118. return WM_NOMATCH;
  119. text = (const uchar*)slash;
  120. /* the slash is consumed by the top-level for loop */
  121. break;
  122. }
  123. while (1) {
  124. if (t_ch == '\0')
  125. break;
  126. /*
  127. * Try to advance faster when an asterisk is
  128. * followed by a literal. We know in this case
  129. * that the string before the literal
  130. * must belong to "*".
  131. * If match_slash is false, do not look past
  132. * the first slash as it cannot belong to '*'.
  133. */
  134. if (!is_glob_special(*p)) {
  135. p_ch = *p;
  136. if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
  137. p_ch = tolower(p_ch);
  138. while ((t_ch = *text) != '\0' &&
  139. (match_slash || t_ch != '/')) {
  140. if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
  141. t_ch = tolower(t_ch);
  142. if (t_ch == p_ch)
  143. break;
  144. text++;
  145. }
  146. if (t_ch != p_ch)
  147. return WM_NOMATCH;
  148. }
  149. if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
  150. if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
  151. return matched;
  152. } else if (!match_slash && t_ch == '/')
  153. return WM_ABORT_TO_STARSTAR;
  154. t_ch = *++text;
  155. }
  156. return WM_ABORT_ALL;
  157. case '[':
  158. p_ch = *++p;
  159. #ifdef NEGATE_CLASS2
  160. if (p_ch == NEGATE_CLASS2)
  161. p_ch = NEGATE_CLASS;
  162. #endif
  163. /* Assign literal 1/0 because of "matched" comparison. */
  164. negated = p_ch == NEGATE_CLASS ? 1 : 0;
  165. if (negated) {
  166. /* Inverted character class. */
  167. p_ch = *++p;
  168. }
  169. prev_ch = 0;
  170. matched = 0;
  171. do {
  172. if (!p_ch)
  173. return WM_ABORT_ALL;
  174. if (p_ch == '\\') {
  175. p_ch = *++p;
  176. if (!p_ch)
  177. return WM_ABORT_ALL;
  178. if (t_ch == p_ch)
  179. matched = 1;
  180. } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
  181. p_ch = *++p;
  182. if (p_ch == '\\') {
  183. p_ch = *++p;
  184. if (!p_ch)
  185. return WM_ABORT_ALL;
  186. }
  187. if (t_ch <= p_ch && t_ch >= prev_ch)
  188. matched = 1;
  189. else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
  190. uchar t_ch_upper = toupper(t_ch);
  191. if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
  192. matched = 1;
  193. }
  194. p_ch = 0; /* This makes "prev_ch" get set to 0. */
  195. } else if (p_ch == '[' && p[1] == ':') {
  196. const uchar *s;
  197. int i;
  198. for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
  199. if (!p_ch)
  200. return WM_ABORT_ALL;
  201. i = p - s - 1;
  202. if (i < 0 || p[-1] != ':') {
  203. /* Didn't find ":]", so treat like a normal set. */
  204. p = s - 2;
  205. p_ch = '[';
  206. if (t_ch == p_ch)
  207. matched = 1;
  208. continue;
  209. }
  210. if (CC_EQ(s,i, "alnum")) {
  211. if (ISALNUM(t_ch))
  212. matched = 1;
  213. } else if (CC_EQ(s,i, "alpha")) {
  214. if (ISALPHA(t_ch))
  215. matched = 1;
  216. } else if (CC_EQ(s,i, "blank")) {
  217. if (ISBLANK(t_ch))
  218. matched = 1;
  219. } else if (CC_EQ(s,i, "cntrl")) {
  220. if (ISCNTRL(t_ch))
  221. matched = 1;
  222. } else if (CC_EQ(s,i, "digit")) {
  223. if (ISDIGIT(t_ch))
  224. matched = 1;
  225. } else if (CC_EQ(s,i, "graph")) {
  226. if (ISGRAPH(t_ch))
  227. matched = 1;
  228. } else if (CC_EQ(s,i, "lower")) {
  229. if (ISLOWER(t_ch))
  230. matched = 1;
  231. } else if (CC_EQ(s,i, "print")) {
  232. if (ISPRINT(t_ch))
  233. matched = 1;
  234. } else if (CC_EQ(s,i, "punct")) {
  235. if (ISPUNCT(t_ch))
  236. matched = 1;
  237. } else if (CC_EQ(s,i, "space")) {
  238. if (ISSPACE(t_ch))
  239. matched = 1;
  240. } else if (CC_EQ(s,i, "upper")) {
  241. if (ISUPPER(t_ch))
  242. matched = 1;
  243. else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
  244. matched = 1;
  245. } else if (CC_EQ(s,i, "xdigit")) {
  246. if (ISXDIGIT(t_ch))
  247. matched = 1;
  248. } else /* malformed [:class:] string */
  249. return WM_ABORT_ALL;
  250. p_ch = 0; /* This makes "prev_ch" get set to 0. */
  251. } else if (t_ch == p_ch)
  252. matched = 1;
  253. } while (prev_ch = p_ch, (p_ch = *++p) != ']');
  254. if (matched == negated ||
  255. ((flags & WM_PATHNAME) && t_ch == '/'))
  256. return WM_NOMATCH;
  257. continue;
  258. }
  259. }
  260. return *text ? WM_NOMATCH : WM_MATCH;
  261. }
  262. /* Match the "pattern" against the "text" string. */
  263. int wildmatch(const char *pattern, const char *text, unsigned int flags)
  264. {
  265. return dowild((const uchar*)pattern, (const uchar*)text, flags);
  266. }