lib/util: Avoid a talloc in ms_fnmatch_protocol
[Samba.git] / lib / util / ms_fnmatch.c
blobc0f61ab04e75da6880918d21d5aab82fe5b82589
1 /*
2 Unix SMB/CIFS implementation.
3 filename matching routine
4 Copyright (C) Andrew Tridgell 1992-2004
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 3 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 This module was originally based on fnmatch.c copyright by the Free
22 Software Foundation. It bears little (if any) resemblence to that
23 code now
24 */
26 /**
27 * @file
28 * @brief MS-style Filename matching
31 #include "includes.h"
32 #include "libcli/smb/smb_constants.h"
34 static int null_match(const char *p)
36 for (;*p;p++) {
37 if (*p != '*' &&
38 *p != '<' &&
39 *p != '"' &&
40 *p != '>') return -1;
42 return 0;
46 the max_n structure is purely for efficiency, it doesn't contribute
47 to the matching algorithm except by ensuring that the algorithm does
48 not grow exponentially
50 struct max_n {
51 const char *predot;
52 const char *postdot;
57 p and n are the pattern and string being matched. The max_n array is
58 an optimisation only. The ldot pointer is NULL if the string does
59 not contain a '.', otherwise it points at the last dot in 'n'.
61 static int ms_fnmatch_core(const char *p, const char *n,
62 struct max_n *max_n, const char *ldot,
63 bool is_case_sensitive)
65 codepoint_t c, c2;
66 int i;
67 size_t size, size_n;
69 while ((c = next_codepoint(p, &size))) {
70 p += size;
72 switch (c) {
73 case '*':
74 /* a '*' matches zero or more characters of any type */
75 if (max_n->predot && max_n->predot <= n) {
76 return null_match(p);
78 for (i=0; n[i]; i += size_n) {
79 next_codepoint(n+i, &size_n);
80 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
81 return 0;
84 if (!max_n->predot || max_n->predot > n) max_n->predot = n;
85 return null_match(p);
87 case '<':
88 /* a '<' matches zero or more characters of
89 any type, but stops matching at the last
90 '.' in the string. */
91 if (max_n->predot && max_n->predot <= n) {
92 return null_match(p);
94 if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
95 return -1;
97 for (i=0; n[i]; i += size_n) {
98 next_codepoint(n+i, &size_n);
99 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
100 if (n+i == ldot) {
101 if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
102 if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
103 return -1;
106 if (!max_n->predot || max_n->predot > n) max_n->predot = n;
107 return null_match(p);
109 case '?':
110 /* a '?' matches any single character */
111 if (! *n) {
112 return -1;
114 next_codepoint(n, &size_n);
115 n += size_n;
116 break;
118 case '>':
119 /* a '?' matches any single character, but
120 treats '.' specially */
121 if (n[0] == '.') {
122 if (! n[1] && null_match(p) == 0) {
123 return 0;
125 break;
127 if (! *n) return null_match(p);
128 next_codepoint(n, &size_n);
129 n += size_n;
130 break;
132 case '"':
133 /* a bit like a soft '.' */
134 if (*n == 0 && null_match(p) == 0) {
135 return 0;
137 if (*n != '.') return -1;
138 next_codepoint(n, &size_n);
139 n += size_n;
140 break;
142 default:
143 c2 = next_codepoint(n, &size_n);
144 if (c != c2) {
145 if (is_case_sensitive) {
146 return -1;
148 if (codepoint_cmpi(c, c2) != 0) {
149 return -1;
152 n += size_n;
153 break;
157 if (! *n) {
158 return 0;
161 return -1;
164 int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
165 bool is_case_sensitive)
167 int ret, count, i;
169 if (strcmp(string, "..") == 0) {
170 string = ".";
173 if (strpbrk(pattern, "<>*?\"") == NULL) {
174 /* this is not just an optimisation - it is essential
175 for LANMAN1 correctness */
176 return strcasecmp_m(pattern, string);
179 if (protocol <= PROTOCOL_LANMAN2) {
180 char *p = talloc_strdup(NULL, pattern);
181 if (p == NULL) {
182 return -1;
185 for older negotiated protocols it is possible to
186 translate the pattern to produce a "new style"
187 pattern that exactly matches w2k behaviour
189 for (i=0;p[i];i++) {
190 if (p[i] == '?') {
191 p[i] = '>';
192 } else if (p[i] == '.' &&
193 (p[i+1] == '?' ||
194 p[i+1] == '*' ||
195 p[i+1] == 0)) {
196 p[i] = '"';
197 } else if (p[i] == '*' &&
198 p[i+1] == '.') {
199 p[i] = '<';
202 ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1,
203 is_case_sensitive);
204 talloc_free(p);
205 return ret;
208 for (count=i=0;pattern[i];i++) {
209 if (pattern[i] == '*' || pattern[i] == '<') count++;
213 struct max_n max_n[count];
215 memset(max_n, 0, sizeof(struct max_n) * count);
217 ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
218 is_case_sensitive);
221 return ret;
225 /** a generic fnmatch function - uses for non-CIFS pattern matching */
226 int gen_fnmatch(const char *pattern, const char *string)
228 return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);