Masks: implement specific matching for Windows filemasks like foo*.* and other Windows oddities.

git-svn-id: trunk@40970 -
This commit is contained in:
bart 2013-05-01 10:53:23 +00:00
parent 04a4b1e14f
commit fe79a8fdff

View File

@ -26,7 +26,7 @@ interface
uses
// For Smart Linking: Do not use the LCL!
Classes, SysUtils, Contnrs, LazUtilsStrConsts;
Classes, SysUtils, Contnrs, StrUtils, LazUtilsStrConsts;
type
TMaskCharType = (mcChar, mcCharSet, mcAnyChar, mcAnyText);
@ -53,11 +53,15 @@ type
private
FMask: TMaskString;
fCaseSensitive: Boolean;
fInitialMask: String;
procedure InitMaskString(const AValue: String; const CaseSensitive: Boolean);
procedure ClearMaskString;
public
constructor Create(const AValue: String; const CaseSensitive: Boolean = False);
destructor Destroy; override;
function Matches(const AFileName: String): Boolean;
function MatchesWindowsMask(const AFileName: String): Boolean;
end;
{ TParseStringList }
@ -79,13 +83,16 @@ type
destructor Destroy; override;
function Matches(const AFileName: String): Boolean;
function MatchesWindowsMask(const AFileName: String): Boolean;
property Count: Integer read GetCount;
property Items[Index: Integer]: TMask read GetItem;
end;
function MatchesMask(const FileName, Mask: String; const CaseSensitive: Boolean = False): Boolean;
function MatchesWindowsMask(const FileName, Mask: String; const CaseSensitive: Boolean = False): Boolean;
function MatchesMaskList(const FileName, Mask: String; Separator: Char = ';'; const CaseSensitive: Boolean = False): Boolean;
function MatchesWindowsMaskList(const FileName, Mask: String; Separator: Char = ';'; const CaseSensitive: Boolean = False): Boolean;
implementation
@ -101,6 +108,18 @@ begin
end;
end;
function MatchesWindowsMask(const FileName, Mask: String; const CaseSensitive: Boolean): Boolean;
var
AMask: TMask;
begin
AMask := TMask.Create(Mask, CaseSensitive);
try
Result := AMask.MatchesWindowsMask(FileName);
finally
AMask.Free;
end;
end;
function MatchesMaskList(const FileName, Mask: String; Separator: Char; const CaseSensitive: Boolean): Boolean;
var
AMaskList: TMaskList;
@ -113,9 +132,21 @@ begin
end;
end;
function MatchesWindowsMaskList(const FileName, Mask: String; Separator: Char; const CaseSensitive: Boolean): Boolean;
var
AMaskList: TMaskList;
begin
AMaskList := TMaskList.Create(Mask, Separator, CaseSensitive);
try
Result := AMaskList.MatchesWindowsMask(FileName);
finally
AMaskList.Free;
end;
end;
{ TMask }
constructor TMask.Create(const AValue: String; const CaseSensitive: Boolean);
procedure TMask.InitMaskString(const AValue: String; const CaseSensitive: Boolean);
var
I: Integer;
SkipAnyText: Boolean;
@ -246,7 +277,6 @@ var
end;
begin
fCaseSensitive := CaseSensitive;
SetLength(FMask.Chars, 0);
FMask.MinLength := 0;
FMask.MaxLength := 0;
@ -264,14 +294,26 @@ begin
end;
end;
destructor TMask.Destroy;
procedure TMask.ClearMaskString;
var
I: Integer;
begin
for I := 0 to High(FMask.Chars) do
if FMask.Chars[I].CharType = mcCharSet then
Dispose(FMask.Chars[I].SetValue);
end;
constructor TMask.Create(const AValue: String; const CaseSensitive: Boolean);
begin
fInitialMask := AValue;
fCaseSensitive := CaseSensitive;
InitMaskString(AValue, CaseSensitive);
end;
destructor TMask.Destroy;
begin
ClearMaskString;
inherited Destroy;
end;
@ -346,6 +388,68 @@ begin
Result := MatchToEnd(0, 1);
end;
function TMask.MatchesWindowsMask(const AFileName: String): Boolean;
var
NewMaskValue, Ext: String;
begin
// treat initial mask differently for special cases:
// foo*.* -> foo*
// foo*. -> match foo*, but muts not have an extension
// *. -> any file without extension ( .foo is a filename without extension according to Windows)
// foo. matches only foo but not foo.txt
// foo.* -> match either foo or foo.*
if (Length(fInitialMask) > 2) and (RightStr(fInitialMask,3) = '*.*') then
// foo*.*
begin
NewMaskValue := Copy(fInitialMask,1,Length(fInitialMask)-2);
ClearMaskString;
InitMaskString(NewMaskValue, fCaseSensitive);
Result := Matches(AFileName);
//Restore initial state of FMask
ClearMaskString;
InitMaskString(fInitialMask, fCaseSensitive);
end
//else if (Length(fInitialMask) > 1) and (RightStr(fInitialMask,2) = '*.') then
else if (Length(fInitialMask) > 1) and (fInitialMask[Length(fInitialMask)] = '.') then
//foo*. or *. or foo.
begin
//if AFileName has an extension then Result is False, otherwise see if it matches foo*/foo
//a filename like .foo under Windows is considered to be a file without an extension
Ext := ExtractFileExt(AFileName);
if (Ext = '') or (Ext = AFileName) then
begin
NewMaskValue := Copy(fInitialMask,1,Length(fInitialMask)-1);
ClearMaskString;
InitMaskString(NewMaskValue, fCaseSensitive);
Result := Matches(AFileName);
//Restore initial state of FMask
ClearMaskString;
InitMaskString(fInitialMask, fCaseSensitive);
end
else
begin
Result := False;
end;
end
else if (Length(fInitialMask) > 2) and (RightStr(fInitialMask,2) = '.*') then
//foo.* (but not '.*')
begin
//First see if we have 'foo'
if fCaseSensitive then
Result := (AFileName = Copy(fInitialMask,1,Length(fInitialMask)-2))
else
Result := (CompareText(AFileName,Copy(fInitialMask,1,Length(fInitialMask)-2)) = 0);
if not Result then Result := Matches(AFileName);
end
else
//all other cases just call Matches()
begin
Result := Matches(AFileName);
end;
end;
{ TParseStringList }
constructor TParseStringList.Create(const AText, ASeparators: String);
@ -418,5 +522,21 @@ begin
end;
end;
function TMaskList.MatchesWindowsMask(const AFileName: String): Boolean;
var
I: Integer;
begin
Result := False;
for I := 0 to FMasks.Count - 1 do
begin
if TMask(FMasks.Items[I]).MatchesWindowsMask(AFileName) then
begin
Result := True;
Exit;
end;
end;
end;
end.