$path =~ s|(\\\.)+\\|\\|g; # xx\.\.\xx -> xx\xx
$path =~ s|^(\.\\)+||s unless $path eq ".\\"; # .\xx -> xx
$path =~ s|\\\Z(?!\n)||
- unless $path =~ m#^([A-Z]:)?\\\Z(?!\n)#s; # xx\ -> xx
- # xx1/xx2/xx3/../../xx -> xx1/xx
- $path =~ s|\\\.\.\.\\|\\\.\.\\\.\.\\|g; # \...\ is 2 levels up
- $path =~ s|^\.\.\.\\|\.\.\\\.\.\\|g; # ...\ is 2 levels up
- return $path if $path =~ m|^\.\.|; # skip relative paths
- return $path unless $path =~ /\.\./; # too few .'s to cleanup
- return $path if $path =~ /\.\.\.\./; # too many .'s to cleanup
- return $path if $orig_path =~ m|^\Q/../\E|
- and $orig_path =~ m|\/$|; # don't do /../dirs/
- # when called from rel2abs()
- # for ../dirs/
- my ($vol,$dirs,$file) = $self->splitpath($path);
- my @dirs = $self->splitdir($dirs);
- my (@base_dirs, @path_dirs);
- my $dest = \@base_dirs;
- for my $dir (@dirs){
- $dest = \@path_dirs if $dir eq $self->updir;
- push @$dest, $dir;
- }
- # for each .. in @path_dirs pop one item from
- # @base_dirs
- while (my $dir = shift @path_dirs){
- unless ($dir eq $self->updir){
- unshift @path_dirs, $dir;
- last;
- }
- pop @base_dirs;
+ unless $path =~ m{^([A-Z]:)?\\\Z(?!\n)}s; # xx\ -> xx
+ # xx1/xx2/xx3/../../xx -> xx1/xx
+ $path =~ s|\\\.\.\.\\|\\\.\.\\\.\.\\|g; # \...\ is 2 levels up
+ $path =~ s|^\.\.\.\\|\.\.\\\.\.\\|g; # ...\ is 2 levels up
+ return $path if $path =~ m|^\.\.|; # skip relative paths
+ return $path unless $path =~ /\.\./; # too few .'s to cleanup
+ return $path if $path =~ /\.\.\.\./; # too many .'s to cleanup
+ return $path if $orig_path =~ m|^\Q/../\E|
+ and $orig_path =~ m|\/$|; # don't do /../dirs/ when called
+ # from rel2abs() for ../dirs/
+ 1 while $path =~ s{^\\\.\.}{}; # \..\xx -> \xx
+
+ my ($vol,$dirs,$file) = $self->splitpath($path);
+ my @dirs = $self->splitdir($dirs);
+ my (@base_dirs, @path_dirs);
+ my $dest = \@base_dirs;
+ for my $dir (@dirs){
+ $dest = \@path_dirs if $dir eq $self->updir;
+ push @$dest, $dir;
+ }
+ # for each .. in @path_dirs pop one item from
+ # @base_dirs
+ while (my $dir = shift @path_dirs){
+ unless ($dir eq $self->updir){
+ unshift @path_dirs, $dir;
+ last;
}
- $path = $self->catpath(
- $vol,
- $self->catdir(@base_dirs, @path_dirs),
- $file
- );
+ pop @base_dirs;
+ }
+ $path = $self->catpath(
+ $vol,
+ $self->catdir(@base_dirs, @path_dirs),
+ $file
+ );
return $path;
}
sub abs2rel {
my($self,$path,$base) = @_;
+ $base = $self->cwd() unless defined $base and length $base;
- # Clean up $path
- if ( ! $self->file_name_is_absolute( $path ) ) {
- $path = $self->rel2abs( $path ) ;
- }
- else {
- $path = $self->canonpath( $path ) ;
+ for ($path, $base) {
+ $_ = $self->canonpath($self->rel2abs($_));
}
+ my ($path_volume, $path_directories) = $self->splitpath($path, 1) ;
+ my ($base_volume, $base_directories) = $self->splitpath($base, 1);
- # Figure out the effective $base and clean it up.
- if ( !defined( $base ) || $base eq '' ) {
- $base = cwd() ;
+ if ($path_volume and not $base_volume) {
+ ($base_volume) = $self->splitpath($self->cwd);
}
- elsif ( ! $self->file_name_is_absolute( $base ) ) {
- $base = $self->rel2abs( $base ) ;
- }
- else {
- $base = $self->canonpath( $base ) ;
- }
-
- # Split up paths
- my ( undef, $path_directories, $path_file ) =
- $self->splitpath( $path, 1 ) ;
- my $base_directories = ($self->splitpath( $base, 1 ))[1] ;
+ # Can't relativize across volumes
+ return $path unless $path_volume eq $base_volume;
# Now, remove all leading components that are the same
my @pathchunks = $self->splitdir( $path_directories );
shift @basechunks ;
}
- # No need to catdir, we know these are well formed.
- $path_directories = CORE::join( '\\', @pathchunks );
- $base_directories = CORE::join( '\\', @basechunks );
-
- # $base_directories now contains the directories the resulting relative
- # path must ascend out of before it can descend to $path_directory. So,
- # replace all names with $parentDir
-
- #FA Need to replace between backslashes...
- $base_directories =~ s|[^\\]+|..|g ;
-
- # Glue the two together, using a separator if necessary, and preventing an
- # empty result.
-
- #FA Must check that new directories are not empty.
- if ( $path_directories ne '' && $base_directories ne '' ) {
- $path_directories = "$base_directories\\$path_directories" ;
- } else {
- $path_directories = "$base_directories$path_directories" ;
- }
+ my $result_dirs = $self->catdir( ($self->updir) x @basechunks, @pathchunks );
- return $self->canonpath(
- $self->catpath( "", $path_directories, $path_file )
- ) ;
+ return $self->canonpath( $self->catpath('', $result_dirs, '') );
}
if ( ! $self->file_name_is_absolute( $path ) ) {
if ( !defined( $base ) || $base eq '' ) {
- $base = cwd() ;
+ $base = $self->cwd() ;
}
elsif ( ! $self->file_name_is_absolute( $base ) ) {
$base = $self->rel2abs( $base ) ;
-#!./perl
+#!/usr/bin/perl -w
+
+use Test;
-BEGIN {
- chdir 't' if -d 't';
- @INC = '../lib';
- if ($^O eq 'MacOS') {
- push @INC, "::lib:$MacPerl::Architecture";
- }
-}
# Grab all of the plain routines from File::Spec
use File::Spec @File::Spec::EXPORT_OK ;
require File::Spec::OS2 ;
require File::Spec::Mac ;
+require File::Spec::Epoc ;
+require File::Spec::Cygwin ;
# $root is only needed by Mac OS tests; these particular
# tests are skipped on other OSs
-my $root;
+my $root = '';
if ($^O eq 'MacOS') {
$root = File::Spec::Mac->rootdir();
}
@tests = (
# [ Function , Expected , Platform ]
+[ "Unix->case_tolerant()", '0' ],
+
[ "Unix->catfile('a','b','c')", 'a/b/c' ],
[ "Unix->catfile('a','b','./c')", 'a/b/c' ],
[ "Unix->catfile('./a','b','c')", 'a/b/c' ],
[ "Unix->rel2abs('../t4','/t1/t2/t3')", '/t1/t2/t3/../t4' ],
[ "Unix->rel2abs('/t1','/t1/t2/t3')", '/t1' ],
+[ "Win32->case_tolerant()", '1' ],
+
[ "Win32->splitpath('file')", ',,file' ],
[ "Win32->splitpath('\\d1/d2\\d3/')", ',\\d1/d2\\d3/,' ],
[ "Win32->splitpath('d1/d2\\d3/')", ',d1/d2\\d3/,' ],
[ "Win32->catdir('')", '\\' ],
[ "Win32->catdir('/')", '\\' ],
[ "Win32->catdir('//d1','d2')", '\\\\d1\\d2' ],
+[ "Win32->catdir('\\d1\\','d2')", '\\d1\\d2' ],
+[ "Win32->catdir('\\d1','d2')", '\\d1\\d2' ],
+[ "Win32->catdir('\\d1','\\d2')", '\\d1\\d2' ],
+[ "Win32->catdir('\\d1','\\d2\\')", '\\d1\\d2' ],
[ "Win32->catdir('','/d1','d2')", '\\\\d1\\d2' ],
[ "Win32->catdir('','','/d1','d2')", '\\\\\\d1\\d2' ],
[ "Win32->catdir('','//d1','d2')", '\\\\\\d1\\d2' ],
[ "Win32->canonpath('//a/b/c/.../d')", '\\\\a\\b\\d' ],
[ "Win32->canonpath('/a/b/c/../../d')", '\\a\\d' ],
[ "Win32->canonpath('/a/b/c/.../d')", '\\a\\d' ],
-
-## Hmmm, we should test missing and relative base paths some day...
-## would need to cd to a known place, get the cwd() and use it I
-## think.
-[ "Win32->abs2rel('/t1/t2/t3','/t1/t2/t3')", '' ],
-[ "Win32->abs2rel('/t1/t2/t4','/t1/t2/t3')", '..\\t4' ],
-[ "Win32->abs2rel('/t1/t2','/t1/t2/t3')", '..' ],
-[ "Win32->abs2rel('/t1/t2/t3/t4','/t1/t2/t3')", 't4' ],
-[ "Win32->abs2rel('/t4/t5/t6','/t1/t2/t3')", '..\\..\\..\\t4\\t5\\t6' ],
-#[ "Win32->abs2rel('../t4','/t1/t2/t3')", '\\t1\\t2\\t3\\..\\t4' ],
-[ "Win32->abs2rel('/','/t1/t2/t3')", '..\\..\\..' ],
-[ "Win32->abs2rel('///','/t1/t2/t3')", '..\\..\\..' ],
-[ "Win32->abs2rel('/.','/t1/t2/t3')", '..\\..\\..\\.' ],
-[ "Win32->abs2rel('/./','/t1/t2/t3')", '..\\..\\..' ],
-[ "Win32->abs2rel('\\\\a/t1/t2/t4','/t2/t3')", '..\\t4' ],
-[ "Win32->abs2rel('//a/t1/t2/t4','/t2/t3')", '..\\t4' ],
-[ "Win32->abs2rel('A:/t1/t2/t3','B:/t1/t2/t3')",'' ],
-[ "Win32->abs2rel('A:/t1/t2/t3/t4','B:/t1/t2/t3')",'t4' ],
-
-[ "Win32->rel2abs('temp','C:/')", 'C:\\temp' ],
-[ "Win32->rel2abs('temp','C:/a')", 'C:\\a\\temp' ],
-[ "Win32->rel2abs('temp','C:/a/')", 'C:\\a\\temp' ],
-[ "Win32->rel2abs('../','C:/')", 'C:\\' ],
-[ "Win32->rel2abs('../','C:/a')", 'C:\\' ],
-[ "Win32->rel2abs('temp','//prague_main/work/')", '\\\\prague_main\\work\\temp' ],
-[ "Win32->rel2abs('../temp','//prague_main/work/')", '\\\\prague_main\\work\\temp' ],
-[ "Win32->rel2abs('temp','//prague_main/work')", '\\\\prague_main\\work\\temp' ],
-[ "Win32->rel2abs('../','//prague_main/work')", '\\\\prague_main\\work' ],
+[ "Win32->canonpath('\\../temp\\')", '\\temp' ],
+
+# FakeWin32 subclass (see below) just sets CWD to C:\one\two
+
+[ "FakeWin32->abs2rel('/t1/t2/t3','/t1/t2/t3')", '' ],
+[ "FakeWin32->abs2rel('/t1/t2/t4','/t1/t2/t3')", '..\\t4' ],
+[ "FakeWin32->abs2rel('/t1/t2','/t1/t2/t3')", '..' ],
+[ "FakeWin32->abs2rel('/t1/t2/t3/t4','/t1/t2/t3')", 't4' ],
+[ "FakeWin32->abs2rel('/t4/t5/t6','/t1/t2/t3')", '..\\..\\..\\t4\\t5\\t6' ],
+[ "FakeWin32->abs2rel('../t4','/t1/t2/t3')", '..\\..\\..\\one\\t4' ],
+[ "FakeWin32->abs2rel('/','/t1/t2/t3')", '..\\..\\..' ],
+[ "FakeWin32->abs2rel('///','/t1/t2/t3')", '..\\..\\..' ],
+[ "FakeWin32->abs2rel('/.','/t1/t2/t3')", '..\\..\\..' ],
+[ "FakeWin32->abs2rel('/./','/t1/t2/t3')", '..\\..\\..' ],
+[ "FakeWin32->abs2rel('\\\\a/t1/t2/t4','/t2/t3')", '\\\\a\\t1\\t2\\t4' ],
+[ "FakeWin32->abs2rel('//a/t1/t2/t4','/t2/t3')", '\\\\a\\t1\\t2\\t4' ],
+[ "FakeWin32->abs2rel('A:/t1/t2/t3','A:/t1/t2/t3')", '' ],
+[ "FakeWin32->abs2rel('A:/t1/t2/t3/t4','A:/t1/t2/t3')", 't4' ],
+[ "FakeWin32->abs2rel('A:/t1/t2/t3','A:/t1/t2/t3/t4')", '..' ],
+[ "FakeWin32->abs2rel('A:/t1/t2/t3','B:/t1/t2/t3')", 'A:\\t1\\t2\\t3' ],
+[ "FakeWin32->abs2rel('A:/t1/t2/t3/t4','B:/t1/t2/t3')", 'A:\\t1\\t2\\t3\\t4' ],
+[ "FakeWin32->abs2rel('E:/foo/bar/baz')", 'E:\\foo\\bar\\baz' ],
+[ "FakeWin32->abs2rel('C:/one/two/three')", 'three' ],
+
+[ "FakeWin32->rel2abs('temp','C:/')", 'C:\\temp' ],
+[ "FakeWin32->rel2abs('temp','C:/a')", 'C:\\a\\temp' ],
+[ "FakeWin32->rel2abs('temp','C:/a/')", 'C:\\a\\temp' ],
+[ "FakeWin32->rel2abs('../','C:/')", 'C:\\' ],
+[ "FakeWin32->rel2abs('../','C:/a')", 'C:\\' ],
+[ "FakeWin32->rel2abs('temp','//prague_main/work/')", '\\\\prague_main\\work\\temp' ],
+[ "FakeWin32->rel2abs('../temp','//prague_main/work/')", '\\\\prague_main\\work\\temp' ],
+[ "FakeWin32->rel2abs('temp','//prague_main/work')", '\\\\prague_main\\work\\temp' ],
+[ "FakeWin32->rel2abs('../','//prague_main/work')", '\\\\prague_main\\work' ],
+
+[ "VMS->case_tolerant()", '1' ],
[ "VMS->catfile('a','b','c')", '[.a.b]c' ],
[ "VMS->catfile('a','b','[]c')", '[.a.b]c' ],
[ "VMS->rel2abs('[-.t4]','[t1.t2.t3]')", '[t1.t2.t4]' ],
[ "VMS->rel2abs('[t1]','[t1.t2.t3]')", '[t1]' ],
+[ "OS2->case_tolerant()", '1' ],
+
[ "OS2->catdir('A:/d1','B:/d2','d3','')", 'A:/d1/B:/d2/d3' ],
[ "OS2->catfile('a','b','c')", 'a/b/c' ],
[ "OS2->catfile('c')", 'c' ],
[ "OS2->catfile('./c')", 'c' ],
+[ "Mac->case_tolerant()", '1' ],
[ "Mac->catpath('','','')", '' ],
[ "Mac->catpath('',':','')", ':' ],
[ "Mac->rel2abs('hd:','hd:d1:d2:')", 'hd:' ], # path already absolute
[ "Mac->rel2abs('hd:d3:file','hd:d1:d2:')", 'hd:d3:file' ],
[ "Mac->rel2abs('hd:d3:','hd:d1:file')", 'hd:d3:' ],
+
+[ "Epoc->case_tolerant()", '1' ],
+
+[ "Epoc->canonpath('')", '' ],
+[ "Epoc->canonpath('///../../..//./././a//b/.././c/././')", '/a/b/../c' ],
+[ "Epoc->canonpath('/./')", '/' ],
+[ "Epoc->canonpath('/a/./')", '/a' ],
+
+# XXX Todo, copied from Unix, but fail. Should they? 2003-07-07 Tels
+#[ "Epoc->canonpath('/a/.')", '/a' ],
+#[ "Epoc->canonpath('/.')", '/' ],
+
+[ "Cygwin->case_tolerant()", '0' ],
+
) ;
+plan tests => scalar @tests;
-print "1..", scalar( @tests ), "\n" ;
+{
+ @File::Spec::FakeWin32::ISA = qw(File::Spec::Win32);
+ sub File::Spec::FakeWin32::cwd { 'C:\\one\\two' }
+}
-my $current_test= 1 ;
# Test out the class methods
for ( @tests ) {
my $platform = shift ;
if ($platform && $^O ne $platform) {
- print "ok $current_test # skipped: $function\n" ;
- ++$current_test ;
+ skip("skip $function", 1);
return;
}
$function =~ s#\\#\\\\#g ;
-
- my $got ;
- if ( $function =~ /^[^\$].*->/ ) {
- $got = eval( "join( ',', File::Spec::$function )" ) ;
- }
- else {
- $got = eval( "join( ',', $function )" ) ;
- }
+ $function =~ s/^([^\$].*->)/File::Spec::$1/;
+ my $got = join ',', eval $function;
if ( $@ ) {
- if ( substr( $@, 0, length $skip_exception ) eq $skip_exception ) {
- chomp $@ ;
- print "ok $current_test # skip $function: $@\n" ;
- }
- else {
- chomp $@ ;
- print "not ok $current_test # $function: $@\n" ;
- }
- }
- elsif ( !defined( $got ) || $got ne $expected ) {
- print "not ok $current_test # $function: got '$got', expected '$expected'\n" ;
+ if ( $@ =~ /^\Q$skip_exception/ ) {
+ skip "skip $function: $skip_exception", 1;
+ }
+ else {
+ ok $@, '', $function;
+ }
+ return;
}
- else {
- print "ok $current_test # $function\n" ;
- }
- ++$current_test ;
+
+ ok $got, $expected, $function;
}