s3/lib/ctdbd_conn: assert hdr following read/recv
[Samba.git] / lib / util / ms_fnmatch.c
blob5e05312f25afd6299e3441b6ce68d45f6fe9f71e
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 != NULL && max_n->predot &&
76 max_n->predot <= n) {
77 return null_match(p);
79 for (i=0; n[i]; i += size_n) {
80 next_codepoint(n+i, &size_n);
81 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
82 return 0;
85 if (max_n != NULL && (!max_n->predot ||
86 max_n->predot > n)) {
87 max_n->predot = n;
89 return null_match(p);
91 case '<':
92 /* a '<' matches zero or more characters of
93 any type, but stops matching at the last
94 '.' in the string. */
95 if (max_n != NULL && max_n->predot &&
96 max_n->predot <= n) {
97 return null_match(p);
99 if (max_n != NULL && max_n->postdot &&
100 max_n->postdot <= n && n <= ldot) {
101 return -1;
103 for (i=0; n[i]; i += size_n) {
104 next_codepoint(n+i, &size_n);
105 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
106 if (n+i == ldot) {
107 if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
108 if (max_n != NULL) {
109 if (!max_n->postdot ||
110 max_n->postdot > n) {
111 max_n->postdot = n;
114 return -1;
117 if (max_n != NULL && (!max_n->predot ||
118 max_n->predot > n)) {
119 max_n->predot = n;
121 return null_match(p);
123 case '?':
124 /* a '?' matches any single character */
125 if (! *n) {
126 return -1;
128 next_codepoint(n, &size_n);
129 n += size_n;
130 break;
132 case '>':
133 /* a '?' matches any single character, but
134 treats '.' specially */
135 if (n[0] == '.') {
136 if (! n[1] && null_match(p) == 0) {
137 return 0;
139 break;
141 if (! *n) return null_match(p);
142 next_codepoint(n, &size_n);
143 n += size_n;
144 break;
146 case '"':
147 /* a bit like a soft '.' */
148 if (*n == 0 && null_match(p) == 0) {
149 return 0;
151 if (*n != '.') return -1;
152 next_codepoint(n, &size_n);
153 n += size_n;
154 break;
156 default:
157 c2 = next_codepoint(n, &size_n);
158 if (c != c2) {
159 if (is_case_sensitive) {
160 return -1;
162 if (codepoint_cmpi(c, c2) != 0) {
163 return -1;
166 n += size_n;
167 break;
171 if (! *n) {
172 return 0;
175 return -1;
178 int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
179 bool is_case_sensitive)
181 int ret = -1;
182 size_t count, i;
184 if (strcmp(string, "..") == 0) {
185 string = ".";
188 if (strpbrk(pattern, "<>*?\"") == NULL) {
189 /* this is not just an optimisation - it is essential
190 for LANMAN1 correctness */
191 return strcasecmp_m(pattern, string);
194 if (protocol <= PROTOCOL_LANMAN2) {
195 char *p = talloc_strdup(NULL, pattern);
196 if (p == NULL) {
197 return -1;
200 for older negotiated protocols it is possible to
201 translate the pattern to produce a "new style"
202 pattern that exactly matches w2k behaviour
204 for (i=0;p[i];i++) {
205 if (p[i] == '?') {
206 p[i] = '>';
207 } else if (p[i] == '.' &&
208 (p[i+1] == '?' ||
209 p[i+1] == '*' ||
210 p[i+1] == 0)) {
211 p[i] = '"';
212 } else if (p[i] == '*' &&
213 p[i+1] == '.') {
214 p[i] = '<';
217 ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1,
218 is_case_sensitive);
219 talloc_free(p);
220 return ret;
223 for (count=i=0;pattern[i];i++) {
224 if (pattern[i] == '*' || pattern[i] == '<') count++;
227 /* If the pattern includes '*' or '<' */
228 if (count >= 1) {
229 struct max_n max_n[count];
231 memset(max_n, 0, sizeof(struct max_n) * count);
233 ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
234 is_case_sensitive);
235 } else {
236 ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
237 is_case_sensitive);
240 return ret;
244 /** a generic fnmatch function - uses for non-CIFS pattern matching */
245 int gen_fnmatch(const char *pattern, const char *string)
247 return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);