1 // Common/Wildcard.cpp
2
3 #include "StdAfx.h"
4
5 #include "Wildcard.h"
6
7 extern
8 bool g_CaseSensitive;
9 bool g_CaseSensitive =
10 #ifdef _WIN32
11 false;
12 #elif defined (__APPLE__)
13 #ifdef TARGET_OS_IPHONE
14 true;
15 #else
16 false;
17 #endif
18 #else
19 true;
20 #endif
21
22
IsPath1PrefixedByPath2(const wchar_t * s1,const wchar_t * s2)23 bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2)
24 {
25 if (g_CaseSensitive)
26 return IsString1PrefixedByString2(s1, s2);
27 return IsString1PrefixedByString2_NoCase(s1, s2);
28 }
29
30 // #include <stdio.h>
31
32 /*
33 static int MyStringCompare_PathLinux(const wchar_t *s1, const wchar_t *s2) throw()
34 {
35 for (;;)
36 {
37 wchar_t c1 = *s1++;
38 wchar_t c2 = *s2++;
39 if (c1 != c2)
40 {
41 if (c1 == 0) return -1;
42 if (c2 == 0) return 1;
43 if (c1 == '/') c1 = 0;
44 if (c2 == '/') c2 = 0;
45 if (c1 < c2) return -1;
46 if (c1 > c2) return 1;
47 continue;
48 }
49 if (c1 == 0) return 0;
50 }
51 }
52 */
53
MyStringCompare_Path(const wchar_t * s1,const wchar_t * s2)54 static int MyStringCompare_Path(const wchar_t *s1, const wchar_t *s2) throw()
55 {
56 for (;;)
57 {
58 wchar_t c1 = *s1++;
59 wchar_t c2 = *s2++;
60 if (c1 != c2)
61 {
62 if (c1 == 0) return -1;
63 if (c2 == 0) return 1;
64 if (IS_PATH_SEPAR(c1)) c1 = 0;
65 if (IS_PATH_SEPAR(c2)) c2 = 0;
66 if (c1 < c2) return -1;
67 if (c1 > c2) return 1;
68 continue;
69 }
70 if (c1 == 0) return 0;
71 }
72 }
73
MyStringCompareNoCase_Path(const wchar_t * s1,const wchar_t * s2)74 static int MyStringCompareNoCase_Path(const wchar_t *s1, const wchar_t *s2) throw()
75 {
76 for (;;)
77 {
78 wchar_t c1 = *s1++;
79 wchar_t c2 = *s2++;
80 if (c1 != c2)
81 {
82 if (c1 == 0) return -1;
83 if (c2 == 0) return 1;
84 if (IS_PATH_SEPAR(c1)) c1 = 0;
85 if (IS_PATH_SEPAR(c2)) c2 = 0;
86 c1 = MyCharUpper(c1);
87 c2 = MyCharUpper(c2);
88 if (c1 < c2) return -1;
89 if (c1 > c2) return 1;
90 continue;
91 }
92 if (c1 == 0) return 0;
93 }
94 }
95
CompareFileNames(const wchar_t * s1,const wchar_t * s2)96 int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW
97 {
98 /*
99 printf("\nCompareFileNames");
100 printf("\n S1: %ls", s1);
101 printf("\n S2: %ls", s2);
102 printf("\n");
103 */
104 // 21.07 : we parse PATH_SEPARATOR so: 0 < PATH_SEPARATOR < 1
105 if (g_CaseSensitive)
106 return MyStringCompare_Path(s1, s2);
107 return MyStringCompareNoCase_Path(s1, s2);
108 }
109
110 #ifndef USE_UNICODE_FSTRING
CompareFileNames(const char * s1,const char * s2)111 int CompareFileNames(const char *s1, const char *s2)
112 {
113 const UString u1 = fs2us(s1);
114 const UString u2 = fs2us(s2);
115 return CompareFileNames(u1, u2);
116 }
117 #endif
118
119 // -----------------------------------------
120 // this function compares name with mask
121 // ? - any char
122 // * - any char or empty
123
EnhancedMaskTest(const wchar_t * mask,const wchar_t * name)124 static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name)
125 {
126 for (;;)
127 {
128 const wchar_t m = *mask;
129 const wchar_t c = *name;
130 if (m == 0)
131 return (c == 0);
132 if (m == '*')
133 {
134 if (EnhancedMaskTest(mask + 1, name))
135 return true;
136 if (c == 0)
137 return false;
138 }
139 else
140 {
141 if (m == '?')
142 {
143 if (c == 0)
144 return false;
145 }
146 else if (m != c)
147 if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c))
148 return false;
149 mask++;
150 }
151 name++;
152 }
153 }
154
155 // --------------------------------------------------
156 // Splits path to strings
157
SplitPathToParts(const UString & path,UStringVector & pathParts)158 void SplitPathToParts(const UString &path, UStringVector &pathParts)
159 {
160 pathParts.Clear();
161 unsigned len = path.Len();
162 if (len == 0)
163 return;
164 UString name;
165 unsigned prev = 0;
166 for (unsigned i = 0; i < len; i++)
167 if (IsPathSepar(path[i]))
168 {
169 name.SetFrom(path.Ptr(prev), i - prev);
170 pathParts.Add(name);
171 prev = i + 1;
172 }
173 name.SetFrom(path.Ptr(prev), len - prev);
174 pathParts.Add(name);
175 }
176
SplitPathToParts_2(const UString & path,UString & dirPrefix,UString & name)177 void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name)
178 {
179 const wchar_t *start = path;
180 const wchar_t *p = start + path.Len();
181 for (; p != start; p--)
182 if (IsPathSepar(*(p - 1)))
183 break;
184 dirPrefix.SetFrom(path, (unsigned)(p - start));
185 name = p;
186 }
187
SplitPathToParts_Smart(const UString & path,UString & dirPrefix,UString & name)188 void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name)
189 {
190 const wchar_t *start = path;
191 const wchar_t *p = start + path.Len();
192 if (p != start)
193 {
194 if (IsPathSepar(*(p - 1)))
195 p--;
196 for (; p != start; p--)
197 if (IsPathSepar(*(p - 1)))
198 break;
199 }
200 dirPrefix.SetFrom(path, (unsigned)(p - start));
201 name = p;
202 }
203
204 /*
205 UString ExtractDirPrefixFromPath(const UString &path)
206 {
207 return path.Left(path.ReverseFind_PathSepar() + 1));
208 }
209 */
210
ExtractFileNameFromPath(const UString & path)211 UString ExtractFileNameFromPath(const UString &path)
212 {
213 return UString(path.Ptr((unsigned)(path.ReverseFind_PathSepar() + 1)));
214 }
215
216
DoesWildcardMatchName(const UString & mask,const UString & name)217 bool DoesWildcardMatchName(const UString &mask, const UString &name)
218 {
219 return EnhancedMaskTest(mask, name);
220 }
221
DoesNameContainWildcard(const UString & path)222 bool DoesNameContainWildcard(const UString &path)
223 {
224 for (unsigned i = 0; i < path.Len(); i++)
225 {
226 wchar_t c = path[i];
227 if (c == '*' || c == '?')
228 return true;
229 }
230 return false;
231 }
232
233
234 // ----------------------------------------------------------'
235 // NWildcard
236
237 namespace NWildcard {
238
239 /*
240
241 M = MaskParts.Size();
242 N = TestNameParts.Size();
243
244 File Dir
245 ForFile rec M<=N [N-M, N) -
246 !ForDir nonrec M=N [0, M) -
247
248 ForDir rec M<N [0, M) ... [N-M-1, N-1) same as ForBoth-File
249 !ForFile nonrec [0, M) same as ForBoth-File
250
251 ForFile rec m<=N [0, M) ... [N-M, N) same as ForBoth-File
252 ForDir nonrec [0, M) same as ForBoth-File
253
254 */
255
AreAllAllowed() const256 bool CItem::AreAllAllowed() const
257 {
258 return ForFile && ForDir && WildcardMatching && PathParts.Size() == 1 && PathParts.Front() == L"*";
259 }
260
CheckPath(const UStringVector & pathParts,bool isFile) const261 bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const
262 {
263 if (!isFile && !ForDir)
264 return false;
265
266 /*
267 if (PathParts.IsEmpty())
268 {
269 // PathParts.IsEmpty() means all items (universal wildcard)
270 if (!isFile)
271 return true;
272 if (pathParts.Size() <= 1)
273 return ForFile;
274 return (ForDir || Recursive && ForFile);
275 }
276 */
277
278 int delta = (int)pathParts.Size() - (int)PathParts.Size();
279 if (delta < 0)
280 return false;
281 int start = 0;
282 int finish = 0;
283
284 if (isFile)
285 {
286 if (!ForDir)
287 {
288 if (Recursive)
289 start = delta;
290 else if (delta !=0)
291 return false;
292 }
293 if (!ForFile && delta == 0)
294 return false;
295 }
296
297 if (Recursive)
298 {
299 finish = delta;
300 if (isFile && !ForFile)
301 finish = delta - 1;
302 }
303
304 for (int d = start; d <= finish; d++)
305 {
306 unsigned i;
307 for (i = 0; i < PathParts.Size(); i++)
308 {
309 if (WildcardMatching)
310 {
311 if (!DoesWildcardMatchName(PathParts[i], pathParts[i + (unsigned)d]))
312 break;
313 }
314 else
315 {
316 if (CompareFileNames(PathParts[i], pathParts[i + (unsigned)d]) != 0)
317 break;
318 }
319 }
320 if (i == PathParts.Size())
321 return true;
322 }
323 return false;
324 }
325
AreAllAllowed() const326 bool CCensorNode::AreAllAllowed() const
327 {
328 if (!Name.IsEmpty() ||
329 !SubNodes.IsEmpty() ||
330 !ExcludeItems.IsEmpty() ||
331 IncludeItems.Size() != 1)
332 return false;
333 return IncludeItems.Front().AreAllAllowed();
334 }
335
FindSubNode(const UString & name) const336 int CCensorNode::FindSubNode(const UString &name) const
337 {
338 FOR_VECTOR (i, SubNodes)
339 if (CompareFileNames(SubNodes[i].Name, name) == 0)
340 return (int)i;
341 return -1;
342 }
343
AddItemSimple(bool include,CItem & item)344 void CCensorNode::AddItemSimple(bool include, CItem &item)
345 {
346 CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems;
347 items.Add(item);
348 }
349
AddItem(bool include,CItem & item,int ignoreWildcardIndex)350 void CCensorNode::AddItem(bool include, CItem &item, int ignoreWildcardIndex)
351 {
352 if (item.PathParts.Size() <= 1)
353 {
354 if (item.PathParts.Size() != 0 && item.WildcardMatching)
355 {
356 if (!DoesNameContainWildcard(item.PathParts.Front()))
357 item.WildcardMatching = false;
358 }
359 AddItemSimple(include, item);
360 return;
361 }
362
363 const UString &front = item.PathParts.Front();
364
365 // WIN32 doesn't support wildcards in file names
366 if (item.WildcardMatching
367 && ignoreWildcardIndex != 0
368 && DoesNameContainWildcard(front))
369 {
370 AddItemSimple(include, item);
371 return;
372 }
373 CCensorNode &subNode = Find_SubNode_Or_Add_New(front);
374 item.PathParts.Delete(0);
375 subNode.AddItem(include, item, ignoreWildcardIndex - 1);
376 }
377
378 /*
379 void CCensorNode::AddItem(bool include, const UString &path, const CCensorPathProps &props)
380 {
381 CItem item;
382 SplitPathToParts(path, item.PathParts);
383 item.Recursive = props.Recursive;
384 item.ForFile = props.ForFile;
385 item.ForDir = props.ForDir;
386 item.WildcardMatching = props.WildcardMatching;
387 AddItem(include, item);
388 }
389 */
390
NeedCheckSubDirs() const391 bool CCensorNode::NeedCheckSubDirs() const
392 {
393 FOR_VECTOR (i, IncludeItems)
394 {
395 const CItem &item = IncludeItems[i];
396 if (item.Recursive || item.PathParts.Size() > 1)
397 return true;
398 }
399 return false;
400 }
401
AreThereIncludeItems() const402 bool CCensorNode::AreThereIncludeItems() const
403 {
404 if (IncludeItems.Size() > 0)
405 return true;
406 FOR_VECTOR (i, SubNodes)
407 if (SubNodes[i].AreThereIncludeItems())
408 return true;
409 return false;
410 }
411
CheckPathCurrent(bool include,const UStringVector & pathParts,bool isFile) const412 bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const
413 {
414 const CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems;
415 FOR_VECTOR (i, items)
416 if (items[i].CheckPath(pathParts, isFile))
417 return true;
418 return false;
419 }
420
CheckPathVect(const UStringVector & pathParts,bool isFile,bool & include) const421 bool CCensorNode::CheckPathVect(const UStringVector &pathParts, bool isFile, bool &include) const
422 {
423 if (CheckPathCurrent(false, pathParts, isFile))
424 {
425 include = false;
426 return true;
427 }
428 if (pathParts.Size() > 1)
429 {
430 int index = FindSubNode(pathParts.Front());
431 if (index >= 0)
432 {
433 UStringVector pathParts2 = pathParts;
434 pathParts2.Delete(0);
435 if (SubNodes[(unsigned)index].CheckPathVect(pathParts2, isFile, include))
436 return true;
437 }
438 }
439 bool finded = CheckPathCurrent(true, pathParts, isFile);
440 include = finded; // if (!finded), then (true) is allowed also
441 return finded;
442 }
443
444 /*
445 bool CCensorNode::CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const
446 {
447 UStringVector pathParts;
448 SplitPathToParts(path, pathParts);
449 if (CheckPathVect(pathParts, isFile, include))
450 {
451 if (!include || !isAltStream)
452 return true;
453 }
454 if (isAltStream && !pathParts.IsEmpty())
455 {
456 UString &back = pathParts.Back();
457 int pos = back.Find(L':');
458 if (pos > 0)
459 {
460 back.DeleteFrom(pos);
461 return CheckPathVect(pathParts, isFile, include);
462 }
463 }
464 return false;
465 }
466
467 bool CCensorNode::CheckPath(bool isAltStream, const UString &path, bool isFile) const
468 {
469 bool include;
470 if (CheckPath2(isAltStream, path, isFile, include))
471 return include;
472 return false;
473 }
474 */
475
CheckPathToRoot_Change(bool include,UStringVector & pathParts,bool isFile) const476 bool CCensorNode::CheckPathToRoot_Change(bool include, UStringVector &pathParts, bool isFile) const
477 {
478 if (CheckPathCurrent(include, pathParts, isFile))
479 return true;
480 if (!Parent)
481 return false;
482 pathParts.Insert(0, Name);
483 return Parent->CheckPathToRoot_Change(include, pathParts, isFile);
484 }
485
CheckPathToRoot(bool include,const UStringVector & pathParts,bool isFile) const486 bool CCensorNode::CheckPathToRoot(bool include, const UStringVector &pathParts, bool isFile) const
487 {
488 if (CheckPathCurrent(include, pathParts, isFile))
489 return true;
490 if (!Parent)
491 return false;
492 UStringVector pathParts2;
493 pathParts2.Add(Name);
494 pathParts2 += pathParts;
495 return Parent->CheckPathToRoot_Change(include, pathParts2, isFile);
496 }
497
498 /*
499 bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const
500 {
501 UStringVector pathParts;
502 SplitPathToParts(path, pathParts);
503 return CheckPathToRoot(include, pathParts, isFile);
504 }
505 */
506
ExtendExclude(const CCensorNode & fromNodes)507 void CCensorNode::ExtendExclude(const CCensorNode &fromNodes)
508 {
509 ExcludeItems += fromNodes.ExcludeItems;
510 FOR_VECTOR (i, fromNodes.SubNodes)
511 {
512 const CCensorNode &node = fromNodes.SubNodes[i];
513 Find_SubNode_Or_Add_New(node.Name).ExtendExclude(node);
514 }
515 }
516
FindPairForPrefix(const UString & prefix) const517 int CCensor::FindPairForPrefix(const UString &prefix) const
518 {
519 FOR_VECTOR (i, Pairs)
520 if (CompareFileNames(Pairs[i].Prefix, prefix) == 0)
521 return (int)i;
522 return -1;
523 }
524
525 #ifdef _WIN32
526
IsDriveColonName(const wchar_t * s)527 bool IsDriveColonName(const wchar_t *s)
528 {
529 unsigned c = s[0];
530 c |= 0x20;
531 c -= 'a';
532 return c <= (unsigned)('z' - 'a') && s[1] == ':' && s[2] == 0;
533 }
534
GetNumPrefixParts_if_DrivePath(UStringVector & pathParts)535 unsigned GetNumPrefixParts_if_DrivePath(UStringVector &pathParts)
536 {
537 if (pathParts.IsEmpty())
538 return 0;
539
540 unsigned testIndex = 0;
541 if (pathParts[0].IsEmpty())
542 {
543 if (pathParts.Size() < 4
544 || !pathParts[1].IsEmpty()
545 || pathParts[2] != L"?")
546 return 0;
547 testIndex = 3;
548 }
549 if (NWildcard::IsDriveColonName(pathParts[testIndex]))
550 return testIndex + 1;
551 return 0;
552 }
553
554 #endif
555
GetNumPrefixParts(const UStringVector & pathParts)556 static unsigned GetNumPrefixParts(const UStringVector &pathParts)
557 {
558 if (pathParts.IsEmpty())
559 return 0;
560
561 /* empty last part could be removed already from (pathParts),
562 if there was tail path separator (slash) in original full path string. */
563
564 #ifdef _WIN32
565
566 if (IsDriveColonName(pathParts[0]))
567 return 1;
568 if (!pathParts[0].IsEmpty())
569 return 0;
570
571 if (pathParts.Size() == 1)
572 return 1;
573 if (!pathParts[1].IsEmpty())
574 return 1;
575 if (pathParts.Size() == 2)
576 return 2;
577 if (pathParts[2] == L".")
578 return 3;
579
580 unsigned networkParts = 2;
581 if (pathParts[2] == L"?")
582 {
583 if (pathParts.Size() == 3)
584 return 3;
585 if (IsDriveColonName(pathParts[3]))
586 return 4;
587 if (!pathParts[3].IsEqualTo_Ascii_NoCase("UNC"))
588 return 3;
589 networkParts = 4;
590 }
591
592 networkParts +=
593 // 2; // server/share
594 1; // server
595 if (pathParts.Size() <= networkParts)
596 return pathParts.Size();
597 return networkParts;
598
599 #else
600
601 return pathParts[0].IsEmpty() ? 1 : 0;
602
603 #endif
604 }
605
AddItem(ECensorPathMode pathMode,bool include,const UString & path,const CCensorPathProps & props)606 void CCensor::AddItem(ECensorPathMode pathMode, bool include, const UString &path,
607 const CCensorPathProps &props)
608 {
609 if (path.IsEmpty())
610 throw "Empty file path";
611
612 UStringVector pathParts;
613 SplitPathToParts(path, pathParts);
614
615 CCensorPathProps props2 = props;
616
617 bool forFile = true;
618 bool forDir = true;
619 const UString &back = pathParts.Back();
620 if (back.IsEmpty())
621 {
622 // we have tail path separator. So it's directory.
623 // we delete tail path separator here even for "\" and "c:\"
624 forFile = false;
625 pathParts.DeleteBack();
626 }
627 else
628 {
629 if (props.MarkMode == kMark_StrictFile
630 || (props.MarkMode == kMark_StrictFile_IfWildcard
631 && DoesNameContainWildcard(back)))
632 forDir = false;
633 }
634
635
636 UString prefix;
637
638 int ignoreWildcardIndex = -1;
639
640 // #ifdef _WIN32
641 // we ignore "?" wildcard in "\\?\" prefix.
642 if (pathParts.Size() >= 3
643 && pathParts[0].IsEmpty()
644 && pathParts[1].IsEmpty()
645 && pathParts[2] == L"?")
646 ignoreWildcardIndex = 2;
647 // #endif
648
649 if (pathMode != k_AbsPath)
650 {
651 // detection of the number of Skip Parts for prefix
652 ignoreWildcardIndex = -1;
653
654 const unsigned numPrefixParts = GetNumPrefixParts(pathParts);
655 unsigned numSkipParts = numPrefixParts;
656
657 if (pathMode != k_FullPath)
658 {
659 // if absolute path, then all parts before last part will be in prefix
660 if (numPrefixParts != 0 && pathParts.Size() > numPrefixParts)
661 numSkipParts = pathParts.Size() - 1;
662 }
663 {
664 int dotsIndex = -1;
665 for (unsigned i = numPrefixParts; i < pathParts.Size(); i++)
666 {
667 const UString &part = pathParts[i];
668 if (part == L".." || part == L".")
669 dotsIndex = (int)i;
670 }
671
672 if (dotsIndex >= 0)
673 {
674 if (dotsIndex == (int)pathParts.Size() - 1)
675 numSkipParts = pathParts.Size();
676 else
677 numSkipParts = pathParts.Size() - 1;
678 }
679 }
680
681 // we split (pathParts) to (prefix) and (pathParts).
682 for (unsigned i = 0; i < numSkipParts; i++)
683 {
684 {
685 const UString &front = pathParts.Front();
686 // WIN32 doesn't support wildcards in file names
687 if (props.WildcardMatching)
688 if (i >= numPrefixParts && DoesNameContainWildcard(front))
689 break;
690 prefix += front;
691 prefix.Add_PathSepar();
692 }
693 pathParts.Delete(0);
694 }
695 }
696
697 int index = FindPairForPrefix(prefix);
698 if (index < 0)
699 {
700 index = (int)Pairs.Size();
701 Pairs.AddNew().Prefix = prefix;
702 }
703
704 if (pathMode != k_AbsPath)
705 {
706 if (pathParts.IsEmpty() || (pathParts.Size() == 1 && pathParts[0].IsEmpty()))
707 {
708 // we create universal item, if we skip all parts as prefix (like \ or L:\ )
709 pathParts.Clear();
710 pathParts.Add(UString("*"));
711 forFile = true;
712 forDir = true;
713 props2.WildcardMatching = true;
714 props2.Recursive = false;
715 }
716 }
717
718 /*
719 // not possible now
720 if (!forDir && !forFile)
721 {
722 UString s ("file path was blocked for files and directories: ");
723 s += path;
724 throw s;
725 // return; // for debug : ignore item (don't create Item)
726 }
727 */
728
729 CItem item;
730 item.PathParts = pathParts;
731 item.ForDir = forDir;
732 item.ForFile = forFile;
733 item.Recursive = props2.Recursive;
734 item.WildcardMatching = props2.WildcardMatching;
735 Pairs[(unsigned)index].Head.AddItem(include, item, ignoreWildcardIndex);
736 }
737
738 /*
739 bool CCensor::CheckPath(bool isAltStream, const UString &path, bool isFile) const
740 {
741 bool finded = false;
742 FOR_VECTOR (i, Pairs)
743 {
744 bool include;
745 if (Pairs[i].Head.CheckPath2(isAltStream, path, isFile, include))
746 {
747 if (!include)
748 return false;
749 finded = true;
750 }
751 }
752 return finded;
753 }
754 */
755
ExtendExclude()756 void CCensor::ExtendExclude()
757 {
758 unsigned i;
759 for (i = 0; i < Pairs.Size(); i++)
760 if (Pairs[i].Prefix.IsEmpty())
761 break;
762 if (i == Pairs.Size())
763 return;
764 unsigned index = i;
765 for (i = 0; i < Pairs.Size(); i++)
766 if (index != i)
767 Pairs[i].Head.ExtendExclude(Pairs[index].Head);
768 }
769
AddPathsToCensor(ECensorPathMode censorPathMode)770 void CCensor::AddPathsToCensor(ECensorPathMode censorPathMode)
771 {
772 FOR_VECTOR(i, CensorPaths)
773 {
774 const CCensorPath &cp = CensorPaths[i];
775 AddItem(censorPathMode, cp.Include, cp.Path, cp.Props);
776 }
777 CensorPaths.Clear();
778 }
779
AddPreItem(bool include,const UString & path,const CCensorPathProps & props)780 void CCensor::AddPreItem(bool include, const UString &path, const CCensorPathProps &props)
781 {
782 CCensorPath &cp = CensorPaths.AddNew();
783 cp.Path = path;
784 cp.Include = include;
785 cp.Props = props;
786 }
787
788 }
789