mirror of
https://github.com/CloudDelphi/Virtual-File-System
synced 2025-12-19 18:03:49 +01:00
276 lines
6.6 KiB
ObjectPascal
276 lines
6.6 KiB
ObjectPascal
unit VfsMatching;
|
|
(*
|
|
Description: Implements NT files matching strategy, same as RtlIsNameInExpression.
|
|
@link https://blogs.msdn.microsoft.com/jeremykuhne/2017/06/04/wildcards-in-windows/
|
|
@link https://devblogs.microsoft.com/oldnewthing/?p=24143
|
|
*)
|
|
|
|
|
|
(***) interface (***)
|
|
|
|
uses
|
|
SysUtils,
|
|
Utils, PatchForge;
|
|
|
|
|
|
function CompilePattern (const Pattern: WideString): Utils.TArrayOfByte;
|
|
function MatchPattern (const Str: WideString; {n} Pattern: pointer): boolean; overload;
|
|
function MatchPattern (const Str, Pattern: WideString): boolean; overload;
|
|
|
|
|
|
(***) implementation (***)
|
|
|
|
|
|
const
|
|
(* File name without last separator and extension: ~([^.]*+\z|.*(?=\.))~ *)
|
|
DOS_STAR = '<';
|
|
|
|
(* Dos single char or before dot/end: ~((?=\.)|.?)~ *)
|
|
DOS_QM = '>';
|
|
|
|
(* Dos dot or string end: ~(\.|\z)~ *)
|
|
DOS_DOT = '"';
|
|
|
|
MAX_STR_LEN = High(word);
|
|
|
|
|
|
type
|
|
TPatternKind = (KIND_CHAR, KIND_ANY_CHAR, KIND_ANY_CHARS, KIND_DOS_ANY_CHAR, KIND_DOS_ANY_CHARS, KIND_DOS_DOT, KIND_END);
|
|
|
|
PPattern = ^TPattern;
|
|
TPattern = record
|
|
Kind: TPatternKind;
|
|
Len: word;
|
|
Ch: WideChar;
|
|
end;
|
|
|
|
|
|
function CompilePattern (const Pattern: WideString): Utils.TArrayOfByte;
|
|
var
|
|
{O} Compiled: PatchForge.TPatchHelper;
|
|
PrevPatternKind: TPatternKind;
|
|
NextPatternKind: TPatternKind;
|
|
SkipPattern: boolean;
|
|
c: WideChar;
|
|
i: integer;
|
|
|
|
begin
|
|
Compiled := PatchForge.TPatchHelper.Wrap(PatchForge.TPatchMaker.Create);
|
|
// * * * * * //
|
|
PrevPatternKind := KIND_END;
|
|
|
|
for i := 1 to Length(Pattern) do begin
|
|
c := Pattern[i];
|
|
SkipPattern := false;
|
|
|
|
case c of
|
|
'?': NextPatternKind := KIND_ANY_CHAR;
|
|
|
|
'*': begin
|
|
NextPatternKind := KIND_ANY_CHARS;
|
|
SkipPattern := PrevPatternKind = KIND_ANY_CHARS;
|
|
end;
|
|
|
|
DOS_STAR: begin
|
|
NextPatternKind := KIND_DOS_ANY_CHARS;
|
|
SkipPattern := PrevPatternKind = KIND_DOS_ANY_CHARS;
|
|
end;
|
|
|
|
DOS_QM: NextPatternKind := KIND_DOS_ANY_CHAR;
|
|
|
|
DOS_DOT: NextPatternKind := KIND_DOS_DOT;
|
|
else
|
|
NextPatternKind := KIND_CHAR;
|
|
end; // .switch
|
|
|
|
if not SkipPattern then begin
|
|
with PPattern(Compiled.AllocAndSkip(sizeof(TPattern)))^ do begin
|
|
Kind := NextPatternKind;
|
|
Ch := c;
|
|
end;
|
|
end;
|
|
|
|
PrevPatternKind := NextPatternKind;
|
|
end; // .for
|
|
|
|
PPattern(Compiled.AllocAndSkip(sizeof(TPattern))).Kind := KIND_END;
|
|
result := Compiled.GetPatch;
|
|
// * * * * * //
|
|
Compiled.Release;
|
|
end; // .function CompilePattern
|
|
|
|
function MatchPattern (const Str: WideString; {n} Pattern: pointer): boolean; overload;
|
|
var
|
|
{Un} Subpattern: PPattern;
|
|
StrLen: integer;
|
|
StrStart: PWideChar;
|
|
StrEnd: PWideChar;
|
|
s: PWideChar;
|
|
|
|
function MatchSubpattern: boolean;
|
|
var
|
|
DotFinder: PWideChar;
|
|
|
|
begin
|
|
result := false;
|
|
Subpattern.Len := 1;
|
|
|
|
case Subpattern.Kind of
|
|
KIND_CHAR: begin
|
|
result := s^ = Subpattern.Ch;
|
|
end;
|
|
|
|
KIND_ANY_CHAR: begin
|
|
result := s <> StrEnd;
|
|
end;
|
|
|
|
KIND_DOS_ANY_CHAR: begin
|
|
result := true;
|
|
|
|
if (s^ = '.') or (s = StrEnd) then begin
|
|
Subpattern.Len := 0;
|
|
end;
|
|
end;
|
|
|
|
KIND_DOS_DOT: begin
|
|
result := (s^ = '.') or (s = StrEnd);
|
|
|
|
if s = StrEnd then begin
|
|
Subpattern.Len := 0;
|
|
end;
|
|
end;
|
|
|
|
KIND_DOS_ANY_CHARS: begin
|
|
result := true;
|
|
|
|
if s^ <> '.' then begin
|
|
DotFinder := StrEnd;
|
|
|
|
while (DotFinder > s) and (DotFinder^ <> '.') do begin
|
|
Dec(DotFinder);
|
|
end;
|
|
|
|
if DotFinder^ <> '.' then begin
|
|
DotFinder := StrEnd;
|
|
end;
|
|
end else begin
|
|
DotFinder := s;
|
|
end;
|
|
|
|
Subpattern.Len := DotFinder - s;
|
|
end; // .case KIND_DOS_ANY_CHARS
|
|
|
|
KIND_ANY_CHARS: begin
|
|
result := true;
|
|
Subpattern.Len := 0;
|
|
end;
|
|
|
|
KIND_END: begin
|
|
result := s = StrEnd;
|
|
Subpattern.Len := 0;
|
|
end;
|
|
end; // .switch
|
|
|
|
if result then begin
|
|
Inc(s, Subpattern.Len);
|
|
end;
|
|
end; // .function MatchSubpattern
|
|
|
|
function Recover: boolean;
|
|
var
|
|
NextSubpattern: PPattern;
|
|
NextChar: WideChar;
|
|
Caret: PWideChar;
|
|
|
|
begin
|
|
result := false;
|
|
|
|
while not result and (cardinal(Subpattern) >= cardinal(Pattern)) do begin
|
|
case Subpattern.Kind of
|
|
KIND_ANY_CHARS: begin
|
|
if s < StrEnd then begin
|
|
result := true;
|
|
NextSubpattern := Utils.PtrOfs(Subpattern, sizeof(TPattern));
|
|
Inc(Subpattern.Len);
|
|
Inc(s);
|
|
|
|
(* Fast consume to the end: xxx* *)
|
|
if NextSubpattern.Kind = KIND_END then begin
|
|
Inc(Subpattern.Len, StrEnd - s);
|
|
s := StrEnd;
|
|
end
|
|
(* Fast search for special character: *carry *)
|
|
else if NextSubpattern.Kind = KIND_CHAR then begin
|
|
NextChar := NextSubpattern.Ch;
|
|
Caret := s;
|
|
|
|
while (Caret < StrEnd) and (Caret^ <> NextChar) do begin
|
|
Inc(Caret);
|
|
end;
|
|
|
|
if Caret < StrEnd then begin
|
|
Inc(Subpattern.Len, Caret - s);
|
|
s := Caret;
|
|
end else begin
|
|
result := false;
|
|
end;
|
|
end; // .elseif
|
|
end else begin
|
|
Dec(s, Subpattern.Len);
|
|
end; // .else
|
|
end; // .case KIND_ANY_CHARS
|
|
else
|
|
Dec(s, Subpattern.Len);
|
|
end; // .switch
|
|
|
|
if result then begin
|
|
Inc(Subpattern);
|
|
end else begin
|
|
Dec(Subpattern);
|
|
end;
|
|
end; // .while
|
|
end; // .function Recover
|
|
|
|
begin
|
|
Subpattern := Pattern;
|
|
StrLen := Length(Str);
|
|
StrStart := PWideChar(Str);
|
|
StrEnd := StrStart + StrLen;
|
|
s := StrStart;
|
|
// * * * * * //
|
|
if Pattern = nil then begin
|
|
result := Str = '';
|
|
exit;
|
|
end;
|
|
|
|
if StrLen > MAX_STR_LEN then begin
|
|
result := false;
|
|
exit;
|
|
end;
|
|
|
|
while cardinal(Subpattern) >= cardinal(Pattern) do begin
|
|
if MatchSubpattern then begin
|
|
if Subpattern.Kind = KIND_END then begin
|
|
break;
|
|
end;
|
|
|
|
Inc(Subpattern);
|
|
end else begin
|
|
Dec(Subpattern);
|
|
Recover;
|
|
end;
|
|
end;
|
|
|
|
result := (cardinal(Subpattern) >= cardinal(Pattern)) and (s^ = #0);
|
|
end; // .function MatchPattern
|
|
|
|
function MatchPattern (const Str, Pattern: WideString): boolean; overload;
|
|
var
|
|
CompiledPattern: Utils.TArrayOfByte;
|
|
|
|
begin
|
|
CompiledPattern := CompilePattern(Pattern);
|
|
result := MatchPattern(Str, pointer(CompiledPattern));
|
|
end;
|
|
|
|
end. |