Then you can't do it. On a file system that supports symbolic links,
like Unix file systems do, "/export/home/bob/../steve/bin/./" and
"/export/home/steve/bin" are not necessarily the same, if
/export/home/bob is a symbolic link to somewhere else.
Assume /export/home/bob is a symlink to /auto/home/bob, then
/export/home/bob/../steve/./ will refer to /auto/home/steve,
which is not the same as /export/home/steve (unless
/export/home/steve is also a symlink to /auto/home/steve).
You are of course right, but this isn't a platform-independence
problem. It is a problem inherent in filesystems with symlinks: there
are simply two ways to resolve the path /export/home/bob/../steve.
The 'logical' way is to /export/home/steve; I don't know of a Perl
module that will do this. You could write a function to do it fairly
simply: (untested)
use File::Spec::Function qw/:ALL/;
use Carp;
sub realpath_log {
my $path = canonpath shift;
# this assumes the path refers to a directory
my ($vol, $dirs) = splitpath $path, 'no file';
my @dirs = splitdir $dirs;
my ($ups, @real);
for (reverse @dirs) {
# this assumes there is only one representation of ..
$_ eq updir and $ups++, next;
$ups and $ups--, next;
push @real, $_;
}
# On unix systems, canonpath will convert /../.. to /. If this
# hasn't been done, it must not hold on this OS, so we have an
# invalid path.
$ups and carp "invalid path: $path", return;
return catpath $vol, catdir @real;
}
The 'physical' way is to /auto/home/steve; you can do that, as you
say, with Cwd::abs_path; provided the file exists. If it doesn't,
Cwd::abs_path will fail. The OP needs to decide which of the two
alternatives he wants.
The Cwd module (standard with Perl) has the realpath() or abs_path()
function, which can give you the canonical pathname of their argument.
If you do that for both, and compare those results, you should be able
to tell.
I would not recommend simply comparing the absolute (or even
canonical) paths: while it will work on Unix, it may well not on other
systems[1]. I have used this routine in the past:
use File::Spec::Functions qw/:ALL/;
# Returns true if $ful is a subdir of $pre.
# This assumes that both paths passed represent directories rather
# than files. On some systems, this makes a difference.
# This will *not* remove /../ components before testing. If you need
# to, change the 'canonpath's into 'abs_path's.
BEGIN { *is_abs = \&File::Spec::Functions::file_name_is_absolute }
# Who came up with that name??
sub is_prefix {
my ($pre, $ful) = @_;
if (is_abs $pre or is_abs $ful) {
$pre = rel2abs $pre;
$ful = rel2abs $ful;
}
my (@pre, @ful, $d);
# deal with a potential volume component
($pre[0], $d) = splitpath canonpath($pre), 'no file';
push @pre, splitdir $d;
($ful[0], $d, undef) = splitpath canonpath($ful);
push @ful, splitdir $d;
return not grep { case_tolerant() ?
lc $pre[$_] ne lc $ful[$_] :
$pre[$_] ne $ful[$_] } 0..$#pre;
}
[1] VMS with a VMSish path and a Unixish path; or even Win32 with one
path using \ and one path using /.
Ben