diff options
Diffstat (limited to 'lib/File')
-rw-r--r-- | lib/File/Spec/Mac.pm | 168 | ||||
-rw-r--r-- | lib/File/Spec/Unix.pm | 19 | ||||
-rw-r--r-- | lib/File/Spec/VMS.pm | 214 | ||||
-rw-r--r-- | lib/File/Spec/Win32.pm | 28 |
4 files changed, 399 insertions, 30 deletions
diff --git a/lib/File/Spec/Mac.pm b/lib/File/Spec/Mac.pm index e1f3c175ab..14da25a773 100644 --- a/lib/File/Spec/Mac.pm +++ b/lib/File/Spec/Mac.pm @@ -218,6 +218,174 @@ sub path { return split(/,/, $ENV{Commands}); } +=item splitpath + +=cut + +sub splitpath { + my ($self,$path, $nofile) = @_; + + my ($volume,$directory,$file) = ('','',''); + + if ( $nofile ) { + ( $volume, $directory ) = $path =~ m@((?:[^:]+(?::|$))?)(.*)@; + } + else { + $path =~ + m@^( (?: [^:]+: )? ) + ( (?: .*: )? ) + ( .* ) + @x; + $volume = $1; + $directory = $2; + $file = $3; + } + + # Make sure non-empty volumes and directories end in ':' + $volume .= ':' if $volume =~ m@[^:]$@ ; + $directory .= ':' if $directory =~ m@[^:]$@ ; + return ($volume,$directory,$file); +} + + +=item splitdir + +=cut + +sub splitdir { + my ($self,$directories) = @_ ; + # + # split() likes to forget about trailing null fields, so here we + # check to be sure that there will not be any before handling the + # simple case. + # + if ( $directories !~ m@:$@ ) { + return split( m@:@, $directories ); + } + else { + # + # since there was a trailing separator, add a file name to the end, + # then do the split, then replace it with ''. + # + my( @directories )= split( m@:@, "${directories}dummy" ) ; + $directories[ $#directories ]= '' ; + return @directories ; + } +} + + +=item catpath + +=cut + +sub catpath { + my $self = shift ; + + my $result = shift ; + $result =~ s@^([^/])@/$1@ ; + + my $segment ; + for $segment ( @_ ) { + if ( $result =~ m@[^/]$@ && $segment =~ m@^[^/]@ ) { + $result .= "/$segment" ; + } + elsif ( $result =~ m@/$@ && $segment =~ m@^/@ ) { + $result =~ s@/+$@/@; + $segment =~ s@^/+@@; + $result .= "$segment" ; + } + else { + $result .= $segment ; + } + } + + return $result ; +} + +=item abs2rel + +=cut + +sub abs2rel { + my($self,$path,$base) = @_; + + # Clean up $path + if ( ! $self->file_name_is_absolute( $path ) ) { + $path = $self->rel2abs( $path ) ; + } + + # Figure out the effective $base and clean it up. + if ( !defined( $base ) || $base eq '' ) { + $base = cwd() ; + } + elsif ( ! $self->file_name_is_absolute( $base ) ) { + $base = $self->rel2abs( $base ) ; + } + + # Now, remove all leading components that are the same + my @pathchunks = $self->splitdir( $path ); + my @basechunks = $self->splitdir( $base ); + + while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) { + shift @pathchunks ; + shift @basechunks ; + } + + $path = join( ':', @pathchunks ); + + # @basechunks now contains the number of directories to climb out of. + $base = ':' x @basechunks ; + + return "$base:$path" ; +} + +=item rel2abs + +Converts a relative path to an absolute path. + + $abs_path = $File::Spec->rel2abs( $destination ) ; + $abs_path = $File::Spec->rel2abs( $destination, $base ) ; + +If $base is not present or '', then L<cwd()> is used. If $base is relative, +then it is converted to absolute form using L</rel2abs()>. This means that it +is taken to be relative to L<cwd()>. + +On systems with the concept of a volume, this assumes that both paths +are on the $base volume, and ignores the $destination volume. + +On systems that have a grammar that indicates filenames, this ignores the +$base filename as well. Otherwise all path components are assumed to be +directories. + +If $path is absolute, it is cleaned up and returned using L</canonpath()>. + +Based on code written by Shigio Yamaguchi. + +No checks against the filesystem are made. + +=cut + +sub rel2abs($;$;) { + my ($self,$path,$base ) = @_; + + if ( ! $self->file_name_is_absolute( $path ) ) { + if ( !defined( $base ) || $base eq '' ) { + $base = cwd() ; + } + elsif ( ! $self->file_name_is_absolute( $base ) ) { + $base = $self->rel2abs( $base ) ; + } + else { + $base = $self->canonpath( $base ) ; + } + + $path = $self->canonpath("$base$path") ; + } + + return $path ; +} + + =back =head1 SEE ALSO diff --git a/lib/File/Spec/Unix.pm b/lib/File/Spec/Unix.pm index 85df2c2d3b..d47a60e9cc 100644 --- a/lib/File/Spec/Unix.pm +++ b/lib/File/Spec/Unix.pm @@ -26,28 +26,15 @@ No physical check on the filesystem, but a logical cleanup of a path. On UNIX eliminated successive slashes and successive "/.". $cpath = File::Spec->canonpath( $path ) ; - $cpath = File::Spec->canonpath( $path, $reduce_ricochet ) ; - -If $reduce_ricochet is present and true, then "dirname/.." -constructs are eliminated from the path. Without $reduce_ricochet, -if dirname is a symbolic link, then "a/dirname/../b" will often -take you to someplace other than "a/b". This is sometimes desirable. -If it's not, setting $reduce_ricochet causes the "dirname/.." to -be removed from this path, resulting in "a/b". This may make -your perl more portable and robust, unless you want to -ricochet (some scripts depend on it). =cut sub canonpath { - my ($self,$path,$reduce_ricochet) = @_; + my ($self,$path) = @_; $path =~ s|/+|/|g unless($^O eq 'cygwin'); # xx////xx -> xx/xx $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx $path =~ s|^(\./)+|| unless $path eq "./"; # ./xx -> xx $path =~ s|^/(\.\./)+|/|; # /../../xx -> xx - if ( $reduce_ricochet ) { - while ( $path =~ s@[^/]+/\.\.(?:/|$)@@ ) {}# xx/.. -> xx - } $path =~ s|/$|| unless $path eq "/"; # xx/ -> xx return $path; } @@ -281,8 +268,8 @@ sub splitdir { =item catpath Takes volume, directory and file portions and returns an entire path. Under -Unix, $volume is ignored, and this is just like catfile(). On other OSs, -the $volume become significant. +Unix, $volume is ignored, and directory and file are catenated. A '/' is +inserted if need be. On other OSs, $volume is significant. =cut diff --git a/lib/File/Spec/VMS.pm b/lib/File/Spec/VMS.pm index 79491463cd..71c38f222f 100644 --- a/lib/File/Spec/VMS.pm +++ b/lib/File/Spec/VMS.pm @@ -263,6 +263,220 @@ sub file_name_is_absolute { $file =~ /:[^<\[]/); } +=item splitpath + + ($volume,$directories,$file) = File::Spec->splitpath( $path ); + ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file ); + +Splits a VMS path in to volume, directory, and filename portions. +Ignores $no_file, if present, since VMS paths indicate the 'fileness' of a +file. + +The results can be passed to L</catpath()> to get back a path equivalent to +(usually identical to) the original path. + +=cut + +sub splitpath { + my $self = shift ; + my ($path, $nofile) = @_; + + my ($volume,$directory,$file) ; + + if ( $path =~ m{/} ) { + $path =~ + m{^ ( (?: /[^/]* )? ) + ( (?: .*/(?:[^/]+.dir)? )? ) + (.*) + }x; + $volume = $1; + $directory = $2; + $file = $3; + } + else { + $path =~ + m{^ ( (?: (?: (?: [\w\$-]+ (?: "[^"]*")?:: )? [\w\$-]+: )? ) ) + ( (?:\[.*\])? ) + (.*) + }x; + $volume = $1; + $directory = $2; + $file = $3; + } + + $directory = $1 + if $directory =~ /^\[(.*)\]$/ ; + + return ($volume,$directory,$file); +} + + +=item splitdir + +The opposite of L</catdir()>. + + @dirs = File::Spec->splitdir( $directories ); + +$directories must be only the directory portion of the path. + +'[' and ']' delimiters are optional. An empty string argument is +equivalent to '[]': both return an array with no elements. + +=cut + +sub splitdir { + my $self = shift ; + my $directories = $_[0] ; + + return File::Spec::Unix::splitdir( $self, @_ ) + if ( $directories =~ m{/} ) ; + + $directories =~ s/^\[(.*)\]$/$1/ ; + + # + # split() likes to forget about trailing null fields, so here we + # check to be sure that there will not be any before handling the + # simple case. + # + if ( $directories !~ m{\.$} ) { + return split( m{\.}, $directories ); + } + else { + # + # since there was a trailing separator, add a file name to the end, + # then do the split, then replace it with ''. + # + my( @directories )= split( m{\.}, "${directories}dummy" ) ; + $directories[ $#directories ]= '' ; + return @directories ; + } +} + + +sub catpath { + my $self = shift; + + return File::Spec::Unix::catpath( $self, @_ ) + if ( join( '', @_ ) =~ m{/} ) ; + + my ($volume,$directory,$file) = @_; + + $volume .= ':' + if $volume =~ /[^:]$/ ; + + $directory = "[$directory" + if $directory =~ /^[^\[]/ ; + + $directory .= ']' + if $directory =~ /[^\]]$/ ; + + return "$volume$directory$file" ; +} + + +sub abs2rel { + my $self = shift; + + return File::Spec::Unix::abs2rel( $self, @_ ) + if ( join( '', @_ ) =~ m{/} ) ; + + my($path,$base) = @_; + + # Note: we use '/' to glue things together here, then let canonpath() + # clean them up at the end. + + # Clean up $path + if ( ! $self->file_name_is_absolute( $path ) ) { + $path = $self->rel2abs( $path ) ; + } + else { + $path = $self->canonpath( $path ) ; + } + + # Figure out the effective $base and clean it up. + if ( ! $self->file_name_is_absolute( $base ) ) { + $base = $self->rel2abs( $base ) ; + } + elsif ( !defined( $base ) || $base eq '' ) { + $base = cwd() ; + } + else { + $base = $self->canonpath( $base ) ; + } + + # Split up paths + my ( undef, $path_directories, $path_file ) = + $self->splitpath( $path, 1 ) ; + + $path_directories = $1 + if $path_directories =~ /^\[(.*)\]$/ ; + + my ( undef, $base_directories, undef ) = + $self->splitpath( $base, 1 ) ; + + $base_directories = $1 + if $base_directories =~ /^\[(.*)\]$/ ; + + # Now, remove all leading components that are the same + my @pathchunks = $self->splitdir( $path_directories ); + my @basechunks = $self->splitdir( $base_directories ); + + while ( @pathchunks && + @basechunks && + lc( $pathchunks[0] ) eq lc( $basechunks[0] ) + ) { + shift @pathchunks ; + shift @basechunks ; + } + + # @basechunks now contains the directories to climb out of, + # @pathchunks now has the directories to descend in to. + $path_directories = '-.' x @basechunks . join( '.', @pathchunks ) ; + $path_directories =~ s{\.$}{} ; + return $self->catpath( '', $path_directories, $path_file ) ; +} + + +sub rel2abs($;$;) { + my $self = shift ; + return File::Spec::Unix::rel2abs( $self, @_ ) + if ( join( '', @_ ) =~ m{/} ) ; + + my ($path,$base ) = @_; + # Clean up and split up $path + if ( ! $self->file_name_is_absolute( $path ) ) { + # Figure out the effective $base and clean it up. + if ( !defined( $base ) || $base eq '' ) { + $base = 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 ) ; + + my ( $base_volume, $base_directories, undef ) = + $self->splitpath( $base ) ; + + my $sep = '' ; + $sep = '.' + if ( $base_directories =~ m{[^.]$} && + $path_directories =~ m{^[^.]} + ) ; + $base_directories = "$base_directories$sep$path_directories" ; + + $path = $self->catpath( $base_volume, $base_directories, $path_file ); + } + + return $self->canonpath( $path ) ; +} + + =back =head1 SEE ALSO diff --git a/lib/File/Spec/Win32.pm b/lib/File/Spec/Win32.pm index 120b799cd2..f1c6ccf8c7 100644 --- a/lib/File/Spec/Win32.pm +++ b/lib/File/Spec/Win32.pm @@ -95,7 +95,7 @@ path. On UNIX eliminated successive slashes and successive "/.". =cut sub canonpath { - my ($self,$path,$reduce_ricochet) = @_; + my ($self,$path) = @_; $path =~ s/^([a-z]:)/\u$1/; $path =~ s|/|\\|g; $path =~ s|([^\\])\\+|$1\\|g; # xx////xx -> xx/xx @@ -120,7 +120,7 @@ Separators accepted are \ and /. Volumes can be drive letters or UNC sharenames (\\server\share). -The results can be passed to L</catpath()> to get back a path equivalent to +The results can be passed to L</catpath> to get back a path equivalent to (usually identical to) the original path. =cut @@ -130,21 +130,21 @@ sub splitpath { my ($volume,$directory,$file) = ('','',''); if ( $nofile ) { $path =~ - m@^( (?:[a-zA-Z]:|(?:\\\\\\\\|//)[^\\\\/]+[\\\\/][^\\\\/]+)? ) + m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? ) (.*) - @x; + }x; $volume = $1; $directory = $2; } else { $path =~ - m@^ ( (?: [a-zA-Z]: | - (?:\\\\\\\\|//)[^\\\\/]+[\\\\/][^\\\\/]+ + m{^ ( (?: [a-zA-Z]: | + (?:\\\\|//)[^\\/]+[\\/][^\\/]+ )? ) ( (?:.*[\\\\/](?:\.\.?$)?)? ) (.*) - @x; + }x; $volume = $1; $directory = $2; $file = $3; @@ -221,8 +221,8 @@ sub catpath { # If the volume is not just A:, make sure the glue separator is # there, reusing whatever separator is first in the $volume if possible. if ( $volume !~ m@^[a-zA-Z]:$@ && - $volume !~ m@[\\/]$@ && - $file !~ m@^[\\/]@ + $volume =~ m@[^\\/]$@ && + $file =~ m@[^\\/]@ ) { $volume =~ m@([\\/])@ ; my $sep = $1 ? $1 : '\\' ; @@ -248,7 +248,7 @@ then it is converted to absolute form using L</rel2abs()>. This means that it is taken to be relative to L<cwd()>. On systems with the concept of a volume, this assumes that both paths -are on the $destination volume, and ignores the $base volume. +are on the $destination volume, and ignores the $base volume. On systems that have a grammar that indicates filenames, this ignores the $base filename as well. Otherwise all path components are assumed to be @@ -325,8 +325,11 @@ sub abs2rel { $path_directories = "$base_directories$path_directories" ; } + # It makes no sense to add a relative path to a UNC volume + $path_volume = '' unless $path_volume =~ m{^[A-Z]:}i ; + return $self->canonpath( - $self->catpath( $path_volume, $path_directories, $path_file ) + $self->catpath($path_volume, $path_directories, $path_file ) ) ; } @@ -359,10 +362,8 @@ No checks against the filesystem are made. sub rel2abs($;$;) { my ($self,$path,$base ) = @_; - # Clean up and split up $path if ( ! $self->file_name_is_absolute( $path ) ) { - # Figure out the effective $base and clean it up. if ( ! $self->file_name_is_absolute( $base ) ) { $base = $self->rel2abs( $base ) ; } @@ -373,7 +374,6 @@ sub rel2abs($;$;) { $base = $self->canonpath( $base ) ; } - # Split up paths my ( undef, $path_directories, $path_file ) = $self->splitpath( $path, 1 ) ; |