1/* 2 Unix SMB/CIFS implementation. 3 filename matching routine 4 Copyright (C) Andrew Tridgell 1992-1998 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ 19 20/* 21 This module was originally based on fnmatch.c copyright by the Free 22 Software Foundation. It bears little resemblence to that code now 23*/ 24 25 26#if FNMATCH_TEST 27#include <stdio.h> 28#include <stdlib.h> 29#else 30#include "includes.h" 31#endif 32 33/* 34 bugger. we need a separate wildcard routine for older versions 35 of the protocol. This is not yet perfect, but its a lot 36 better than what we had */ 37static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern, 38 const smb_ucs2_t *string, 39 BOOL case_sensitive) 40{ 41 const smb_ucs2_t *p = pattern, *n = string; 42 smb_ucs2_t c; 43 44 if (strcmp_wa(p, "?")==0 && strcmp_wa(n, ".")) goto match; 45 46 while ((c = *p++)) { 47 switch (c) { 48 case UCS2_CHAR('.'): 49 if (! *n) goto next; 50 if (*n != UCS2_CHAR('.')) goto nomatch; 51 n++; 52 break; 53 54 case UCS2_CHAR('?'): 55 if (! *n) goto next; 56 if ((*n == UCS2_CHAR('.') && 57 n[1] != UCS2_CHAR('.')) || ! *n) 58 goto next; 59 n++; 60 break; 61 62 case UCS2_CHAR('>'): 63 if (! *n) goto next; 64 if (n[0] == UCS2_CHAR('.')) { 65 if (! n[1] && ms_fnmatch_lanman_core(p, n+1, case_sensitive) == 0) goto match; 66 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match; 67 goto nomatch; 68 } 69 n++; 70 break; 71 72 case UCS2_CHAR('*'): 73 if (! *n) goto next; 74 if (! *p) goto match; 75 for (; *n; n++) { 76 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match; 77 } 78 break; 79 80 case UCS2_CHAR('<'): 81 for (; *n; n++) { 82 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match; 83 if (*n == UCS2_CHAR('.') && 84 !strchr_w(n+1,UCS2_CHAR('.'))) { 85 n++; 86 break; 87 } 88 } 89 break; 90 91 case UCS2_CHAR('"'): 92 if (*n == 0 && ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match; 93 if (*n != UCS2_CHAR('.')) goto nomatch; 94 n++; 95 break; 96 97 default: 98 if (case_sensitive) { 99 if (c != *n) goto nomatch; 100 } else { 101 if (tolower_w(c) != tolower_w(*n)) goto nomatch; 102 } 103 n++; 104 } 105 } 106 107 if (! *n) goto match; 108 109 nomatch: 110 /* 111 if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string); 112 */ 113 return -1; 114 115next: 116 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match; 117 goto nomatch; 118 119 match: 120 /* 121 if (verbose) printf("MATCH pattern=[%s] string=[%s]\n", pattern, string); 122 */ 123 return 0; 124} 125 126static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern, 127 const smb_ucs2_t *string, BOOL case_sensitive) 128{ 129 if (!strpbrk_wa(pattern, "?*<>\"")) { 130 smb_ucs2_t s[] = {UCS2_CHAR('.'), 0}; 131 if (strcmp_wa(string,"..") == 0) string = s; 132 return strcasecmp_w(pattern, string); 133 } 134 135 if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) { 136 smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0}; 137 smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0}; 138 return ms_fnmatch_lanman_core(pattern, dotdot, case_sensitive) && 139 ms_fnmatch_lanman_core(pattern, dot, case_sensitive); 140 } 141 142 return ms_fnmatch_lanman_core(pattern, string, case_sensitive); 143} 144 145 146/* the following function was derived using the masktest utility - 147 after years of effort we finally have a perfect MS wildcard 148 matching routine! 149 150 NOTE: this matches only filenames with no directory component 151 152 Returns 0 on match, -1 on fail. 153*/ 154static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string, 155 int protocol, BOOL case_sensitive) 156{ 157 const smb_ucs2_t *p = pattern, *n = string; 158 smb_ucs2_t c; 159 160 if (protocol <= PROTOCOL_LANMAN2) { 161 return ms_fnmatch_lanman1(pattern, string, case_sensitive); 162 } 163 164 while ((c = *p++)) { 165 switch (c) { 166 case UCS2_CHAR('?'): 167 if (! *n) return -1; 168 n++; 169 break; 170 171 case UCS2_CHAR('>'): 172 if (n[0] == UCS2_CHAR('.')) { 173 if (! n[1] && ms_fnmatch_w(p, n+1, protocol, case_sensitive) == 0) return 0; 174 if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0; 175 return -1; 176 } 177 if (! *n) return ms_fnmatch_w(p, n, protocol, case_sensitive); 178 n++; 179 break; 180 181 case UCS2_CHAR('*'): 182 for (; *n; n++) { 183 if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0; 184 } 185 break; 186 187 case UCS2_CHAR('<'): 188 for (; *n; n++) { 189 if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0; 190 if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) { 191 n++; 192 break; 193 } 194 } 195 break; 196 197 case UCS2_CHAR('"'): 198 if (*n == 0 && ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0; 199 if (*n != UCS2_CHAR('.')) return -1; 200 n++; 201 break; 202 203 default: 204 if (case_sensitive) { 205 if (c != *n) return -1; 206 } else { 207 if (tolower_w(c) != tolower_w(*n)) return -1; 208 } 209 n++; 210 } 211 } 212 213 if (! *n) return 0; 214 215 return -1; 216} 217 218int ms_fnmatch(const char *pattern, const char *string, int protocol, 219 BOOL case_senstive) 220{ 221 wpstring buffer_pattern, buffer_string; 222 int ret; 223 size_t size; 224 225 size = push_ucs2(NULL, buffer_pattern, pattern, sizeof(buffer_pattern), STR_TERMINATE); 226 if (size == (size_t)-1) { 227 return -1; 228 /* Not quite the right answer, but finding the right one 229 under this failure case is expensive, and it's pretty close */ 230 } 231 232 size = push_ucs2(NULL, buffer_string, string, sizeof(buffer_string), STR_TERMINATE); 233 if (size == (size_t)-1) { 234 return -1; 235 /* Not quite the right answer, but finding the right one 236 under this failure case is expensive, and it's pretty close */ 237 } 238 239 ret = ms_fnmatch_w(buffer_pattern, buffer_string, protocol, case_senstive); 240 DEBUG(10,("ms_fnmatch(%s,%s) -> %d\n", pattern, string, ret)); 241 242 return ret; 243} 244 245/* a generic fnmatch function - uses for non-CIFS pattern matching */ 246int gen_fnmatch(const char *pattern, const char *string) 247{ 248 return ms_fnmatch(pattern, string, PROTOCOL_NT1, True); 249} 250