2004-04-02 Dick Porter <dick@ximian.com>
[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 using System;\r
11 \r
12 namespace System.IO {\r
13 \r
14         // FIXME: there's a complication with this algorithm under windows.\r
15         // the pattern '*.*' matches all files (i think . matches the extension),\r
16         // whereas under UNIX it should only match files containing the '.' character.\r
17 \r
18         class SearchPattern {\r
19                 public SearchPattern (string pattern) : this (pattern, false) { }\r
20 \r
21                 public SearchPattern (string pattern, bool ignore)\r
22                 {\r
23                         this.ignore = ignore;\r
24                         Compile (pattern);\r
25                 }\r
26 \r
27                 public bool IsMatch (string text)\r
28                 {\r
29                         return Match (ops, text, 0);\r
30                 }\r
31 \r
32                 // private\r
33 \r
34                 private Op ops;         // the compiled pattern\r
35                 private bool ignore;    // ignore case\r
36 \r
37                 private void Compile (string pattern)\r
38                 {\r
39                         if (pattern == null || pattern.IndexOfAny (InvalidChars) >= 0)\r
40                                 throw new ArgumentException ("Invalid search pattern.");\r
41 \r
42                         if (pattern == "*") {   // common case\r
43                                 ops = new Op (OpCode.True);\r
44                                 return;\r
45                         }\r
46 \r
47                         ops = null;\r
48 \r
49                         int ptr = 0;\r
50                         Op last_op = null;\r
51                         while (ptr < pattern.Length) {\r
52                                 Op op;\r
53                         \r
54                                 switch (pattern [ptr]) {\r
55                                 case '?':\r
56                                         op = new Op (OpCode.AnyChar);\r
57                                         ++ ptr;\r
58                                         break;\r
59 \r
60                                 case '*':\r
61                                         op = new Op (OpCode.AnyString);\r
62                                         ++ ptr;\r
63                                         break;\r
64                                         \r
65                                 default:\r
66                                         op = new Op (OpCode.ExactString);\r
67                                         int end = pattern.IndexOfAny (WildcardChars, ptr);\r
68                                         if (end < 0)\r
69                                                 end = pattern.Length;\r
70 \r
71                                         op.Argument = pattern.Substring (ptr, end - ptr);\r
72                                         if (ignore)\r
73                                                 op.Argument = op.Argument.ToLower ();\r
74 \r
75                                         ptr = end;\r
76                                         break;\r
77                                 }\r
78 \r
79                                 if (last_op == null)\r
80                                         ops = op;\r
81                                 else\r
82                                         last_op.Next = op;\r
83 \r
84                                 last_op = op;\r
85                         }\r
86 \r
87                         if (last_op == null)\r
88                                 ops = new Op (OpCode.End);\r
89                         else\r
90                                 last_op.Next = new Op (OpCode.End);\r
91                 }\r
92 \r
93                 private bool Match (Op op, string text, int ptr)\r
94                 {\r
95                         while (op != null) {\r
96                                 switch (op.Code) {\r
97                                 case OpCode.True:\r
98                                         return true;\r
99 \r
100                                 case OpCode.End:\r
101                                         if (ptr == text.Length)\r
102                                                 return true;\r
103 \r
104                                         return false;\r
105                                 \r
106                                 case OpCode.ExactString:\r
107                                         int length = op.Argument.Length;\r
108                                         if (ptr + length > text.Length)\r
109                                                 return false;\r
110 \r
111                                         string str = text.Substring (ptr, length);\r
112                                         if (ignore)\r
113                                                 str = str.ToLower ();\r
114 \r
115                                         if (str != op.Argument)\r
116                                                 return false;\r
117 \r
118                                         ptr += length;\r
119                                         break;\r
120 \r
121                                 case OpCode.AnyChar:\r
122                                         if (++ ptr > text.Length)\r
123                                                 return false;\r
124                                         break;\r
125 \r
126                                 case OpCode.AnyString:\r
127                                         while (ptr <= text.Length) {\r
128                                                 if (Match (op.Next, text, ptr))\r
129                                                         return true;\r
130 \r
131                                                 ++ ptr;\r
132                                         }\r
133 \r
134                                         return false;\r
135                                 }\r
136 \r
137                                 op = op.Next;\r
138                         }\r
139 \r
140                         return true;\r
141                 }\r
142 \r
143                 // private static\r
144 \r
145                 internal static readonly char [] WildcardChars = { '*', '?' };\r
146                 internal static readonly char [] InvalidChars = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };\r
147 \r
148                 private class Op {\r
149                         public Op (OpCode code)\r
150                         {\r
151                                 this.Code = code;\r
152                                 this.Argument = null;\r
153                                 this.Next = null;\r
154                         }\r
155                 \r
156                         public OpCode Code;\r
157                         public string Argument;\r
158                         public Op Next;\r
159                 }\r
160 \r
161                 private enum OpCode {\r
162                         ExactString,            // literal\r
163                         AnyChar,                // ?\r
164                         AnyString,              // *\r
165                         End,                    // end of pattern\r
166                         True                    // always succeeds\r
167                 };\r
168         }\r
169 }\r