LazUtils: Resolve '/somedir/..' correctly in ResolveDots. Add tests. Issue #40678.

(cherry picked from commit 148b2d23f4)
This commit is contained in:
Juha 2024-01-11 05:47:39 +02:00 committed by Maxim Ganetsky
parent 7c62769d75
commit 9835b964fb
3 changed files with 18 additions and 36 deletions

View File

@ -21,14 +21,13 @@ function ResolveDots(const AFilename: string): string;
Result:=false; Result:=false;
end; end;
var SrcPos, DestPos, Len, DirStart: integer; var
SrcPos, DestPos, Len, DirStart: integer;
c: char; c: char;
MacroPos: LongInt; MacroPos: LongInt;
ResultMustEndWithPathDelim: Boolean;
begin begin
Len:=length(AFilename); Len:=length(AFilename);
if Len=0 then exit(''); if Len=0 then exit('');
Result:=AFilename; Result:=AFilename;
{$ifdef windows} {$ifdef windows}
@ -38,13 +37,6 @@ begin
exit; exit;
{$endif} {$endif}
//To add some consistency to the outcomes
//Depending on the path the algorithm takes it may remove the trailing PathDelim, so we restore it later if needed
//Issue #37188
//It's a workaround, fee free to implement a better fix
ResultMustEndWithPathDelim := ((Len>2) and (AFilename[Len]='.') and (AFilename[Len-1]='.') and (AFilename[Len-2] in AllowDirectorySeparators)) or
((Len>1) and (AFilename[Len]='.') and (AFilename[Len-1] in AllowDirectorySeparators));
SrcPos:=1; SrcPos:=1;
DestPos:=1; DestPos:=1;
@ -76,8 +68,7 @@ begin
if (SrcPos<Len) then begin if (SrcPos<Len) then begin
if (AFilename[SrcPos+1] in AllowDirectorySeparators) if (AFilename[SrcPos+1] in AllowDirectorySeparators)
and IsPathDelim(Result,DestPos-1) then begin and IsPathDelim(Result,DestPos-1) then begin
// special dir ./ or */./ // special dir ./ or */./ -> skip
// -> skip
inc(SrcPos,2); inc(SrcPos,2);
while (SrcPos<=Len) and (AFilename[SrcPos] in AllowDirectorySeparators) do while (SrcPos<=Len) and (AFilename[SrcPos] in AllowDirectorySeparators) do
inc(SrcPos); inc(SrcPos);
@ -145,12 +136,8 @@ begin
//writeln('ResolveDots ',DestPos,' SrcPos=',SrcPos,' File="',AFilename,'" Result="',copy(Result,1,DestPos-1),'"'); //writeln('ResolveDots ',DestPos,' SrcPos=',SrcPos,' File="',AFilename,'" Result="',copy(Result,1,DestPos-1),'"');
if SrcPos>Len then begin if SrcPos>Len then begin
// '..' at end of filename // '..' at end of filename
if (DestPos>1) and (Result[DestPos-1]=PathDelim) then begin if (DestPos=1) then begin
// foo/dir/.. -> foo Result[1]:='.'; // foo/.. -> .
dec(DestPos);
end else if (DestPos=1) then begin
// foo/.. -> .
Result[1]:='.';
DestPos:=2; DestPos:=2;
end; end;
end else if DestPos=1 then begin end else if DestPos=1 then begin
@ -165,20 +152,8 @@ begin
end; end;
end else begin end else begin
// special dir . at end of filename // special dir . at end of filename
if DestPos=1 then begin if DestPos=1 then
Result:='.'; exit('.');
exit;
end;
if (DestPos>2) and (Result[DestPos-1]=PathDelim)
{$ifdef windows}
and not IsDriveDelim(Result,DestPos-2)
{$endif}
then begin
// foo/. -> foo
// C:foo\. -> C:foo
// C:\. -> C:\
{dec(DestPos); } //Part of issue #37188
end;
break; break;
end; end;
end; end;
@ -198,12 +173,10 @@ begin
end; end;
// trim result // trim result
if DestPos<=length(AFilename) then if DestPos<=length(AFilename) then
if (DestPos=1) then if DestPos=1 then
Result:='.' Result:='.'
else else
SetLength(Result,DestPos-1); SetLength(Result,DestPos-1);
if ResultMustEndWithPathDelim and (Result<>'.') and (Result[Length(Result)]<>PathDelim) then
Result := Result + PathDelim;
end; end;
function FilenameIsWinAbsolute(const TheFilename: string): boolean; function FilenameIsWinAbsolute(const TheFilename: string): boolean;

View File

@ -63,7 +63,7 @@ begin
t('..','..'); t('..','..');
t('bla/..','.'); t('bla/..','.');
t('bla//..','.'); t('bla//..','.');
t('foo/bla//..','foo'); t('foo/bla//..','foo/');
t('bla/../','.'); t('bla/../','.');
t('bla//..//','.'); t('bla//..//','.');
t('foo/../bar','bar'); t('foo/../bar','bar');
@ -82,6 +82,8 @@ begin
t('.//.//.','.'); t('.//.//.','.');
t('foo/bar/./../too','foo/too'); t('foo/bar/./../too','foo/too');
t('foo//bar//.//..//too','foo/too'); t('foo//bar//.//..//too','foo/too');
t('/bla/..','/');
t('/..','/');
{$IFDEF Unix} {$IFDEF Unix}
t('/.','/'); t('/.','/');
t('//.','/'); t('//.','/');
@ -142,10 +144,12 @@ begin
DoTest('a/../b','b'); DoTest('a/../b','b');
DoTest('a/b/../c','a/c'); DoTest('a/b/../c','a/c');
DoTest('a/b/../../c','c'); DoTest('a/b/../../c','c');
DoTest('a/b/c/../..','a/');
DoTest('a/./b','a/b'); DoTest('a/./b','a/b');
DoTest('a/.//b','a/b'); DoTest('a/.//b','a/b');
DoTest('a//b','a/b'); DoTest('a//b','a/b');
DoTest('a//./b','a/b'); DoTest('a//./b','a/b');
DoTest('/a/b/../..','/');
end; end;
procedure TTestLazFileUtils.TestCreateRelativePath; procedure TTestLazFileUtils.TestCreateRelativePath;

View File

@ -178,6 +178,11 @@
<StackChecks Value="True"/> <StackChecks Value="True"/>
</Checks> </Checks>
</CodeGeneration> </CodeGeneration>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf3"/>
</Debugging>
</Linking>
<Other> <Other>
<CustomOptions Value="-dNoSemiAutomatedTests"/> <CustomOptions Value="-dNoSemiAutomatedTests"/>
</Other> </Other>