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