MaskSearch.pas 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. {
  2. Basic pattern matching. Supports '*' and '?' only.
  3. This code is based on a unit by Markus Stephany ([email protected]) that
  4. I found on DSP (http://sunsite.icm.edu.pl/delphi/). Please DO NOT email
  5. Markus about any problems you may find in it. All problems with it should be
  6. reported to me, since I'm the last one who mucked with it.
  7. The original did a bunch of stuff that I didn't need (like searching inside
  8. of files), didn't do some stuff that I did need (like handling spaces in the
  9. filename), and had a few bugs, too. In order to help me find the bugs more
  10. quickly, I stripped out the stuff I didn't need, renamed things to be a little
  11. more clear, and generally tried to clean up the mess that comes from stripping
  12. out stuff you don't need. :)
  13. }
  14. unit MaskSearch;
  15. interface
  16. uses
  17. Classes, SysUtils;
  18. procedure BuildMask(Str: string; MaskList: TStringList);
  19. function FileMatches(AFile: string; MaskList: TStringList): boolean;
  20. implementation
  21. // fills the grep_list with the parts of 's' (divided by ';')
  22. procedure BuildMask(Str: string; MaskList: TStringList);
  23. var
  24. ct: integer;
  25. begin
  26. MaskList.clear;
  27. MaskList.sorted := false;
  28. if Str = '' then
  29. begin
  30. MaskList.add('*');
  31. end else begin
  32. if Str[length(Str)] <> ';' then
  33. Str := Str + ';';
  34. // divide the string
  35. ct := Pos(';', Str);
  36. while ct > 0 do
  37. begin
  38. MaskList.Add(AnsiLowerCase(Copy(Str, 1, ct-1)));
  39. Delete(Str, 1, ct);
  40. ct := Pos(';', Str);
  41. end;
  42. MaskList.sorted := TRUE;
  43. MaskList.duplicates := dupIgnore;
  44. end;
  45. end;
  46. // tests whether the string 'Str' fits to the search mask in 'Mask'
  47. function SimpleGrep(Str, Mask: string):boolean;
  48. var
  49. sr, s2: string;
  50. ps1,ps2,ps3: integer;
  51. DontCare: boolean;
  52. OneChar: char;
  53. TmpList: TStringList;
  54. begin
  55. if (Mask = '*') or // fits always
  56. ((Mask = '*.*') and (Pos('.', Str) > 0)) then // always fits, too
  57. Result := TRUE
  58. else begin
  59. if (Pos('*', Mask) = 0) and (Pos('?', Mask) = 0) and (Mask = Str) then
  60. // searched text was found (searchstring IN text)
  61. Result := TRUE
  62. else begin
  63. Result := FALSE;
  64. if Mask = '' then
  65. exit;
  66. TmpList := TStringList.Create;
  67. try
  68. // divide partial strings ('?','*' or text) to TmpList
  69. repeat
  70. OneChar := Mask[1];
  71. if (OneChar in ['*', '?']) then
  72. begin
  73. TmpList.Add(OneChar);
  74. Delete(Mask, 1, 1);
  75. end else begin
  76. ps1 := Pos('?', Mask);
  77. if ps1 = 0 then
  78. ps1 := MaxInt;
  79. ps2 := Pos('*', Mask);
  80. if ps2 = 0 then
  81. ps2 := MaxInt;
  82. if ps2 > ps1 then
  83. ps2 := ps1;
  84. TmpList.Add(Copy(Mask, 1, ps2-1));
  85. Delete(Mask, 1, ps2-1);
  86. end;
  87. until Mask = '';
  88. // now compare the string with the partial search masks
  89. DontCare := FALSE;
  90. ps2 := 1;
  91. if TmpList.Count > 0 then
  92. begin
  93. for ps1 := 0 to pred(TmpList.Count) do
  94. begin
  95. sr := TmpList[ps1];
  96. if sr = '?' then
  97. begin
  98. inc(ps2);
  99. if ps2 > length(Str)+1 then
  100. exit;
  101. end else begin
  102. if sr = '*' then
  103. DontCare := TRUE
  104. else begin
  105. if DontCare then
  106. begin
  107. if ps1 = pred(TmpList.Count) then
  108. begin
  109. s2 := Copy(Str, ps2, maxint);
  110. ps2 := length(Str); // just something to make the thing fail
  111. if Length(s2) >= Length(SR) then
  112. if sr = Copy(s2, Length(s2)-Length(SR)+1, MaxInt) then
  113. ps2 := length(Str) + 1;
  114. end else begin
  115. ps3:= Pos(sr, Copy(Str, ps2, maxint));
  116. if ps3 = 0 then
  117. exit;
  118. ps2 := ps2 + ps3 + length(sr) - 1;
  119. end;
  120. DontCare := FALSE;
  121. end else begin
  122. if Copy(Str, ps2, length(sr)) <> sr then
  123. exit;
  124. ps2 := ps2 + length(sr);
  125. end;
  126. end;
  127. end;
  128. end;
  129. end;
  130. if (not DontCare) and (ps2 <> length(Str)+1) then
  131. Result := FALSE
  132. else
  133. Result := TRUE;
  134. finally
  135. TmpList.free;
  136. end;
  137. end;
  138. end;
  139. end;
  140. // tests whether the filename fits the search masks in MaskList
  141. function FileMatches(AFile: string; MaskList: TStringList): boolean;
  142. var
  143. ct: integer;
  144. begin
  145. AFile := AnsiLowerCase(AFile);
  146. if (MaskList = NIL) or (MaskList.Count = 0) then
  147. Result := TRUE // if no search AFileing, the always return TRUE
  148. else begin
  149. if Pos('.', AFile) = 0 then
  150. AFile := AFile + '.'; // '.' is implied for filenames
  151. Result := FALSE;
  152. // compare with the whole MaskList until one fits
  153. for ct := 0 to Pred(MaskList.Count) do
  154. begin
  155. if SimpleGrep(AFile, MaskList[ct]) then
  156. begin
  157. Result := TRUE;
  158. break;
  159. end;
  160. end;
  161. end;
  162. end;
  163. end.