4

Is there a built-in way to compare two strings representing paths in Inno Setup Pascal? If not with one function, then perhaps via some normalisation of the path.

Naively comparing strings is obviously not correct, even if we ignore case with SameText() (as per the Windows rules).

As a minimum, correct comparison must

  • Treat \ and / as identical
  • Ignore multiple separators like \\ (theat them as one, like the OS)
  • Ignore trailing separators (to compare directory paths correctly, for which it is mainly needed)
  • Resolve paths (foo\..\bar equals bar, at least if foo exists)
  • etc. (rules are well known)
  • Not require the paths actually exist in the file system.

Resolving absolute vs. relative paths is a bonus, but it requires specifying the current path. Perhaps CWD is OK, but I'm not sure Inno accepts relative installation paths anyway.

This must be a fairly common task for an installer, but I'm surprised not to find an easy yet correct solution...

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Zeus
  • 1,052
  • 1
  • 9
  • 18

1 Answers1

3

Combine ExpandFileName, AddBackslash and SameText:

function SamePath(P1, P2: string): Boolean;
begin
  P1 := AddBackslash(ExpandFileName(P1));
  P2 := AddBackslash(ExpandFileName(P2));
  Result := SameText(P1, P2);
end;

The ExpandFileName:

  • Converts / to \.
  • Normalizes a sequence of any slashes to one backslash (except for leading backslashes in UNC paths).
  • Resolves relative paths.

The AddBackslash takes care of ignoring the trailing separators.


Tests:

procedure TestSamePath(P: string);
begin
  if not SamePath(P, 'C:\my\path\MyProg.exe') then
    RaiseException('Test failed: ' + P);
end;

function InitializeSetup(): Boolean;
begin
  TestSamePath('C:\my\path\MyProg.exe');
  TestSamePath('C:\my\path\MYPROG.exe');
  TestSamePath('C:\my\path\\MyProg.exe');
  TestSamePath('C:/my/path/MyProg.exe');
  TestSamePath('C:\my/path//MyProg.exe');
  TestSamePath('C:\my\path\MyProg.exe\');
  TestSamePath('C:\my\..\my\path\MyProg.exe');

  SetCurrentDir('C:\');
  TestSamePath('\my\path\MyProg.exe');
  TestSamePath('my\path\MyProg.exe');

  SetCurrentDir('C:\my');
  TestSamePath('path\MyProg.exe');
  TestSamePath('.\path\MyProg.exe');
  Result := True;
end;
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • 1
    Yes, that seems to be the way... (`RemoveBackslashUnlessRoot()` would also work). I still find it surprising there is no built-in function for this even in such a specialised tool (not to mention .NET). `ExpandFileName()` does 90% of the job, but ideally the comparison would figure out how to threat character case in a portable way... – Zeus Dec 05 '21 at 23:50