mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-10 07:08:21 +02:00
406 lines
8.9 KiB
ObjectPascal
406 lines
8.9 KiB
ObjectPascal
{
|
|
/***************************************************************************
|
|
masks.pas
|
|
---------
|
|
|
|
***************************************************************************/
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* This file is part of the Lazarus Component Library (LCL) *
|
|
* *
|
|
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
|
|
* for details about the copyright. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
* *
|
|
*****************************************************************************
|
|
}
|
|
unit Masks;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Contnrs;
|
|
|
|
type
|
|
TMaskCharType = (mcChar, mcCharSet, mcAnyChar, mcAnyText);
|
|
|
|
TCharSet = set of Char;
|
|
PCharSet = ^TCharSet;
|
|
|
|
TMaskChar = record
|
|
case CharType: TMaskCharType of
|
|
mcChar: (CharValue: Char);
|
|
mcCharSet: (Negative: Boolean; SetValue: PCharSet);
|
|
mcAnyChar, mcAnyText: ();
|
|
end;
|
|
|
|
TMaskString = record
|
|
MinLength: Integer;
|
|
MaxLength: Integer;
|
|
Chars: Array of TMaskChar;
|
|
end;
|
|
|
|
{ TMask }
|
|
|
|
TMask = class
|
|
private
|
|
FMask: TMaskString;
|
|
public
|
|
constructor Create(const AValue: String);
|
|
destructor Destroy; override;
|
|
|
|
function Matches(const AFileName: String): Boolean;
|
|
end;
|
|
|
|
{ TParseStringList }
|
|
|
|
TParseStringList = class(TStringList)
|
|
public
|
|
constructor Create(const AText, ASeparators: String);
|
|
end;
|
|
|
|
{ TMaskList }
|
|
|
|
TMaskList = class
|
|
private
|
|
FMasks: TObjectList;
|
|
function GetCount: Integer;
|
|
function GetItem(Index: Integer): TMask;
|
|
public
|
|
constructor Create(const AValue: String; ASeparator: Char = ';');
|
|
destructor Destroy; override;
|
|
|
|
function Matches(const AFileName: String): Boolean;
|
|
|
|
property Count: Integer read GetCount;
|
|
property Items[Index: Integer]: TMask read GetItem;
|
|
end;
|
|
|
|
function MatchesMask(const FileName, Mask: String): Boolean;
|
|
function MatchesMaskList(const FileName, Mask: String; Separator: Char = ';'): Boolean;
|
|
|
|
implementation
|
|
|
|
uses LCLProc, LCLStrConsts;
|
|
|
|
function MatchesMask(const FileName, Mask: String): Boolean;
|
|
var
|
|
AMask: TMask;
|
|
begin
|
|
AMask := TMask.Create(Mask);
|
|
try
|
|
Result := AMask.Matches(FileName);
|
|
finally
|
|
AMask.Free;
|
|
end;
|
|
end;
|
|
|
|
function MatchesMaskList(const FileName, Mask: String; Separator: Char): Boolean;
|
|
var
|
|
AMaskList: TMaskList;
|
|
begin
|
|
AMaskList := TMaskList.Create(Mask, Separator);
|
|
try
|
|
Result := AMaskList.Matches(FileName);
|
|
finally
|
|
AMaskList.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TMask }
|
|
|
|
constructor TMask.Create(const AValue: String);
|
|
var
|
|
I: Integer;
|
|
SkipAnyText: Boolean;
|
|
|
|
procedure CharSetError;
|
|
begin
|
|
raise EConvertError.CreateFmt(sInvalidCharSet, [AValue]);
|
|
end;
|
|
|
|
procedure AddAnyText;
|
|
begin
|
|
if SkipAnyText then
|
|
begin
|
|
Inc(I);
|
|
Exit;
|
|
end;
|
|
|
|
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
|
|
FMask.Chars[High(FMask.Chars)].CharType := mcAnyText;
|
|
|
|
FMask.MaxLength := MaxInt;
|
|
SkipAnyText := True;
|
|
Inc(I);
|
|
end;
|
|
|
|
procedure AddAnyChar;
|
|
begin
|
|
SkipAnyText := False;
|
|
|
|
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
|
|
FMask.Chars[High(FMask.Chars)].CharType := mcAnyChar;
|
|
|
|
Inc(FMask.MinLength);
|
|
if FMask.MaxLength < MaxInt then Inc(FMask.MaxLength);
|
|
|
|
Inc(I);
|
|
end;
|
|
|
|
procedure AddCharSet;
|
|
var
|
|
CharSet: TCharSet;
|
|
Valid: Boolean;
|
|
C, Last: Char;
|
|
begin
|
|
SkipAnyText := False;
|
|
|
|
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
|
|
FMask.Chars[High(FMask.Chars)].CharType := mcCharSet;
|
|
|
|
Inc(I);
|
|
if (I <= Length(AValue)) and (AValue[I] = '!') then
|
|
begin
|
|
FMask.Chars[High(FMask.Chars)].Negative := True;
|
|
Inc(I);
|
|
end
|
|
else FMask.Chars[High(FMask.Chars)].Negative := False;
|
|
|
|
Last := '-';
|
|
CharSet := [];
|
|
Valid := False;
|
|
while I <= Length(AValue) do
|
|
begin
|
|
case AValue[I] of
|
|
'-':
|
|
begin
|
|
if Last = '-' then CharSetError;
|
|
Inc(I);
|
|
|
|
if (I > Length(AValue)) then CharSetError;
|
|
//DebugLn('Set: ' + Last + '-' + UpCase(AValue[I]));
|
|
for C := Last to UpCase(AValue[I]) do Include(CharSet, C);
|
|
Inc(I);
|
|
end;
|
|
']':
|
|
begin
|
|
Valid := True;
|
|
Break;
|
|
end;
|
|
else
|
|
begin
|
|
Last := UpCase(AValue[I]);
|
|
Include(CharSet, Last);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if (not Valid) or (CharSet = []) then CharSetError;
|
|
|
|
New(FMask.Chars[High(FMask.Chars)].SetValue);
|
|
FMask.Chars[High(FMask.Chars)].SetValue^ := CharSet;
|
|
|
|
Inc(FMask.MinLength);
|
|
if FMask.MaxLength < MaxInt then Inc(FMask.MaxLength);
|
|
|
|
Inc(I);
|
|
end;
|
|
|
|
procedure AddChar;
|
|
begin
|
|
SkipAnyText := False;
|
|
|
|
SetLength(FMask.Chars, Length(FMask.Chars) + 1);
|
|
with FMask.Chars[High(FMask.Chars)] do
|
|
begin
|
|
CharType := mcChar;
|
|
CharValue := UpCase(AValue[I]);
|
|
end;
|
|
|
|
Inc(FMask.MinLength);
|
|
if FMask.MaxLength < MaxInt then Inc(FMask.MaxLength);
|
|
|
|
Inc(I);
|
|
end;
|
|
|
|
begin
|
|
SetLength(FMask.Chars, 0);
|
|
FMask.MinLength := 0;
|
|
FMask.MaxLength := 0;
|
|
SkipAnyText := False;
|
|
|
|
I := 1;
|
|
while I <= Length(AValue) do
|
|
begin
|
|
case AValue[I] of
|
|
'*': AddAnyText;
|
|
'?': AddAnyChar;
|
|
'[': AddCharSet;
|
|
else AddChar;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
destructor TMask.Destroy;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to High(FMask.Chars) do
|
|
if FMask.Chars[I].CharType = mcCharSet then
|
|
Dispose(FMask.Chars[I].SetValue);
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TMask.Matches(const AFileName: String): Boolean;
|
|
var
|
|
L: Integer;
|
|
S: String;
|
|
|
|
function MatchToEnd(MaskIndex, CharIndex: Integer): Boolean;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
Result := False;
|
|
|
|
for I := MaskIndex to High(FMask.Chars) do
|
|
begin
|
|
case FMask.Chars[I].CharType of
|
|
mcChar:
|
|
begin
|
|
if CharIndex > L then Exit;
|
|
//DebugLn('Match ' + S[CharIndex] + '<?>' + FMask.Chars[I].CharValue);
|
|
if S[CharIndex] <> FMask.Chars[I].CharValue then Exit;
|
|
Inc(CharIndex);
|
|
end;
|
|
mcCharSet:
|
|
begin
|
|
if CharIndex > L then Exit;
|
|
if FMask.Chars[I].Negative xor
|
|
(S[CharIndex] in FMask.Chars[I].SetValue^) then Inc(CharIndex)
|
|
else Exit;
|
|
end;
|
|
mcAnyChar:
|
|
begin
|
|
if CharIndex > L then Exit;
|
|
Inc(CharIndex);
|
|
end;
|
|
mcAnyText:
|
|
begin
|
|
if I = High(FMask.Chars) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
|
|
for J := CharIndex to L do
|
|
if MatchToEnd(I + 1, J) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Result := CharIndex > L;
|
|
end;
|
|
|
|
begin
|
|
Result := False;
|
|
L := Length(AFileName);
|
|
if L = 0 then
|
|
begin
|
|
if FMask.MinLength = 0 then Result := True;
|
|
Exit;
|
|
end;
|
|
|
|
if (L < FMask.MinLength) or (L > FMask.MaxLength) then Exit;
|
|
|
|
S := UpperCase(AFileName);
|
|
Result := MatchToEnd(0, 1);
|
|
end;
|
|
|
|
{ TParseStringList }
|
|
|
|
constructor TParseStringList.Create(const AText, ASeparators: String);
|
|
var
|
|
I, S: Integer;
|
|
begin
|
|
inherited Create;
|
|
|
|
S := 1;
|
|
for I := 1 to Length(AText) do
|
|
begin
|
|
if Pos(AText[I], ASeparators) > 0 then
|
|
begin
|
|
if I > S then Add(Copy(AText, S, I - S));
|
|
S := I + 1;
|
|
end;
|
|
end;
|
|
|
|
if Length(AText) >= S then Add(Copy(AText, S, Length(AText) - S + 1));
|
|
end;
|
|
|
|
{ TMaskList }
|
|
|
|
function TMaskList.GetItem(Index: Integer): TMask;
|
|
begin
|
|
Result := TMask(FMasks.Items[Index]);
|
|
end;
|
|
|
|
function TMaskList.GetCount: Integer;
|
|
begin
|
|
Result := FMasks.Count;
|
|
end;
|
|
|
|
constructor TMaskList.Create(const AValue: String; ASeparator: Char);
|
|
var
|
|
S: TParseStringList;
|
|
I: Integer;
|
|
begin
|
|
FMasks := TObjectList.Create(True);
|
|
|
|
S := TParseStringList.Create(AValue, ASeparator + ' ');
|
|
try
|
|
for I := 0 to S.Count - 1 do
|
|
FMasks.Add(TMask.Create(S[I]));
|
|
finally
|
|
S.Free;
|
|
end;
|
|
end;
|
|
|
|
destructor TMaskList.Destroy;
|
|
begin
|
|
FMasks.Free;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TMaskList.Matches(const AFileName: String): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
|
|
for I := 0 to FMasks.Count - 1 do
|
|
begin
|
|
if TMask(FMasks.Items[I]).Matches(AFileName) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|