// // System.IO.SearchPattern.cs: Filename glob support. // // Author: // Dan Lewis (dihlewis@yahoo.co.uk) // // (C) 2002 // // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // namespace System.IO.Foo { // FIXME: there's a complication with this algorithm under windows. // the pattern '*.*' matches all files (i think . matches the extension), // whereas under UNIX it should only match files containing the '.' character. class SearchPattern { public SearchPattern (string pattern) : this (pattern, false) { } public SearchPattern (string pattern, bool ignore) { SetPattern (pattern, ignore); } public void SetPattern (string pattern, bool ignore) { this.ignore = ignore; Compile (pattern); } public bool IsMatch (string text) { return Match (ops, text, 0); } // private Op ops; // the compiled pattern bool ignore; // ignore case void Compile (string pattern) { if (pattern == null) throw new ArgumentException ("Invalid search pattern."); if (pattern == "*") { // common case ops = new Op (OpCode.True); return; } ops = null; int ptr = 0; Op last_op = null; while (ptr < pattern.Length) { Op op; switch (pattern [ptr]) { case '?': op = new Op (OpCode.AnyChar); ++ ptr; break; case '*': op = new Op (OpCode.AnyString); ++ ptr; break; default: op = new Op (OpCode.ExactString); int end = pattern.IndexOfAny (WildcardChars, ptr); if (end < 0) end = pattern.Length; op.Argument = pattern.Substring (ptr, end - ptr); if (ignore) op.Argument = op.Argument.ToLower (); ptr = end; break; } if (last_op == null) ops = op; else last_op.Next = op; last_op = op; } if (last_op == null) ops = new Op (OpCode.End); else last_op.Next = new Op (OpCode.End); } bool Match (Op op, string text, int ptr) { while (op != null) { switch (op.Code) { case OpCode.True: return true; case OpCode.End: if (ptr == text.Length) return true; return false; case OpCode.ExactString: int length = op.Argument.Length; if (ptr + length > text.Length) return false; string str = text.Substring (ptr, length); if (ignore) str = str.ToLower (); if (str != op.Argument) return false; ptr += length; break; case OpCode.AnyChar: if (++ ptr > text.Length) return false; break; case OpCode.AnyString: while (ptr <= text.Length) { if (Match (op.Next, text, ptr)) return true; ++ ptr; } return false; } op = op.Next; } return true; } // private static internal static readonly char [] WildcardChars = { '*', '?' }; class Op { public Op (OpCode code) { Code = code; Argument = null; Next = null; } public readonly OpCode Code; public string Argument; public Op Next; } enum OpCode { ExactString, // literal AnyChar, // ? AnyString, // * End, // end of pattern True // always succeeds }; } }