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