/* Unix SMB/CIFS implementation. filename matching routine Copyright (C) Andrew Tridgell 1992-2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This module was originally based on fnmatch.c copyright by the Free Software Foundation. It bears little (if any) resemblence to that code now */ #include "includes.h" static int null_match(const smb_ucs2_t *p) { for (;*p;p++) { if (*p != UCS2_CHAR('*') && *p != UCS2_CHAR('<') && *p != UCS2_CHAR('"') && *p != UCS2_CHAR('>')) return -1; } return 0; } /* the max_n structure is purely for efficiency, it doesn't contribute to the matching algorithm except by ensuring that the algorithm does not grow exponentially */ struct max_n { const smb_ucs2_t *predot; const smb_ucs2_t *postdot; }; /* p and n are the pattern and string being matched. The max_n array is an optimisation only. The ldot pointer is NULL if the string does not contain a '.', otherwise it points at the last dot in 'n'. */ static int ms_fnmatch_core(const smb_ucs2_t *p, const smb_ucs2_t *n, struct max_n *max_n, const smb_ucs2_t *ldot) { smb_ucs2_t c; int i; while ((c = *p++)) { switch (c) { /* a '*' matches zero or more characters of any type */ case UCS2_CHAR('*'): if (max_n->predot && max_n->predot <= n) { return null_match(p); } for (i=0; n[i]; i++) { if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) { return 0; } } if (!max_n->predot || max_n->predot > n) max_n->predot = n; return null_match(p); /* a '<' matches zero or more characters of any type, but stops matching at the last '.' in the string. */ case UCS2_CHAR('<'): if (max_n->predot && max_n->predot <= n) { return null_match(p); } if (max_n->postdot && max_n->postdot <= n && n <= ldot) { return -1; } for (i=0; n[i]; i++) { if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) return 0; if (n+i == ldot) { if (ms_fnmatch_core(p, n+i+1, max_n+1, ldot) == 0) return 0; if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n; return -1; } } if (!max_n->predot || max_n->predot > n) max_n->predot = n; return null_match(p); /* a '?' matches any single character */ case UCS2_CHAR('?'): if (! *n) { return -1; } n++; break; /* a '?' matches any single character */ case UCS2_CHAR('>'): if (n[0] == UCS2_CHAR('.')) { if (! n[1] && null_match(p) == 0) { return 0; } break; } if (! *n) return null_match(p); n++; break; case UCS2_CHAR('"'): if (*n == 0 && null_match(p) == 0) { return 0; } if (*n != UCS2_CHAR('.')) return -1; n++; break; default: if (c != *n && toupper_w(c) != toupper_w(*n)) { return -1; } n++; break; } } if (! *n) { return 0; } return -1; } int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol) { wpstring p, s; int ret, count, i; struct max_n *max_n = NULL; if (strcmp(string, "..") == 0) { string = "."; } pstrcpy_wa(p, pattern); pstrcpy_wa(s, string); if (protocol <= PROTOCOL_LANMAN2) { /* for older negotiated protocols it is possible to translate the pattern to produce a "new style" pattern that exactly matches w2k behaviour */ for (i=0;p[i];i++) { if (p[i] == UCS2_CHAR('?')) { p[i] = UCS2_CHAR('>'); } else if (p[i] == UCS2_CHAR('.') && (p[i+1] == UCS2_CHAR('?') || p[i+1] == UCS2_CHAR('*') || p[i+1] == 0)) { p[i] = UCS2_CHAR('"'); } else if (p[i] == UCS2_CHAR('*') && p[i+1] == UCS2_CHAR('.')) { p[i] = UCS2_CHAR('<'); } } } for (count=i=0;p[i];i++) { if (p[i] == UCS2_CHAR('*') || p[i] == UCS2_CHAR('<')) count++; } max_n = talloc_array_p(NULL, struct max_n, count); if (!max_n) { return -1; } memset(max_n, 0, sizeof(struct max_n) * count); ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.'))); talloc_free(max_n); return ret; } /* a generic fnmatch function - uses for non-CIFS pattern matching */ int gen_fnmatch(const char *pattern, const char *string) { return ms_fnmatch(pattern, string, PROTOCOL_NT1); }