Merge pull request #2819 from BrzVlad/fix-major-log
[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 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                                 bool match = String.Compare (pattern, text, ignorecase) == 0;
56                                 if (match)
57                                         return true;
58                         }
59                                 
60                         // This is a special case for FSW. It needs to match e.g. subdir/file.txt
61                         // when the pattern is "file.txt"
62                         var fileName = Path.GetFileName (text);
63                         
64                         if (!hasWildcard)
65                                 return (String.Compare (pattern, fileName, ignorecase) == 0);
66                         
67                         
68                         return Match (ops, fileName, 0);
69                 }
70
71                 public bool IsMatch (string text)
72                 {
73                         return IsMatch (text, ignore);
74                 }
75
76                 public bool HasWildcard {
77                         get { return hasWildcard; }
78                 }
79                 // private
80
81                 Op ops;         // the compiled pattern
82                 bool ignore;    // ignore case
83                 bool hasWildcard;
84                 string pattern;
85
86                 private void Compile (string pattern)
87                 {
88                         if (pattern == null || pattern.IndexOfAny (InvalidChars) >= 0)
89                                 throw new ArgumentException ("Invalid search pattern: '" + pattern + "'");
90
91                         if (pattern == "*") {   // common case
92                                 ops = new Op (OpCode.True);
93                                 hasWildcard = true;
94                                 return;
95                         }
96
97                         ops = null;
98
99                         int ptr = 0;
100                         Op last_op = null;
101                         while (ptr < pattern.Length) {
102                                 Op op;
103                         
104                                 switch (pattern [ptr]) {
105                                 case '?':
106                                         op = new Op (OpCode.AnyChar);
107                                         ++ ptr;
108                                         hasWildcard = true;
109                                         break;
110
111                                 case '*':
112                                         op = new Op (OpCode.AnyString);
113                                         ++ ptr;
114                                         hasWildcard = true;
115                                         break;
116                                         
117                                 default:
118                                         op = new Op (OpCode.ExactString);
119                                         int end = pattern.IndexOfAny (WildcardChars, ptr);
120                                         if (end < 0)
121                                                 end = pattern.Length;
122
123                                         op.Argument = pattern.Substring (ptr, end - ptr);
124                                         if (ignore)
125                                                 op.Argument = op.Argument.ToLower ();
126
127                                         ptr = end;
128                                         break;
129                                 }
130
131                                 if (last_op == null)
132                                         ops = op;
133                                 else
134                                         last_op.Next = op;
135
136                                 last_op = op;
137                         }
138
139                         if (last_op == null)
140                                 ops = new Op (OpCode.End);
141                         else
142                                 last_op.Next = new Op (OpCode.End);
143                 }
144
145                 private bool Match (Op op, string text, int ptr)
146                 {
147                         while (op != null) {
148                                 switch (op.Code) {
149                                 case OpCode.True:
150                                         return true;
151
152                                 case OpCode.End:
153                                         if (ptr == text.Length)
154                                                 return true;
155
156                                         return false;
157                                 
158                                 case OpCode.ExactString:
159                                         int length = op.Argument.Length;
160                                         if (ptr + length > text.Length)
161                                                 return false;
162
163                                         string str = text.Substring (ptr, length);
164                                         if (ignore)
165                                                 str = str.ToLower ();
166
167                                         if (str != op.Argument)
168                                                 return false;
169
170                                         ptr += length;
171                                         break;
172
173                                 case OpCode.AnyChar:
174                                         if (++ ptr > text.Length)
175                                                 return false;
176                                         break;
177
178                                 case OpCode.AnyString:
179                                         while (ptr <= text.Length) {
180                                                 if (Match (op.Next, text, ptr))
181                                                         return true;
182
183                                                 ++ ptr;
184                                         }
185
186                                         return false;
187                                 }
188
189                                 op = op.Next;
190                         }
191
192                         return true;
193                 }
194
195                 // private static
196
197                 internal static readonly char [] WildcardChars = { '*', '?' };
198                 internal static readonly char [] InvalidChars = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
199
200                 private class Op {
201                         public Op (OpCode code)
202                         {
203                                 this.Code = code;
204                                 this.Argument = null;
205                                 this.Next = null;
206                         }
207                 
208                         public OpCode Code;
209                         public string Argument;
210                         public Op Next;
211                 }
212
213                 private enum OpCode {
214                         ExactString,            // literal
215                         AnyChar,                // ?
216                         AnyString,              // *
217                         End,                    // end of pattern
218                         True                    // always succeeds
219                 };
220         }
221 }