mirror of https://github.com/red-prig/fpPS4.git
268 lines
5.4 KiB
Plaintext
268 lines
5.4 KiB
Plaintext
unit sys_fnmatch;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
const
|
|
FNM_NOMATCH =1; { Match failed. }
|
|
|
|
FNM_NOESCAPE=$01; { Disable backslash escaping. }
|
|
FNM_PATHNAME=$02; { Slash must be matched by slash. }
|
|
FNM_PERIOD =$04; { Period must be matched by period. }
|
|
|
|
FNM_NOSYS =(-1); { Reserved. }
|
|
|
|
FNM_LEADING_DIR=$08; { Ignore /<tail> after Imatch. }
|
|
FNM_CASEFOLD =$10; { Case insensitive search. }
|
|
FNM_IGNORECASE =FNM_CASEFOLD;
|
|
FNM_FILE_NAME =FNM_PATHNAME;
|
|
|
|
EOS=#0;
|
|
|
|
RANGE_MATCH =1;
|
|
RANGE_NOMATCH=0;
|
|
RANGE_ERROR =(-1);
|
|
|
|
function rangematch(pattern:pchar;test:char;flags:Integer;newp:ppchar):Integer;
|
|
function fnmatch(pattern,str:pchar;flags:Integer):Integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
sysutils;
|
|
|
|
{
|
|
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
|
|
* Compares a filename or pathname to a pattern.
|
|
}
|
|
function fnmatch(pattern,str:pchar;flags:Integer):Integer;
|
|
label
|
|
norm;
|
|
var
|
|
stringstart:pchar;
|
|
newp:pchar;
|
|
c,test:char;
|
|
begin
|
|
stringstart:=str;
|
|
while true do
|
|
begin
|
|
c:=pattern^;
|
|
Inc(pattern);
|
|
case (c) of
|
|
EOS:
|
|
begin
|
|
if ((flags and FNM_LEADING_DIR)<>0) and (str^='/') then
|
|
Exit(0);
|
|
if (str^=EOS) then
|
|
Exit(0)
|
|
else
|
|
Exit(FNM_NOMATCH);
|
|
end;
|
|
'?':
|
|
begin
|
|
if (str^=EOS) then
|
|
Exit(FNM_NOMATCH);
|
|
if (str^='/') and ((flags and FNM_PATHNAME)<>0) then
|
|
Exit(FNM_NOMATCH);
|
|
if (str^='.') and
|
|
((flags and FNM_PERIOD)<>0) and
|
|
((str=stringstart) or
|
|
(((flags and FNM_PATHNAME)<>0) and
|
|
((str - 1)^='/'))) then
|
|
Exit(FNM_NOMATCH);
|
|
Inc(str);
|
|
end;
|
|
'*':
|
|
begin
|
|
c:=pattern^;
|
|
{ Collapse multiple stars. }
|
|
while (c='*') do
|
|
begin
|
|
Inc(pattern);
|
|
c:=pattern^;
|
|
end;
|
|
|
|
if (str^='.') and
|
|
((flags and FNM_PERIOD)<>0) and
|
|
((str=stringstart) or
|
|
(((flags and FNM_PATHNAME)<>0) and
|
|
((str - 1)^='/'))) then
|
|
Exit(FNM_NOMATCH);
|
|
|
|
{ Optimize for pattern with * at end or before /. }
|
|
if (c=EOS) then
|
|
begin
|
|
if ((flags and FNM_PATHNAME)<>0) then
|
|
begin
|
|
if ((flags and FNM_LEADING_DIR)<>0) or (strscan(str, '/')=nil) then
|
|
Exit(0)
|
|
else
|
|
Exit(FNM_NOMATCH);
|
|
end else
|
|
Exit(0);
|
|
end else
|
|
if (c='/') and ((flags and FNM_PATHNAME)<>0) then
|
|
begin
|
|
str:=strscan(str, '/');
|
|
if (str=nil) then
|
|
Exit(FNM_NOMATCH);
|
|
continue;
|
|
end;
|
|
|
|
{ General case, use recursion. }
|
|
test:=str^;
|
|
while (test<>EOS) do
|
|
begin
|
|
if (fnmatch(pattern, str, flags and (not FNM_PERIOD))=0) then
|
|
Exit(0);
|
|
if (test='/') and ((flags and FNM_PATHNAME)<>0) then
|
|
break;
|
|
Inc(str);
|
|
test:=str^;
|
|
end;
|
|
Exit(FNM_NOMATCH);
|
|
end;
|
|
'[':
|
|
begin
|
|
if (str^=EOS) then
|
|
Exit(FNM_NOMATCH);
|
|
if (str^='/') and ((flags and FNM_PATHNAME)<>0) then
|
|
Exit(FNM_NOMATCH);
|
|
if (str^='.') and
|
|
((flags and FNM_PERIOD)<>0) and
|
|
((str=stringstart) or
|
|
(((flags and FNM_PATHNAME)<>0) and
|
|
((str - 1)^='/'))) then
|
|
Exit(FNM_NOMATCH);
|
|
|
|
case (rangematch(pattern, str^, flags, @newp)) of
|
|
RANGE_ERROR:
|
|
goto norm;
|
|
RANGE_MATCH:
|
|
pattern:=newp;
|
|
RANGE_NOMATCH:
|
|
Exit(FNM_NOMATCH);
|
|
end;
|
|
Inc(str);
|
|
end;
|
|
'\':
|
|
begin
|
|
if ((flags and FNM_NOESCAPE)=0) then
|
|
begin
|
|
c:=pattern^;
|
|
Inc(pattern);
|
|
if (c=EOS) then
|
|
begin
|
|
c:='\';
|
|
Dec(pattern);
|
|
end;
|
|
end;
|
|
goto norm;
|
|
end;
|
|
{ FALLTHROUGH }
|
|
else
|
|
begin
|
|
norm:
|
|
if (c=str^) then
|
|
begin
|
|
//
|
|
end else
|
|
if ((flags and FNM_CASEFOLD)<>0) and
|
|
(lowercase(c)=lowercase(str^)) then
|
|
begin
|
|
//
|
|
end else
|
|
Exit(FNM_NOMATCH);
|
|
Inc(str);
|
|
end;
|
|
end;
|
|
end;
|
|
{ NOTREACHED }
|
|
end;
|
|
|
|
function rangematch(pattern:pchar;test:char;flags:Integer;newp:ppchar):Integer;
|
|
var
|
|
negate,ok:Integer;
|
|
c,c2:char;
|
|
|
|
function _cond:Boolean; inline;
|
|
begin
|
|
c2:=(pattern+1)^;
|
|
Result:=(c2<>EOS) and (c2<>']');
|
|
end;
|
|
|
|
begin
|
|
{
|
|
* A bracket expression starting with an unquoted circumflex
|
|
* character produces unspecified results (IEEE 1003.2-1992,
|
|
* 3.13.2). This implementation treats it like '!', for
|
|
* consistency with the regular expression syntax.
|
|
* J.T. Conklin (conklin@ngai.kaleida.com)
|
|
}
|
|
negate:=ord((pattern^='!') or (pattern^='^'));
|
|
if (negate<>0) then
|
|
Inc(pattern);
|
|
|
|
if ((flags and FNM_CASEFOLD)<>0) then
|
|
test:=lowercase(test);
|
|
|
|
{
|
|
* A right bracket shall lose its special meaning and represent
|
|
* itself in a bracket expression if it occurs first in the list.
|
|
* -- POSIX.2 2.8.3.2
|
|
}
|
|
ok:=0;
|
|
c:=pattern^;
|
|
Inc(pattern);
|
|
repeat
|
|
if (c='\') and ((flags and FNM_NOESCAPE)=0) then
|
|
begin
|
|
c:=pattern^;
|
|
Inc(pattern);
|
|
end;
|
|
if (c=EOS) then
|
|
Exit(RANGE_ERROR);
|
|
|
|
if (c='/') and ((flags and FNM_PATHNAME)<>0) then
|
|
Exit(RANGE_NOMATCH);
|
|
|
|
if ((flags and FNM_CASEFOLD)<>0) then
|
|
c:=lowercase(c);
|
|
|
|
if (pattern^='-') and _cond then
|
|
begin
|
|
Inc(pattern,2);
|
|
if (c2='\') and ((flags and FNM_NOESCAPE)=0) then
|
|
begin
|
|
c2:=pattern^;
|
|
Inc(pattern);
|
|
end;
|
|
if (c2=EOS) then
|
|
Exit(RANGE_ERROR);
|
|
|
|
if ((flags and FNM_CASEFOLD)<>0) then
|
|
c2:=lowercase(c2);
|
|
|
|
if (c <= test) and (test <= c2) then
|
|
ok:=1;
|
|
end else
|
|
if (c=test) then
|
|
ok:=1;
|
|
c:=pattern^;
|
|
Inc(pattern);
|
|
until (c=']');
|
|
|
|
newp^:=pattern;
|
|
|
|
if (ok=negate) then
|
|
Exit(RANGE_NOMATCH)
|
|
else
|
|
Exit(RANGE_MATCH);
|
|
end;
|
|
|
|
|
|
end.
|
|
|