root/lib/util/ms_fnmatch.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. null_match
  2. ms_fnmatch_core
  3. ms_fnmatch
  4. gen_fnmatch

   1 /* 
   2    Unix SMB/CIFS implementation.
   3    filename matching routine
   4    Copyright (C) Andrew Tridgell 1992-2004
   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 3 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, see <http://www.gnu.org/licenses/>.  
  18 */
  19 
  20 /*
  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 */  
  25 
  26 /**
  27  * @file
  28  * @brief MS-style Filename matching
  29  */
  30 
  31 #include "includes.h"
  32 
  33 static int null_match(const char *p)
     /* [<][>][^][v][top][bottom][index][help] */
  34 {
  35         for (;*p;p++) {
  36                 if (*p != '*' &&
  37                     *p != '<' &&
  38                     *p != '"' &&
  39                     *p != '>') return -1;
  40         }
  41         return 0;
  42 }
  43 
  44 /*
  45   the max_n structure is purely for efficiency, it doesn't contribute
  46   to the matching algorithm except by ensuring that the algorithm does
  47   not grow exponentially
  48 */
  49 struct max_n {
  50         const char *predot;
  51         const char *postdot;
  52 };
  53 
  54 
  55 /*
  56   p and n are the pattern and string being matched. The max_n array is
  57   an optimisation only. The ldot pointer is NULL if the string does
  58   not contain a '.', otherwise it points at the last dot in 'n'.
  59 */
  60 static int ms_fnmatch_core(const char *p, const char *n, 
     /* [<][>][^][v][top][bottom][index][help] */
  61                            struct max_n *max_n, const char *ldot)
  62 {
  63         codepoint_t c, c2;
  64         int i;
  65         size_t size, size_n;
  66 
  67         while ((c = next_codepoint(p, &size))) {
  68                 p += size;
  69 
  70                 switch (c) {
  71                 case '*':
  72                         /* a '*' matches zero or more characters of any type */
  73                         if (max_n->predot && max_n->predot <= n) {
  74                                 return null_match(p);
  75                         }
  76                         for (i=0; n[i]; i += size_n) {
  77                                 next_codepoint(n+i, &size_n);
  78                                 if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) {
  79                                         return 0;
  80                                 }
  81                         }
  82                         if (!max_n->predot || max_n->predot > n) max_n->predot = n;
  83                         return null_match(p);
  84 
  85                 case '<':
  86                         /* a '<' matches zero or more characters of
  87                            any type, but stops matching at the last
  88                            '.' in the string. */
  89                         if (max_n->predot && max_n->predot <= n) {
  90                                 return null_match(p);
  91                         }
  92                         if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
  93                                 return -1;
  94                         }
  95                         for (i=0; n[i]; i += size_n) {
  96                                 next_codepoint(n+i, &size_n);
  97                                 if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) return 0;
  98                                 if (n+i == ldot) {
  99                                         if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot) == 0) return 0;
 100                                         if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
 101                                         return -1;
 102                                 }
 103                         }
 104                         if (!max_n->predot || max_n->predot > n) max_n->predot = n;
 105                         return null_match(p);
 106 
 107                 case '?':
 108                         /* a '?' matches any single character */
 109                         if (! *n) {
 110                                 return -1;
 111                         }
 112                         next_codepoint(n, &size_n);
 113                         n += size_n;
 114                         break;
 115 
 116                 case '>':
 117                         /* a '?' matches any single character, but
 118                            treats '.' specially */
 119                         if (n[0] == '.') {
 120                                 if (! n[1] && null_match(p) == 0) {
 121                                         return 0;
 122                                 }
 123                                 break;
 124                         }
 125                         if (! *n) return null_match(p);
 126                         next_codepoint(n, &size_n);
 127                         n += size_n;
 128                         break;
 129 
 130                 case '"':
 131                         /* a bit like a soft '.' */
 132                         if (*n == 0 && null_match(p) == 0) {
 133                                 return 0;
 134                         }
 135                         if (*n != '.') return -1;
 136                         next_codepoint(n, &size_n);
 137                         n += size_n;
 138                         break;
 139 
 140                 default:
 141                         c2 = next_codepoint(n, &size_n);
 142                         if (c != c2 && codepoint_cmpi(c, c2) != 0) {
 143                                 return -1;
 144                         }
 145                         n += size_n;
 146                         break;
 147                 }
 148         }
 149         
 150         if (! *n) {
 151                 return 0;
 152         }
 153         
 154         return -1;
 155 }
 156 
 157 int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol)
     /* [<][>][^][v][top][bottom][index][help] */
 158 {
 159         int ret, count, i;
 160         struct max_n *max_n = NULL;
 161 
 162         if (strcmp(string, "..") == 0) {
 163                 string = ".";
 164         }
 165 
 166         if (strpbrk(pattern, "<>*?\"") == NULL) {
 167                 /* this is not just an optimisation - it is essential
 168                    for LANMAN1 correctness */
 169                 return strcasecmp_m(pattern, string);
 170         }
 171 
 172         if (protocol <= PROTOCOL_LANMAN2) {
 173                 char *p = talloc_strdup(NULL, pattern);
 174                 if (p == NULL) {
 175                         return -1;
 176                 }
 177                 /*
 178                   for older negotiated protocols it is possible to
 179                   translate the pattern to produce a "new style"
 180                   pattern that exactly matches w2k behaviour
 181                 */
 182                 for (i=0;p[i];i++) {
 183                         if (p[i] == '?') {
 184                                 p[i] = '>';
 185                         } else if (p[i] == '.' && 
 186                                    (p[i+1] == '?' || 
 187                                     p[i+1] == '*' ||
 188                                     p[i+1] == 0)) {
 189                                 p[i] = '"';
 190                         } else if (p[i] == '*' && 
 191                                    p[i+1] == '.') {
 192                                 p[i] = '<';
 193                         }
 194                 }
 195                 ret = ms_fnmatch(p, string, PROTOCOL_NT1);
 196                 talloc_free(p);
 197                 return ret;
 198         }
 199 
 200         for (count=i=0;pattern[i];i++) {
 201                 if (pattern[i] == '*' || pattern[i] == '<') count++;
 202         }
 203 
 204         max_n = talloc_zero_array(NULL, struct max_n, count);
 205         if (max_n == NULL) {
 206                 return -1;
 207         }
 208 
 209         ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'));
 210 
 211         talloc_free(max_n);
 212 
 213         return ret;
 214 }
 215 
 216 
 217 /** a generic fnmatch function - uses for non-CIFS pattern matching */
 218 int gen_fnmatch(const char *pattern, const char *string)
     /* [<][>][^][v][top][bottom][index][help] */
 219 {
 220         return ms_fnmatch(pattern, string, PROTOCOL_NT1);
 221 }

/* [<][>][^][v][top][bottom][index][help] */