Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 // Common/Wildcard.cpp
3 #include "StdAfx.h"
5 #include "Wildcard.h"
7 static const wchar_t kPeriodChar = L'.';
8 static const wchar_t kAnyCharsChar = L'*';
9 static const wchar_t kAnyCharChar = L'?';
11 #ifdef _WIN32
12 static const wchar_t kDirDelimiter1 = L'\\';
13 #endif
14 static const wchar_t kDirDelimiter2 = L'/';
16 static const UString kWildCardCharSet = L"?*";
18 static const UString kIllegalWildCardFileNameChars=
19 L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF"
20 L"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
21 L"\"/:<>\\|";
23 static const UString kIllegalFileNameChars = kIllegalWildCardFileNameChars +
24 kWildCardCharSet;
26 static inline bool IsCharDirLimiter(wchar_t c)
27 {
28 return (
29 #ifdef _WIN32
30 c == kDirDelimiter1 ||
31 #endif
32 c == kDirDelimiter2);
33 }
35 // -----------------------------------------
36 // this function tests is name matches mask
37 // ? - any wchar_t or empty
38 // * - any characters or empty
40 static bool EnhancedMaskTest(const UString &mask, int maskPos,
41 const UString &name, int namePos)
42 {
43 int maskLen = mask.Length() - maskPos;
44 int nameLen = name.Length() - namePos;
45 if (maskLen == 0)
46 if (nameLen == 0)
47 return true;
48 else
49 return false;
50 wchar_t maskChar = mask[maskPos];
51 if(maskChar == kAnyCharChar)
52 {
53 /*
54 if (EnhancedMaskTest(mask, maskPos + 1, name, namePos))
55 return true;
56 */
57 if (nameLen == 0)
58 return false;
59 return EnhancedMaskTest(mask, maskPos + 1, name, namePos + 1);
60 }
61 else if(maskChar == kAnyCharsChar)
62 {
63 if (EnhancedMaskTest(mask, maskPos + 1, name, namePos))
64 return true;
65 if (nameLen == 0)
66 return false;
67 return EnhancedMaskTest(mask, maskPos, name, namePos + 1);
68 }
69 else
70 {
71 wchar_t c = name[namePos];
72 if (maskChar != c)
73 #ifdef _WIN32
74 if (MyCharUpper(maskChar) != MyCharUpper(c))
75 #endif
76 return false;
77 return EnhancedMaskTest(mask, maskPos + 1, name, namePos + 1);
78 }
79 }
81 // --------------------------------------------------
82 // Splits path to strings
84 void SplitPathToParts(const UString &path, UStringVector &pathParts)
85 {
86 pathParts.Clear();
87 UString name;
88 int len = path.Length();
89 if (len == 0)
90 return;
91 for (int i = 0; i < len; i++)
92 {
93 wchar_t c = path[i];
94 if (IsCharDirLimiter(c))
95 {
96 pathParts.Add(name);
97 name.Empty();
98 }
99 else
100 name += c;
101 }
102 pathParts.Add(name);
103 }
105 void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name)
106 {
107 int i;
108 for(i = path.Length() - 1; i >= 0; i--)
109 if(IsCharDirLimiter(path[i]))
110 break;
111 dirPrefix = path.Left(i + 1);
112 name = path.Mid(i + 1);
113 }
115 UString ExtractDirPrefixFromPath(const UString &path)
116 {
117 int i;
118 for(i = path.Length() - 1; i >= 0; i--)
119 if(IsCharDirLimiter(path[i]))
120 break;
121 return path.Left(i + 1);
122 }
124 UString ExtractFileNameFromPath(const UString &path)
125 {
126 int i;
127 for(i = path.Length() - 1; i >= 0; i--)
128 if(IsCharDirLimiter(path[i]))
129 break;
130 return path.Mid(i + 1);
131 }
134 bool CompareWildCardWithName(const UString &mask, const UString &name)
135 {
136 return EnhancedMaskTest(mask, 0, name, 0);
137 }
139 bool DoesNameContainWildCard(const UString &path)
140 {
141 return (path.FindOneOf(kWildCardCharSet) >= 0);
142 }
145 // ----------------------------------------------------------'
146 // NWildcard
148 namespace NWildcard {
150 static inline int BoolToIndex(bool value)
151 {
152 return value ? 1: 0;
153 }
156 /*
157 M = MaskParts.Size();
158 N = TestNameParts.Size();
160 File Dir
161 ForFile req M<=N [N-M, N) -
162 nonreq M=N [0, M) -
164 ForDir req M<N [0, M) ... [N-M-1, N-1) same as ForBoth-File
165 nonreq [0, M) same as ForBoth-File
167 ForBoth req m<=N [0, M) ... [N-M, N) same as ForBoth-File
168 nonreq [0, M) same as ForBoth-File
170 */
172 bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const
173 {
174 if (!isFile && !ForDir)
175 return false;
176 int delta = (int)pathParts.Size() - (int)PathParts.Size();
177 if (delta < 0)
178 return false;
179 int start = 0;
180 int finish = 0;
181 if (isFile)
182 {
183 if (!ForDir && !Recursive && delta !=0)
184 return false;
185 if (!ForFile && delta == 0)
186 return false;
187 if (!ForDir && Recursive)
188 start = delta;
189 }
190 if (Recursive)
191 {
192 finish = delta;
193 if (isFile && !ForFile)
194 finish = delta - 1;
195 }
196 for (int d = start; d <= finish; d++)
197 {
198 int i;
199 for (i = 0; i < PathParts.Size(); i++)
200 if (!CompareWildCardWithName(PathParts[i], pathParts[i + d]))
201 break;
202 if (i == PathParts.Size())
203 return true;
204 }
205 return false;
206 }
208 int CCensorNode::FindSubNode(const UString &name) const
209 {
210 for (int i = 0; i < SubNodes.Size(); i++)
211 if (SubNodes[i].Name.CompareNoCase(name) == 0)
212 return i;
213 return -1;
214 }
216 void CCensorNode::AddItemSimple(bool include, CItem &item)
217 {
218 if (include)
219 IncludeItems.Add(item);
220 else
221 ExcludeItems.Add(item);
222 }
224 void CCensorNode::AddItem(bool include, CItem &item)
225 {
226 if (item.PathParts.Size() <= 1)
227 {
228 AddItemSimple(include, item);
229 return;
230 }
231 const UString &front = item.PathParts.Front();
232 if (DoesNameContainWildCard(front))
233 {
234 AddItemSimple(include, item);
235 return;
236 }
237 int index = FindSubNode(front);
238 if (index < 0)
239 index = SubNodes.Add(CCensorNode(front, this));
240 item.PathParts.Delete(0);
241 SubNodes[index].AddItem(include, item);
242 }
244 void CCensorNode::AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir)
245 {
246 CItem item;
247 SplitPathToParts(path, item.PathParts);
248 item.Recursive = recursive;
249 item.ForFile = forFile;
250 item.ForDir = forDir;
251 AddItem(include, item);
252 }
254 bool CCensorNode::NeedCheckSubDirs() const
255 {
256 for (int i = 0; i < IncludeItems.Size(); i++)
257 {
258 const CItem &item = IncludeItems[i];
259 if (item.Recursive || item.PathParts.Size() > 1)
260 return true;
261 }
262 return false;
263 }
265 bool CCensorNode::AreThereIncludeItems() const
266 {
267 if (IncludeItems.Size() > 0)
268 return true;
269 for (int i = 0; i < SubNodes.Size(); i++)
270 if (SubNodes[i].AreThereIncludeItems())
271 return true;
272 return false;
273 }
275 bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const
276 {
277 const CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems;
278 for (int i = 0; i < items.Size(); i++)
279 if (items[i].CheckPath(pathParts, isFile))
280 return true;
281 return false;
282 }
284 bool CCensorNode::CheckPath(UStringVector &pathParts, bool isFile, bool &include) const
285 {
286 if (CheckPathCurrent(false, pathParts, isFile))
287 {
288 include = false;
289 return true;
290 }
291 include = true;
292 bool finded = CheckPathCurrent(true, pathParts, isFile);
293 if (pathParts.Size() == 1)
294 return finded;
295 int index = FindSubNode(pathParts.Front());
296 if (index >= 0)
297 {
298 UStringVector pathParts2 = pathParts;
299 pathParts2.Delete(0);
300 if (SubNodes[index].CheckPath(pathParts2, isFile, include))
301 return true;
302 }
303 return finded;
304 }
306 bool CCensorNode::CheckPath(const UString &path, bool isFile, bool &include) const
307 {
308 UStringVector pathParts;
309 SplitPathToParts(path, pathParts);
310 return CheckPath(pathParts, isFile, include);
311 }
313 bool CCensorNode::CheckPath(const UString &path, bool isFile) const
314 {
315 bool include;
316 if(CheckPath(path, isFile, include))
317 return include;
318 return false;
319 }
321 bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const
322 {
323 if (CheckPathCurrent(include, pathParts, isFile))
324 return true;
325 if (Parent == 0)
326 return false;
327 pathParts.Insert(0, Name);
328 return Parent->CheckPathToRoot(include, pathParts, isFile);
329 }
331 /*
332 bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const
333 {
334 UStringVector pathParts;
335 SplitPathToParts(path, pathParts);
336 return CheckPathToRoot(include, pathParts, isFile);
337 }
338 */
340 void CCensorNode::AddItem2(bool include, const UString &path, bool recursive)
341 {
342 if (path.IsEmpty())
343 return;
344 bool forFile = true;
345 bool forFolder = true;
346 UString path2 = path;
347 if (IsCharDirLimiter(path[path.Length() - 1]))
348 {
349 path2.Delete(path.Length() - 1);
350 forFile = false;
351 }
352 AddItem(include, path2, recursive, forFile, forFolder);
353 }
355 void CCensorNode::ExtendExclude(const CCensorNode &fromNodes)
356 {
357 ExcludeItems += fromNodes.ExcludeItems;
358 for (int i = 0; i < fromNodes.SubNodes.Size(); i++)
359 {
360 const CCensorNode &node = fromNodes.SubNodes[i];
361 int subNodeIndex = FindSubNode(node.Name);
362 if (subNodeIndex < 0)
363 subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this));
364 SubNodes[subNodeIndex].ExtendExclude(node);
365 }
366 }
368 int CCensor::FindPrefix(const UString &prefix) const
369 {
370 for (int i = 0; i < Pairs.Size(); i++)
371 if (Pairs[i].Prefix.CompareNoCase(prefix) == 0)
372 return i;
373 return -1;
374 }
376 void CCensor::AddItem(bool include, const UString &path, bool recursive)
377 {
378 UStringVector pathParts;
379 SplitPathToParts(path, pathParts);
380 bool forFile = true;
381 if (pathParts.Back().IsEmpty())
382 {
383 forFile = false;
384 pathParts.DeleteBack();
385 }
386 const UString &front = pathParts.Front();
387 bool isAbs = false;
388 if (front.IsEmpty())
389 isAbs = true;
390 else if (front.Length() == 2 && front[1] == L':')
391 isAbs = true;
392 else
393 {
394 for (int i = 0; i < pathParts.Size(); i++)
395 {
396 const UString &part = pathParts[i];
397 if (part == L".." || part == L".")
398 {
399 isAbs = true;
400 break;
401 }
402 }
403 }
404 int numAbsParts = 0;
405 if (isAbs)
406 if (pathParts.Size() > 1)
407 numAbsParts = pathParts.Size() - 1;
408 else
409 numAbsParts = 1;
410 UString prefix;
411 for (int i = 0; i < numAbsParts; i++)
412 {
413 const UString &front = pathParts.Front();
414 if (DoesNameContainWildCard(front))
415 break;
416 prefix += front;
417 prefix += WCHAR_PATH_SEPARATOR;
418 pathParts.Delete(0);
419 }
420 int index = FindPrefix(prefix);
421 if (index < 0)
422 index = Pairs.Add(CPair(prefix));
424 CItem item;
425 item.PathParts = pathParts;
426 item.ForDir = true;
427 item.ForFile = forFile;
428 item.Recursive = recursive;
429 Pairs[index].Head.AddItem(include, item);
430 }
432 bool CCensor::CheckPath(const UString &path, bool isFile) const
433 {
434 bool finded = false;
435 for (int i = 0; i < Pairs.Size(); i++)
436 {
437 bool include;
438 if (Pairs[i].Head.CheckPath(path, isFile, include))
439 {
440 if (!include)
441 return false;
442 finded = true;
443 }
444 }
445 return finded;
446 }
448 void CCensor::ExtendExclude()
449 {
450 int i;
451 for (i = 0; i < Pairs.Size(); i++)
452 if (Pairs[i].Prefix.IsEmpty())
453 break;
454 if (i == Pairs.Size())
455 return;
456 int index = i;
457 for (i = 0; i < Pairs.Size(); i++)
458 if (index != i)
459 Pairs[i].Head.ExtendExclude(Pairs[index].Head);
460 }
462 }