1 // 2 // System.IO.SearchPattern.cs: Filename glob support. 3 // 4 // Author: 5 // Dan Lewis (dihlewis@yahoo.co.uk) 6 // 7 // (C) 2002 8 // 9 10 // 11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com) 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining 14 // a copy of this software and associated documentation files (the 15 // "Software"), to deal in the Software without restriction, including 16 // without limitation the rights to use, copy, modify, merge, publish, 17 // distribute, sublicense, and/or sell copies of the Software, and to 18 // permit persons to whom the Software is furnished to do so, subject to 19 // the following conditions: 20 // 21 // The above copyright notice and this permission notice shall be 22 // included in all copies or substantial portions of the Software. 23 // 24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 // 32 33 using System; 34 35 namespace System.Web.Util { 36 37 // FIXME: there's a complication with this algorithm under windows. 38 // the pattern '*.*' matches all files (i think . matches the extension), 39 // whereas under UNIX it should only match files containing the '.' character. 40 class SearchPattern 41 { SearchPattern(string pattern)42 public SearchPattern (string pattern) : this (pattern, false) { } 43 SearchPattern(string pattern, bool ignore)44 public SearchPattern (string pattern, bool ignore) 45 { 46 SetPattern (pattern, ignore); 47 } 48 SetPattern(string pattern, bool ignore)49 public void SetPattern (string pattern, bool ignore) 50 { 51 this.ignore = ignore; 52 Compile (pattern); 53 } 54 IsMatch(string text)55 public bool IsMatch (string text) 56 { 57 return Match (ops, text, 0); 58 } 59 60 // private 61 62 private Op ops; // the compiled pattern 63 private bool ignore; // ignore case 64 Compile(string pattern)65 private void Compile (string pattern) 66 { 67 if (pattern == null) 68 throw new ArgumentException ("Invalid search pattern."); 69 70 if (pattern == "*") { // common case 71 ops = new Op (OpCode.True); 72 return; 73 } 74 75 ops = null; 76 77 int ptr = 0; 78 Op last_op = null; 79 while (ptr < pattern.Length) { 80 Op op; 81 82 switch (pattern [ptr]) { 83 case '?': 84 op = new Op (OpCode.AnyChar); 85 ++ ptr; 86 break; 87 88 case '*': 89 op = new Op (OpCode.AnyString); 90 ++ ptr; 91 break; 92 93 default: 94 op = new Op (OpCode.ExactString); 95 int end = pattern.IndexOfAny (WildcardChars, ptr); 96 if (end < 0) 97 end = pattern.Length; 98 99 op.Argument = pattern.Substring (ptr, end - ptr); 100 if (ignore) 101 op.Argument = op.Argument.ToLower (); 102 103 ptr = end; 104 break; 105 } 106 107 if (last_op == null) 108 ops = op; 109 else 110 last_op.Next = op; 111 112 last_op = op; 113 } 114 115 if (last_op == null) 116 ops = new Op (OpCode.End); 117 else 118 last_op.Next = new Op (OpCode.End); 119 } 120 Match(Op op, string text, int ptr)121 private bool Match (Op op, string text, int ptr) 122 { 123 while (op != null) { 124 switch (op.Code) { 125 case OpCode.True: 126 return true; 127 128 case OpCode.End: 129 if (ptr == text.Length) 130 return true; 131 132 return false; 133 134 case OpCode.ExactString: 135 int length = op.Argument.Length; 136 if (ptr + length > text.Length) 137 return false; 138 139 string str = text.Substring (ptr, length); 140 if (ignore) 141 str = str.ToLower (); 142 143 if (str != op.Argument) 144 return false; 145 146 ptr += length; 147 break; 148 149 case OpCode.AnyChar: 150 if (++ ptr > text.Length) 151 return false; 152 break; 153 154 case OpCode.AnyString: 155 while (ptr <= text.Length) { 156 if (Match (op.Next, text, ptr)) 157 return true; 158 159 ++ ptr; 160 } 161 162 return false; 163 } 164 165 op = op.Next; 166 } 167 168 return true; 169 } 170 171 // private static 172 173 internal static readonly char [] WildcardChars = { '*', '?' }; 174 175 private class Op { Op(OpCode code)176 public Op (OpCode code) 177 { 178 this.Code = code; 179 this.Argument = null; 180 this.Next = null; 181 } 182 183 public OpCode Code; 184 public string Argument; 185 public Op Next; 186 } 187 188 private enum OpCode { 189 ExactString, // literal 190 AnyChar, // ? 191 AnyString, // * 192 End, // end of pattern 193 True // always succeeds 194 }; 195 } 196 } 197