/source3/lib/ms_fnmatch.c

https://github.com/endisd/samba · C · 238 lines · 195 code · 13 blank · 30 comment · 34 complexity · 5ae742966d67728933042352fcebd80c MD5 · raw file

  1. /*
  2. Unix SMB/CIFS implementation.
  3. filename matching routine
  4. Copyright (C) Andrew Tridgell 1992-2004
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. /*
  17. This module was originally based on fnmatch.c copyright by the Free
  18. Software Foundation. It bears little (if any) resemblence to that
  19. code now
  20. */
  21. #include "includes.h"
  22. static int null_match(const smb_ucs2_t *p)
  23. {
  24. for (;*p;p++) {
  25. if (*p != UCS2_CHAR('*') &&
  26. *p != UCS2_CHAR('<') &&
  27. *p != UCS2_CHAR('"') &&
  28. *p != UCS2_CHAR('>')) return -1;
  29. }
  30. return 0;
  31. }
  32. /*
  33. the max_n structure is purely for efficiency, it doesn't contribute
  34. to the matching algorithm except by ensuring that the algorithm does
  35. not grow exponentially
  36. */
  37. struct max_n {
  38. const smb_ucs2_t *predot;
  39. const smb_ucs2_t *postdot;
  40. };
  41. /*
  42. p and n are the pattern and string being matched. The max_n array is
  43. an optimisation only. The ldot pointer is NULL if the string does
  44. not contain a '.', otherwise it points at the last dot in 'n'.
  45. */
  46. static int ms_fnmatch_core(const smb_ucs2_t *p, const smb_ucs2_t *n,
  47. struct max_n *max_n, const smb_ucs2_t *ldot,
  48. bool is_case_sensitive)
  49. {
  50. smb_ucs2_t c;
  51. int i;
  52. while ((c = *p++)) {
  53. switch (c) {
  54. /* a '*' matches zero or more characters of any type */
  55. case UCS2_CHAR('*'):
  56. if (max_n->predot && max_n->predot <= n) {
  57. return null_match(p);
  58. }
  59. for (i=0; n[i]; i++) {
  60. if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
  61. return 0;
  62. }
  63. }
  64. if (!max_n->predot || max_n->predot > n) max_n->predot = n;
  65. return null_match(p);
  66. /* a '<' matches zero or more characters of
  67. any type, but stops matching at the last
  68. '.' in the string. */
  69. case UCS2_CHAR('<'):
  70. if (max_n->predot && max_n->predot <= n) {
  71. return null_match(p);
  72. }
  73. if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
  74. return -1;
  75. }
  76. for (i=0; n[i]; i++) {
  77. if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
  78. if (n+i == ldot) {
  79. if (ms_fnmatch_core(p, n+i+1, max_n+1, ldot, is_case_sensitive) == 0) return 0;
  80. if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
  81. return -1;
  82. }
  83. }
  84. if (!max_n->predot || max_n->predot > n) max_n->predot = n;
  85. return null_match(p);
  86. /* a '?' matches any single character */
  87. case UCS2_CHAR('?'):
  88. if (! *n) {
  89. return -1;
  90. }
  91. n++;
  92. break;
  93. /* a '?' matches any single character */
  94. case UCS2_CHAR('>'):
  95. if (n[0] == UCS2_CHAR('.')) {
  96. if (! n[1] && null_match(p) == 0) {
  97. return 0;
  98. }
  99. break;
  100. }
  101. if (! *n) return null_match(p);
  102. n++;
  103. break;
  104. case UCS2_CHAR('"'):
  105. if (*n == 0 && null_match(p) == 0) {
  106. return 0;
  107. }
  108. if (*n != UCS2_CHAR('.')) return -1;
  109. n++;
  110. break;
  111. default:
  112. if (c != *n) {
  113. if (is_case_sensitive) {
  114. return -1;
  115. }
  116. if (toupper_w(c) != toupper_w(*n)) {
  117. return -1;
  118. }
  119. }
  120. n++;
  121. break;
  122. }
  123. }
  124. if (! *n) {
  125. return 0;
  126. }
  127. return -1;
  128. }
  129. int ms_fnmatch(const char *pattern, const char *string, bool translate_pattern,
  130. bool is_case_sensitive)
  131. {
  132. smb_ucs2_t *p = NULL;
  133. smb_ucs2_t *s = NULL;
  134. int ret, count, i;
  135. struct max_n *max_n = NULL;
  136. struct max_n *max_n_free = NULL;
  137. struct max_n one_max_n;
  138. size_t converted_size;
  139. if (ISDOTDOT(string)) {
  140. string = ".";
  141. }
  142. if (strpbrk(pattern, "<>*?\"") == NULL) {
  143. /* this is not just an optmisation - it is essential
  144. for LANMAN1 correctness */
  145. if (is_case_sensitive) {
  146. return strcmp(pattern, string);
  147. } else {
  148. return StrCaseCmp(pattern, string);
  149. }
  150. }
  151. if (!push_ucs2_talloc(talloc_tos(), &p, pattern, &converted_size)) {
  152. return -1;
  153. }
  154. if (!push_ucs2_talloc(talloc_tos(), &s, string, &converted_size)) {
  155. TALLOC_FREE(p);
  156. return -1;
  157. }
  158. if (translate_pattern) {
  159. /*
  160. for older negotiated protocols it is possible to
  161. translate the pattern to produce a "new style"
  162. pattern that exactly matches w2k behaviour
  163. */
  164. for (i=0;p[i];i++) {
  165. if (p[i] == UCS2_CHAR('?')) {
  166. p[i] = UCS2_CHAR('>');
  167. } else if (p[i] == UCS2_CHAR('.') &&
  168. (p[i+1] == UCS2_CHAR('?') ||
  169. p[i+1] == UCS2_CHAR('*') ||
  170. p[i+1] == 0)) {
  171. p[i] = UCS2_CHAR('"');
  172. } else if (p[i] == UCS2_CHAR('*') && p[i+1] == UCS2_CHAR('.')) {
  173. p[i] = UCS2_CHAR('<');
  174. }
  175. }
  176. }
  177. for (count=i=0;p[i];i++) {
  178. if (p[i] == UCS2_CHAR('*') || p[i] == UCS2_CHAR('<')) count++;
  179. }
  180. if (count != 0) {
  181. if (count == 1) {
  182. /*
  183. * We're doing this a LOT, so save the effort to allocate
  184. */
  185. ZERO_STRUCT(one_max_n);
  186. max_n = &one_max_n;
  187. }
  188. else {
  189. max_n = SMB_CALLOC_ARRAY(struct max_n, count);
  190. if (!max_n) {
  191. TALLOC_FREE(p);
  192. TALLOC_FREE(s);
  193. return -1;
  194. }
  195. max_n_free = max_n;
  196. }
  197. }
  198. ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.')), is_case_sensitive);
  199. SAFE_FREE(max_n_free);
  200. TALLOC_FREE(p);
  201. TALLOC_FREE(s);
  202. return ret;
  203. }
  204. /* a generic fnmatch function - uses for non-CIFS pattern matching */
  205. int gen_fnmatch(const char *pattern, const char *string)
  206. {
  207. return ms_fnmatch(pattern, string, true, False);
  208. }