16fb12b70Safresh1package File::Spec::Mac; 26fb12b70Safresh1 36fb12b70Safresh1use strict; 49f11ffb7Safresh1use Cwd (); 56fb12b70Safresh1require File::Spec::Unix; 66fb12b70Safresh1 7*3d61058aSafresh1our $VERSION = '3.91'; 8b8851fccSafresh1$VERSION =~ tr/_//d; 96fb12b70Safresh1 109f11ffb7Safresh1our @ISA = qw(File::Spec::Unix); 116fb12b70Safresh1 126fb12b70Safresh1sub case_tolerant { 1 } 136fb12b70Safresh1 146fb12b70Safresh1 156fb12b70Safresh1=head1 NAME 166fb12b70Safresh1 176fb12b70Safresh1File::Spec::Mac - File::Spec for Mac OS (Classic) 186fb12b70Safresh1 196fb12b70Safresh1=head1 SYNOPSIS 206fb12b70Safresh1 216fb12b70Safresh1 require File::Spec::Mac; # Done internally by File::Spec if needed 226fb12b70Safresh1 236fb12b70Safresh1=head1 DESCRIPTION 246fb12b70Safresh1 256fb12b70Safresh1Methods for manipulating file specifications. 266fb12b70Safresh1 276fb12b70Safresh1=head1 METHODS 286fb12b70Safresh1 296fb12b70Safresh1=over 2 306fb12b70Safresh1 316fb12b70Safresh1=item canonpath 326fb12b70Safresh1 336fb12b70Safresh1On Mac OS, there's nothing to be done. Returns what it's given. 346fb12b70Safresh1 356fb12b70Safresh1=cut 366fb12b70Safresh1 376fb12b70Safresh1sub canonpath { 386fb12b70Safresh1 my ($self,$path) = @_; 396fb12b70Safresh1 return $path; 406fb12b70Safresh1} 416fb12b70Safresh1 426fb12b70Safresh1=item catdir() 436fb12b70Safresh1 446fb12b70Safresh1Concatenate two or more directory names to form a path separated by colons 456fb12b70Safresh1(":") ending with a directory. Resulting paths are B<relative> by default, 466fb12b70Safresh1but can be forced to be absolute (but avoid this, see below). Automatically 476fb12b70Safresh1puts a trailing ":" on the end of the complete path, because that's what's 486fb12b70Safresh1done in MacPerl's environment and helps to distinguish a file path from a 496fb12b70Safresh1directory path. 506fb12b70Safresh1 516fb12b70Safresh1B<IMPORTANT NOTE:> Beginning with version 1.3 of this module, the resulting 526fb12b70Safresh1path is relative by default and I<not> absolute. This decision was made due 536fb12b70Safresh1to portability reasons. Since C<File::Spec-E<gt>catdir()> returns relative paths 546fb12b70Safresh1on all other operating systems, it will now also follow this convention on Mac 556fb12b70Safresh1OS. Note that this may break some existing scripts. 566fb12b70Safresh1 576fb12b70Safresh1The intended purpose of this routine is to concatenate I<directory names>. 586fb12b70Safresh1But because of the nature of Macintosh paths, some additional possibilities 596fb12b70Safresh1are allowed to make using this routine give reasonable results for some 606fb12b70Safresh1common situations. In other words, you are also allowed to concatenate 616fb12b70Safresh1I<paths> instead of directory names (strictly speaking, a string like ":a" 626fb12b70Safresh1is a path, but not a name, since it contains a punctuation character ":"). 636fb12b70Safresh1 646fb12b70Safresh1So, beside calls like 656fb12b70Safresh1 666fb12b70Safresh1 catdir("a") = ":a:" 676fb12b70Safresh1 catdir("a","b") = ":a:b:" 686fb12b70Safresh1 catdir() = "" (special case) 696fb12b70Safresh1 706fb12b70Safresh1calls like the following 716fb12b70Safresh1 726fb12b70Safresh1 catdir(":a:") = ":a:" 736fb12b70Safresh1 catdir(":a","b") = ":a:b:" 746fb12b70Safresh1 catdir(":a:","b") = ":a:b:" 756fb12b70Safresh1 catdir(":a:",":b:") = ":a:b:" 766fb12b70Safresh1 catdir(":") = ":" 776fb12b70Safresh1 786fb12b70Safresh1are allowed. 796fb12b70Safresh1 806fb12b70Safresh1Here are the rules that are used in C<catdir()>; note that we try to be as 816fb12b70Safresh1compatible as possible to Unix: 826fb12b70Safresh1 836fb12b70Safresh1=over 2 846fb12b70Safresh1 856fb12b70Safresh1=item 1. 866fb12b70Safresh1 876fb12b70Safresh1The resulting path is relative by default, i.e. the resulting path will have a 886fb12b70Safresh1leading colon. 896fb12b70Safresh1 906fb12b70Safresh1=item 2. 916fb12b70Safresh1 926fb12b70Safresh1A trailing colon is added automatically to the resulting path, to denote a 936fb12b70Safresh1directory. 946fb12b70Safresh1 956fb12b70Safresh1=item 3. 966fb12b70Safresh1 976fb12b70Safresh1Generally, each argument has one leading ":" and one trailing ":" 986fb12b70Safresh1removed (if any). They are then joined together by a ":". Special 996fb12b70Safresh1treatment applies for arguments denoting updir paths like "::lib:", 1006fb12b70Safresh1see (4), or arguments consisting solely of colons ("colon paths"), 1016fb12b70Safresh1see (5). 1026fb12b70Safresh1 1036fb12b70Safresh1=item 4. 1046fb12b70Safresh1 1056fb12b70Safresh1When an updir path like ":::lib::" is passed as argument, the number 1066fb12b70Safresh1of directories to climb up is handled correctly, not removing leading 1076fb12b70Safresh1or trailing colons when necessary. E.g. 1086fb12b70Safresh1 1096fb12b70Safresh1 catdir(":::a","::b","c") = ":::a::b:c:" 1106fb12b70Safresh1 catdir(":::a::","::b","c") = ":::a:::b:c:" 1116fb12b70Safresh1 1126fb12b70Safresh1=item 5. 1136fb12b70Safresh1 1146fb12b70Safresh1Adding a colon ":" or empty string "" to a path at I<any> position 1156fb12b70Safresh1doesn't alter the path, i.e. these arguments are ignored. (When a "" 1166fb12b70Safresh1is passed as the first argument, it has a special meaning, see 1176fb12b70Safresh1(6)). This way, a colon ":" is handled like a "." (curdir) on Unix, 1186fb12b70Safresh1while an empty string "" is generally ignored (see 1199f11ffb7Safresh1L<File::Spec::Unix/canonpath()> ). Likewise, a "::" is handled like a ".." 1206fb12b70Safresh1(updir), and a ":::" is handled like a "../.." etc. E.g. 1216fb12b70Safresh1 1226fb12b70Safresh1 catdir("a",":",":","b") = ":a:b:" 1236fb12b70Safresh1 catdir("a",":","::",":b") = ":a::b:" 1246fb12b70Safresh1 1256fb12b70Safresh1=item 6. 1266fb12b70Safresh1 1276fb12b70Safresh1If the first argument is an empty string "" or is a volume name, i.e. matches 1286fb12b70Safresh1the pattern /^[^:]+:/, the resulting path is B<absolute>. 1296fb12b70Safresh1 1306fb12b70Safresh1=item 7. 1316fb12b70Safresh1 1326fb12b70Safresh1Passing an empty string "" as the first argument to C<catdir()> is 1336fb12b70Safresh1like passingC<File::Spec-E<gt>rootdir()> as the first argument, i.e. 1346fb12b70Safresh1 1356fb12b70Safresh1 catdir("","a","b") is the same as 1366fb12b70Safresh1 1376fb12b70Safresh1 catdir(rootdir(),"a","b"). 1386fb12b70Safresh1 1396fb12b70Safresh1This is true on Unix, where C<catdir("","a","b")> yields "/a/b" and 1406fb12b70Safresh1C<rootdir()> is "/". Note that C<rootdir()> on Mac OS is the startup 1416fb12b70Safresh1volume, which is the closest in concept to Unix' "/". This should help 1426fb12b70Safresh1to run existing scripts originally written for Unix. 1436fb12b70Safresh1 1446fb12b70Safresh1=item 8. 1456fb12b70Safresh1 1466fb12b70Safresh1For absolute paths, some cleanup is done, to ensure that the volume 1476fb12b70Safresh1name isn't immediately followed by updirs. This is invalid, because 1486fb12b70Safresh1this would go beyond "root". Generally, these cases are handled like 1496fb12b70Safresh1their Unix counterparts: 1506fb12b70Safresh1 1516fb12b70Safresh1 Unix: 1526fb12b70Safresh1 Unix->catdir("","") = "/" 1536fb12b70Safresh1 Unix->catdir("",".") = "/" 1546fb12b70Safresh1 Unix->catdir("","..") = "/" # can't go 1556fb12b70Safresh1 # beyond root 1566fb12b70Safresh1 Unix->catdir("",".","..","..","a") = "/a" 1576fb12b70Safresh1 Mac: 1586fb12b70Safresh1 Mac->catdir("","") = rootdir() # (e.g. "HD:") 1596fb12b70Safresh1 Mac->catdir("",":") = rootdir() 1606fb12b70Safresh1 Mac->catdir("","::") = rootdir() # can't go 1616fb12b70Safresh1 # beyond root 1626fb12b70Safresh1 Mac->catdir("",":","::","::","a") = rootdir() . "a:" 1636fb12b70Safresh1 # (e.g. "HD:a:") 1646fb12b70Safresh1 1656fb12b70Safresh1However, this approach is limited to the first arguments following 1669f11ffb7Safresh1"root" (again, see L<File::Spec::Unix/canonpath()>. If there are more 1676fb12b70Safresh1arguments that move up the directory tree, an invalid path going 1686fb12b70Safresh1beyond root can be created. 1696fb12b70Safresh1 1706fb12b70Safresh1=back 1716fb12b70Safresh1 1726fb12b70Safresh1As you've seen, you can force C<catdir()> to create an absolute path 1736fb12b70Safresh1by passing either an empty string or a path that begins with a volume 1746fb12b70Safresh1name as the first argument. However, you are strongly encouraged not 1756fb12b70Safresh1to do so, since this is done only for backward compatibility. Newer 1766fb12b70Safresh1versions of File::Spec come with a method called C<catpath()> (see 1776fb12b70Safresh1below), that is designed to offer a portable solution for the creation 1786fb12b70Safresh1of absolute paths. It takes volume, directory and file portions and 1796fb12b70Safresh1returns an entire path. While C<catdir()> is still suitable for the 1806fb12b70Safresh1concatenation of I<directory names>, you are encouraged to use 1816fb12b70Safresh1C<catpath()> to concatenate I<volume names> and I<directory 1826fb12b70Safresh1paths>. E.g. 1836fb12b70Safresh1 1846fb12b70Safresh1 $dir = File::Spec->catdir("tmp","sources"); 1856fb12b70Safresh1 $abs_path = File::Spec->catpath("MacintoshHD:", $dir,""); 1866fb12b70Safresh1 1876fb12b70Safresh1yields 1886fb12b70Safresh1 1896fb12b70Safresh1 "MacintoshHD:tmp:sources:" . 1906fb12b70Safresh1 1916fb12b70Safresh1=cut 1926fb12b70Safresh1 1936fb12b70Safresh1sub catdir { 1946fb12b70Safresh1 my $self = shift; 1956fb12b70Safresh1 return '' unless @_; 1966fb12b70Safresh1 my @args = @_; 1976fb12b70Safresh1 my $first_arg; 1986fb12b70Safresh1 my $relative; 1996fb12b70Safresh1 2006fb12b70Safresh1 # take care of the first argument 2016fb12b70Safresh1 2026fb12b70Safresh1 if ($args[0] eq '') { # absolute path, rootdir 2036fb12b70Safresh1 shift @args; 2046fb12b70Safresh1 $relative = 0; 2056fb12b70Safresh1 $first_arg = $self->rootdir; 2066fb12b70Safresh1 2076fb12b70Safresh1 } elsif ($args[0] =~ /^[^:]+:/) { # absolute path, volume name 2086fb12b70Safresh1 $relative = 0; 2096fb12b70Safresh1 $first_arg = shift @args; 2106fb12b70Safresh1 # add a trailing ':' if need be (may be it's a path like HD:dir) 2116fb12b70Safresh1 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/); 2126fb12b70Safresh1 2136fb12b70Safresh1 } else { # relative path 2146fb12b70Safresh1 $relative = 1; 2156fb12b70Safresh1 if ( $args[0] =~ /^::+\Z(?!\n)/ ) { 2166fb12b70Safresh1 # updir colon path ('::', ':::' etc.), don't shift 2176fb12b70Safresh1 $first_arg = ':'; 2186fb12b70Safresh1 } elsif ($args[0] eq ':') { 2196fb12b70Safresh1 $first_arg = shift @args; 2206fb12b70Safresh1 } else { 2216fb12b70Safresh1 # add a trailing ':' if need be 2226fb12b70Safresh1 $first_arg = shift @args; 2236fb12b70Safresh1 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/); 2246fb12b70Safresh1 } 2256fb12b70Safresh1 } 2266fb12b70Safresh1 2276fb12b70Safresh1 # For all other arguments, 2286fb12b70Safresh1 # (a) ignore arguments that equal ':' or '', 2296fb12b70Safresh1 # (b) handle updir paths specially: 2306fb12b70Safresh1 # '::' -> concatenate '::' 2316fb12b70Safresh1 # '::' . '::' -> concatenate ':::' etc. 2326fb12b70Safresh1 # (c) add a trailing ':' if need be 2336fb12b70Safresh1 2346fb12b70Safresh1 my $result = $first_arg; 2356fb12b70Safresh1 while (@args) { 2366fb12b70Safresh1 my $arg = shift @args; 2376fb12b70Safresh1 unless (($arg eq '') || ($arg eq ':')) { 2386fb12b70Safresh1 if ($arg =~ /^::+\Z(?!\n)/ ) { # updir colon path like ':::' 2396fb12b70Safresh1 my $updir_count = length($arg) - 1; 2406fb12b70Safresh1 while ((@args) && ($args[0] =~ /^::+\Z(?!\n)/) ) { # while updir colon path 2416fb12b70Safresh1 $arg = shift @args; 2426fb12b70Safresh1 $updir_count += (length($arg) - 1); 2436fb12b70Safresh1 } 2446fb12b70Safresh1 $arg = (':' x $updir_count); 2456fb12b70Safresh1 } else { 2466fb12b70Safresh1 $arg =~ s/^://s; # remove a leading ':' if any 2476fb12b70Safresh1 $arg = "$arg:" unless ($arg =~ /:\Z(?!\n)/); # ensure trailing ':' 2486fb12b70Safresh1 } 2496fb12b70Safresh1 $result .= $arg; 2506fb12b70Safresh1 }#unless 2516fb12b70Safresh1 } 2526fb12b70Safresh1 2536fb12b70Safresh1 if ( ($relative) && ($result !~ /^:/) ) { 2546fb12b70Safresh1 # add a leading colon if need be 2556fb12b70Safresh1 $result = ":$result"; 2566fb12b70Safresh1 } 2576fb12b70Safresh1 2586fb12b70Safresh1 unless ($relative) { 2596fb12b70Safresh1 # remove updirs immediately following the volume name 2606fb12b70Safresh1 $result =~ s/([^:]+:)(:*)(.*)\Z(?!\n)/$1$3/; 2616fb12b70Safresh1 } 2626fb12b70Safresh1 2636fb12b70Safresh1 return $result; 2646fb12b70Safresh1} 2656fb12b70Safresh1 2666fb12b70Safresh1=item catfile 2676fb12b70Safresh1 2686fb12b70Safresh1Concatenate one or more directory names and a filename to form a 2696fb12b70Safresh1complete path ending with a filename. Resulting paths are B<relative> 2706fb12b70Safresh1by default, but can be forced to be absolute (but avoid this). 2716fb12b70Safresh1 2726fb12b70Safresh1B<IMPORTANT NOTE:> Beginning with version 1.3 of this module, the 2736fb12b70Safresh1resulting path is relative by default and I<not> absolute. This 2746fb12b70Safresh1decision was made due to portability reasons. Since 2756fb12b70Safresh1C<File::Spec-E<gt>catfile()> returns relative paths on all other 2766fb12b70Safresh1operating systems, it will now also follow this convention on Mac OS. 2776fb12b70Safresh1Note that this may break some existing scripts. 2786fb12b70Safresh1 2796fb12b70Safresh1The last argument is always considered to be the file portion. Since 2806fb12b70Safresh1C<catfile()> uses C<catdir()> (see above) for the concatenation of the 2816fb12b70Safresh1directory portions (if any), the following with regard to relative and 2826fb12b70Safresh1absolute paths is true: 2836fb12b70Safresh1 2846fb12b70Safresh1 catfile("") = "" 2856fb12b70Safresh1 catfile("file") = "file" 2866fb12b70Safresh1 2876fb12b70Safresh1but 2886fb12b70Safresh1 2896fb12b70Safresh1 catfile("","") = rootdir() # (e.g. "HD:") 2906fb12b70Safresh1 catfile("","file") = rootdir() . file # (e.g. "HD:file") 2916fb12b70Safresh1 catfile("HD:","file") = "HD:file" 2926fb12b70Safresh1 2936fb12b70Safresh1This means that C<catdir()> is called only when there are two or more 2946fb12b70Safresh1arguments, as one might expect. 2956fb12b70Safresh1 2966fb12b70Safresh1Note that the leading ":" is removed from the filename, so that 2976fb12b70Safresh1 2986fb12b70Safresh1 catfile("a","b","file") = ":a:b:file" and 2996fb12b70Safresh1 3006fb12b70Safresh1 catfile("a","b",":file") = ":a:b:file" 3016fb12b70Safresh1 3026fb12b70Safresh1give the same answer. 3036fb12b70Safresh1 3046fb12b70Safresh1To concatenate I<volume names>, I<directory paths> and I<filenames>, 3056fb12b70Safresh1you are encouraged to use C<catpath()> (see below). 3066fb12b70Safresh1 3076fb12b70Safresh1=cut 3086fb12b70Safresh1 3096fb12b70Safresh1sub catfile { 3106fb12b70Safresh1 my $self = shift; 3116fb12b70Safresh1 return '' unless @_; 3126fb12b70Safresh1 my $file = pop @_; 3136fb12b70Safresh1 return $file unless @_; 3146fb12b70Safresh1 my $dir = $self->catdir(@_); 3156fb12b70Safresh1 $file =~ s/^://s; 3166fb12b70Safresh1 return $dir.$file; 3176fb12b70Safresh1} 3186fb12b70Safresh1 3196fb12b70Safresh1=item curdir 3206fb12b70Safresh1 3216fb12b70Safresh1Returns a string representing the current directory. On Mac OS, this is ":". 3226fb12b70Safresh1 3236fb12b70Safresh1=cut 3246fb12b70Safresh1 3256fb12b70Safresh1sub curdir { 3266fb12b70Safresh1 return ":"; 3276fb12b70Safresh1} 3286fb12b70Safresh1 3296fb12b70Safresh1=item devnull 3306fb12b70Safresh1 3316fb12b70Safresh1Returns a string representing the null device. On Mac OS, this is "Dev:Null". 3326fb12b70Safresh1 3336fb12b70Safresh1=cut 3346fb12b70Safresh1 3356fb12b70Safresh1sub devnull { 3366fb12b70Safresh1 return "Dev:Null"; 3376fb12b70Safresh1} 3386fb12b70Safresh1 3396fb12b70Safresh1=item rootdir 3406fb12b70Safresh1 3419f11ffb7Safresh1Returns the empty string. Mac OS has no real root directory. 3426fb12b70Safresh1 3436fb12b70Safresh1=cut 3446fb12b70Safresh1 3459f11ffb7Safresh1sub rootdir { '' } 3466fb12b70Safresh1 3476fb12b70Safresh1=item tmpdir 3486fb12b70Safresh1 3496fb12b70Safresh1Returns the contents of $ENV{TMPDIR}, if that directory exits or the 3506fb12b70Safresh1current working directory otherwise. Under MacPerl, $ENV{TMPDIR} will 3516fb12b70Safresh1contain a path like "MacintoshHD:Temporary Items:", which is a hidden 3526fb12b70Safresh1directory on your startup volume. 3536fb12b70Safresh1 3546fb12b70Safresh1=cut 3556fb12b70Safresh1 3566fb12b70Safresh1sub tmpdir { 3576fb12b70Safresh1 my $cached = $_[0]->_cached_tmpdir('TMPDIR'); 3586fb12b70Safresh1 return $cached if defined $cached; 3596fb12b70Safresh1 $_[0]->_cache_tmpdir($_[0]->_tmpdir( $ENV{TMPDIR} ), 'TMPDIR'); 3606fb12b70Safresh1} 3616fb12b70Safresh1 3626fb12b70Safresh1=item updir 3636fb12b70Safresh1 3646fb12b70Safresh1Returns a string representing the parent directory. On Mac OS, this is "::". 3656fb12b70Safresh1 3666fb12b70Safresh1=cut 3676fb12b70Safresh1 3686fb12b70Safresh1sub updir { 3696fb12b70Safresh1 return "::"; 3706fb12b70Safresh1} 3716fb12b70Safresh1 3726fb12b70Safresh1=item file_name_is_absolute 3736fb12b70Safresh1 3746fb12b70Safresh1Takes as argument a path and returns true, if it is an absolute path. 3756fb12b70Safresh1If the path has a leading ":", it's a relative path. Otherwise, it's an 3766fb12b70Safresh1absolute path, unless the path doesn't contain any colons, i.e. it's a name 3776fb12b70Safresh1like "a". In this particular case, the path is considered to be relative 3786fb12b70Safresh1(i.e. it is considered to be a filename). Use ":" in the appropriate place 3796fb12b70Safresh1in the path if you want to distinguish unambiguously. As a special case, 3806fb12b70Safresh1the filename '' is always considered to be absolute. Note that with version 3816fb12b70Safresh11.2 of File::Spec::Mac, this does no longer consult the local filesystem. 3826fb12b70Safresh1 3836fb12b70Safresh1E.g. 3846fb12b70Safresh1 3856fb12b70Safresh1 File::Spec->file_name_is_absolute("a"); # false (relative) 3866fb12b70Safresh1 File::Spec->file_name_is_absolute(":a:b:"); # false (relative) 3876fb12b70Safresh1 File::Spec->file_name_is_absolute("MacintoshHD:"); 3886fb12b70Safresh1 # true (absolute) 3896fb12b70Safresh1 File::Spec->file_name_is_absolute(""); # true (absolute) 3906fb12b70Safresh1 3916fb12b70Safresh1 3926fb12b70Safresh1=cut 3936fb12b70Safresh1 3946fb12b70Safresh1sub file_name_is_absolute { 3956fb12b70Safresh1 my ($self,$file) = @_; 3966fb12b70Safresh1 if ($file =~ /:/) { 3976fb12b70Safresh1 return (! ($file =~ m/^:/s) ); 3986fb12b70Safresh1 } elsif ( $file eq '' ) { 3996fb12b70Safresh1 return 1 ; 4006fb12b70Safresh1 } else { 4016fb12b70Safresh1 return 0; # i.e. a file like "a" 4026fb12b70Safresh1 } 4036fb12b70Safresh1} 4046fb12b70Safresh1 4056fb12b70Safresh1=item path 4066fb12b70Safresh1 4076fb12b70Safresh1Returns the null list for the MacPerl application, since the concept is 4086fb12b70Safresh1usually meaningless under Mac OS. But if you're using the MacPerl tool under 4096fb12b70Safresh1MPW, it gives back $ENV{Commands} suitably split, as is done in 4106fb12b70Safresh1:lib:ExtUtils:MM_Mac.pm. 4116fb12b70Safresh1 4126fb12b70Safresh1=cut 4136fb12b70Safresh1 4146fb12b70Safresh1sub path { 4156fb12b70Safresh1# 4166fb12b70Safresh1# The concept is meaningless under the MacPerl application. 4176fb12b70Safresh1# Under MPW, it has a meaning. 4186fb12b70Safresh1# 4196fb12b70Safresh1 return unless exists $ENV{Commands}; 4206fb12b70Safresh1 return split(/,/, $ENV{Commands}); 4216fb12b70Safresh1} 4226fb12b70Safresh1 4236fb12b70Safresh1=item splitpath 4246fb12b70Safresh1 4256fb12b70Safresh1 ($volume,$directories,$file) = File::Spec->splitpath( $path ); 4266fb12b70Safresh1 ($volume,$directories,$file) = File::Spec->splitpath( $path, 4276fb12b70Safresh1 $no_file ); 4286fb12b70Safresh1 4296fb12b70Safresh1Splits a path into volume, directory, and filename portions. 4306fb12b70Safresh1 4316fb12b70Safresh1On Mac OS, assumes that the last part of the path is a filename unless 4326fb12b70Safresh1$no_file is true or a trailing separator ":" is present. 4336fb12b70Safresh1 4346fb12b70Safresh1The volume portion is always returned with a trailing ":". The directory portion 4356fb12b70Safresh1is always returned with a leading (to denote a relative path) and a trailing ":" 4366fb12b70Safresh1(to denote a directory). The file portion is always returned I<without> a leading ":". 4376fb12b70Safresh1Empty portions are returned as empty string ''. 4386fb12b70Safresh1 4396fb12b70Safresh1The results can be passed to C<catpath()> to get back a path equivalent to 4406fb12b70Safresh1(usually identical to) the original path. 4416fb12b70Safresh1 4426fb12b70Safresh1 4436fb12b70Safresh1=cut 4446fb12b70Safresh1 4456fb12b70Safresh1sub splitpath { 4466fb12b70Safresh1 my ($self,$path, $nofile) = @_; 4476fb12b70Safresh1 my ($volume,$directory,$file); 4486fb12b70Safresh1 4496fb12b70Safresh1 if ( $nofile ) { 4506fb12b70Safresh1 ( $volume, $directory ) = $path =~ m|^((?:[^:]+:)?)(.*)|s; 4516fb12b70Safresh1 } 4526fb12b70Safresh1 else { 4536fb12b70Safresh1 $path =~ 4546fb12b70Safresh1 m|^( (?: [^:]+: )? ) 4556fb12b70Safresh1 ( (?: .*: )? ) 4566fb12b70Safresh1 ( .* ) 4576fb12b70Safresh1 |xs; 4586fb12b70Safresh1 $volume = $1; 4596fb12b70Safresh1 $directory = $2; 4606fb12b70Safresh1 $file = $3; 4616fb12b70Safresh1 } 4626fb12b70Safresh1 4636fb12b70Safresh1 $volume = '' unless defined($volume); 4646fb12b70Safresh1 $directory = ":$directory" if ( $volume && $directory ); # take care of "HD::dir" 4656fb12b70Safresh1 if ($directory) { 4666fb12b70Safresh1 # Make sure non-empty directories begin and end in ':' 4676fb12b70Safresh1 $directory .= ':' unless (substr($directory,-1) eq ':'); 4686fb12b70Safresh1 $directory = ":$directory" unless (substr($directory,0,1) eq ':'); 4696fb12b70Safresh1 } else { 4706fb12b70Safresh1 $directory = ''; 4716fb12b70Safresh1 } 4726fb12b70Safresh1 $file = '' unless defined($file); 4736fb12b70Safresh1 4746fb12b70Safresh1 return ($volume,$directory,$file); 4756fb12b70Safresh1} 4766fb12b70Safresh1 4776fb12b70Safresh1 4786fb12b70Safresh1=item splitdir 4796fb12b70Safresh1 4806fb12b70Safresh1The opposite of C<catdir()>. 4816fb12b70Safresh1 4826fb12b70Safresh1 @dirs = File::Spec->splitdir( $directories ); 4836fb12b70Safresh1 4846fb12b70Safresh1$directories should be only the directory portion of the path on systems 4856fb12b70Safresh1that have the concept of a volume or that have path syntax that differentiates 4866fb12b70Safresh1files from directories. Consider using C<splitpath()> otherwise. 4876fb12b70Safresh1 4886fb12b70Safresh1Unlike just splitting the directories on the separator, empty directory names 4896fb12b70Safresh1(C<"">) can be returned. Since C<catdir()> on Mac OS always appends a trailing 4906fb12b70Safresh1colon to distinguish a directory path from a file path, a single trailing colon 4916fb12b70Safresh1will be ignored, i.e. there's no empty directory name after it. 4926fb12b70Safresh1 4936fb12b70Safresh1Hence, on Mac OS, both 4946fb12b70Safresh1 4956fb12b70Safresh1 File::Spec->splitdir( ":a:b::c:" ); and 4966fb12b70Safresh1 File::Spec->splitdir( ":a:b::c" ); 4976fb12b70Safresh1 4986fb12b70Safresh1yield: 4996fb12b70Safresh1 5006fb12b70Safresh1 ( "a", "b", "::", "c") 5016fb12b70Safresh1 5026fb12b70Safresh1while 5036fb12b70Safresh1 5046fb12b70Safresh1 File::Spec->splitdir( ":a:b::c::" ); 5056fb12b70Safresh1 5066fb12b70Safresh1yields: 5076fb12b70Safresh1 5086fb12b70Safresh1 ( "a", "b", "::", "c", "::") 5096fb12b70Safresh1 5106fb12b70Safresh1 5116fb12b70Safresh1=cut 5126fb12b70Safresh1 5136fb12b70Safresh1sub splitdir { 5146fb12b70Safresh1 my ($self, $path) = @_; 5156fb12b70Safresh1 my @result = (); 5166fb12b70Safresh1 my ($head, $sep, $tail, $volume, $directories); 5176fb12b70Safresh1 5186fb12b70Safresh1 return @result if ( (!defined($path)) || ($path eq '') ); 5196fb12b70Safresh1 return (':') if ($path eq ':'); 5206fb12b70Safresh1 5216fb12b70Safresh1 ( $volume, $sep, $directories ) = $path =~ m|^((?:[^:]+:)?)(:*)(.*)|s; 5226fb12b70Safresh1 5236fb12b70Safresh1 # deprecated, but handle it correctly 5246fb12b70Safresh1 if ($volume) { 5256fb12b70Safresh1 push (@result, $volume); 5266fb12b70Safresh1 $sep .= ':'; 5276fb12b70Safresh1 } 5286fb12b70Safresh1 5296fb12b70Safresh1 while ($sep || $directories) { 5306fb12b70Safresh1 if (length($sep) > 1) { 5316fb12b70Safresh1 my $updir_count = length($sep) - 1; 5326fb12b70Safresh1 for (my $i=0; $i<$updir_count; $i++) { 5336fb12b70Safresh1 # push '::' updir_count times; 5346fb12b70Safresh1 # simulate Unix '..' updirs 5356fb12b70Safresh1 push (@result, '::'); 5366fb12b70Safresh1 } 5376fb12b70Safresh1 } 5386fb12b70Safresh1 $sep = ''; 5396fb12b70Safresh1 if ($directories) { 5406fb12b70Safresh1 ( $head, $sep, $tail ) = $directories =~ m|^((?:[^:]+)?)(:*)(.*)|s; 5416fb12b70Safresh1 push (@result, $head); 5426fb12b70Safresh1 $directories = $tail; 5436fb12b70Safresh1 } 5446fb12b70Safresh1 } 5456fb12b70Safresh1 return @result; 5466fb12b70Safresh1} 5476fb12b70Safresh1 5486fb12b70Safresh1 5496fb12b70Safresh1=item catpath 5506fb12b70Safresh1 5516fb12b70Safresh1 $path = File::Spec->catpath($volume,$directory,$file); 5526fb12b70Safresh1 5536fb12b70Safresh1Takes volume, directory and file portions and returns an entire path. On Mac OS, 5546fb12b70Safresh1$volume, $directory and $file are concatenated. A ':' is inserted if need be. You 5556fb12b70Safresh1may pass an empty string for each portion. If all portions are empty, the empty 5566fb12b70Safresh1string is returned. If $volume is empty, the result will be a relative path, 5576fb12b70Safresh1beginning with a ':'. If $volume and $directory are empty, a leading ":" (if any) 5586fb12b70Safresh1is removed form $file and the remainder is returned. If $file is empty, the 5596fb12b70Safresh1resulting path will have a trailing ':'. 5606fb12b70Safresh1 5616fb12b70Safresh1 5626fb12b70Safresh1=cut 5636fb12b70Safresh1 5646fb12b70Safresh1sub catpath { 5656fb12b70Safresh1 my ($self,$volume,$directory,$file) = @_; 5666fb12b70Safresh1 5676fb12b70Safresh1 if ( (! $volume) && (! $directory) ) { 5686fb12b70Safresh1 $file =~ s/^:// if $file; 5696fb12b70Safresh1 return $file ; 5706fb12b70Safresh1 } 5716fb12b70Safresh1 5726fb12b70Safresh1 # We look for a volume in $volume, then in $directory, but not both 5736fb12b70Safresh1 5746fb12b70Safresh1 my ($dir_volume, $dir_dirs) = $self->splitpath($directory, 1); 5756fb12b70Safresh1 5766fb12b70Safresh1 $volume = $dir_volume unless length $volume; 5776fb12b70Safresh1 my $path = $volume; # may be '' 5786fb12b70Safresh1 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':' 5796fb12b70Safresh1 5806fb12b70Safresh1 if ($directory) { 5816fb12b70Safresh1 $directory = $dir_dirs if $volume; 5826fb12b70Safresh1 $directory =~ s/^://; # remove leading ':' if any 5836fb12b70Safresh1 $path .= $directory; 5846fb12b70Safresh1 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':' 5856fb12b70Safresh1 } 5866fb12b70Safresh1 5876fb12b70Safresh1 if ($file) { 5886fb12b70Safresh1 $file =~ s/^://; # remove leading ':' if any 5896fb12b70Safresh1 $path .= $file; 5906fb12b70Safresh1 } 5916fb12b70Safresh1 5926fb12b70Safresh1 return $path; 5936fb12b70Safresh1} 5946fb12b70Safresh1 5956fb12b70Safresh1=item abs2rel 5966fb12b70Safresh1 5976fb12b70Safresh1Takes a destination path and an optional base path and returns a relative path 5986fb12b70Safresh1from the base path to the destination path: 5996fb12b70Safresh1 6006fb12b70Safresh1 $rel_path = File::Spec->abs2rel( $path ) ; 6016fb12b70Safresh1 $rel_path = File::Spec->abs2rel( $path, $base ) ; 6026fb12b70Safresh1 6036fb12b70Safresh1Note that both paths are assumed to have a notation that distinguishes a 6046fb12b70Safresh1directory path (with trailing ':') from a file path (without trailing ':'). 6056fb12b70Safresh1 6066fb12b70Safresh1If $base is not present or '', then the current working directory is used. 6076fb12b70Safresh1If $base is relative, then it is converted to absolute form using C<rel2abs()>. 6086fb12b70Safresh1This means that it is taken to be relative to the current working directory. 6096fb12b70Safresh1 6106fb12b70Safresh1If $path and $base appear to be on two different volumes, we will not 6116fb12b70Safresh1attempt to resolve the two paths, and we will instead simply return 6126fb12b70Safresh1$path. Note that previous versions of this module ignored the volume 6136fb12b70Safresh1of $base, which resulted in garbage results part of the time. 6146fb12b70Safresh1 6156fb12b70Safresh1If $base doesn't have a trailing colon, the last element of $base is 6166fb12b70Safresh1assumed to be a filename. This filename is ignored. Otherwise all path 6176fb12b70Safresh1components are assumed to be directories. 6186fb12b70Safresh1 6196fb12b70Safresh1If $path is relative, it is converted to absolute form using C<rel2abs()>. 6206fb12b70Safresh1This means that it is taken to be relative to the current working directory. 6216fb12b70Safresh1 6226fb12b70Safresh1Based on code written by Shigio Yamaguchi. 6236fb12b70Safresh1 6246fb12b70Safresh1 6256fb12b70Safresh1=cut 6266fb12b70Safresh1 6276fb12b70Safresh1# maybe this should be done in canonpath() ? 6286fb12b70Safresh1sub _resolve_updirs { 6296fb12b70Safresh1 my $path = shift @_; 6306fb12b70Safresh1 my $proceed; 6316fb12b70Safresh1 6326fb12b70Safresh1 # resolve any updirs, e.g. "HD:tmp::file" -> "HD:file" 6336fb12b70Safresh1 do { 6346fb12b70Safresh1 $proceed = ($path =~ s/^(.*):[^:]+::(.*?)\z/$1:$2/); 6356fb12b70Safresh1 } while ($proceed); 6366fb12b70Safresh1 6376fb12b70Safresh1 return $path; 6386fb12b70Safresh1} 6396fb12b70Safresh1 6406fb12b70Safresh1 6416fb12b70Safresh1sub abs2rel { 6426fb12b70Safresh1 my($self,$path,$base) = @_; 6436fb12b70Safresh1 6446fb12b70Safresh1 # Clean up $path 6456fb12b70Safresh1 if ( ! $self->file_name_is_absolute( $path ) ) { 6466fb12b70Safresh1 $path = $self->rel2abs( $path ) ; 6476fb12b70Safresh1 } 6486fb12b70Safresh1 6496fb12b70Safresh1 # Figure out the effective $base and clean it up. 6506fb12b70Safresh1 if ( !defined( $base ) || $base eq '' ) { 6519f11ffb7Safresh1 $base = Cwd::getcwd(); 6526fb12b70Safresh1 } 6536fb12b70Safresh1 elsif ( ! $self->file_name_is_absolute( $base ) ) { 6546fb12b70Safresh1 $base = $self->rel2abs( $base ) ; 6556fb12b70Safresh1 $base = _resolve_updirs( $base ); # resolve updirs in $base 6566fb12b70Safresh1 } 6576fb12b70Safresh1 else { 6586fb12b70Safresh1 $base = _resolve_updirs( $base ); 6596fb12b70Safresh1 } 6606fb12b70Safresh1 6616fb12b70Safresh1 # Split up paths - ignore $base's file 6626fb12b70Safresh1 my ( $path_vol, $path_dirs, $path_file ) = $self->splitpath( $path ); 6636fb12b70Safresh1 my ( $base_vol, $base_dirs ) = $self->splitpath( $base ); 6646fb12b70Safresh1 6656fb12b70Safresh1 return $path unless lc( $path_vol ) eq lc( $base_vol ); 6666fb12b70Safresh1 6676fb12b70Safresh1 # Now, remove all leading components that are the same 6686fb12b70Safresh1 my @pathchunks = $self->splitdir( $path_dirs ); 6696fb12b70Safresh1 my @basechunks = $self->splitdir( $base_dirs ); 6706fb12b70Safresh1 6716fb12b70Safresh1 while ( @pathchunks && 6726fb12b70Safresh1 @basechunks && 6736fb12b70Safresh1 lc( $pathchunks[0] ) eq lc( $basechunks[0] ) ) { 6746fb12b70Safresh1 shift @pathchunks ; 6756fb12b70Safresh1 shift @basechunks ; 6766fb12b70Safresh1 } 6776fb12b70Safresh1 6786fb12b70Safresh1 # @pathchunks now has the directories to descend in to. 6796fb12b70Safresh1 # ensure relative path, even if @pathchunks is empty 6806fb12b70Safresh1 $path_dirs = $self->catdir( ':', @pathchunks ); 6816fb12b70Safresh1 6826fb12b70Safresh1 # @basechunks now contains the number of directories to climb out of. 6836fb12b70Safresh1 $base_dirs = (':' x @basechunks) . ':' ; 6846fb12b70Safresh1 6856fb12b70Safresh1 return $self->catpath( '', $self->catdir( $base_dirs, $path_dirs ), $path_file ) ; 6866fb12b70Safresh1} 6876fb12b70Safresh1 6886fb12b70Safresh1=item rel2abs 6896fb12b70Safresh1 6906fb12b70Safresh1Converts a relative path to an absolute path: 6916fb12b70Safresh1 6926fb12b70Safresh1 $abs_path = File::Spec->rel2abs( $path ) ; 6936fb12b70Safresh1 $abs_path = File::Spec->rel2abs( $path, $base ) ; 6946fb12b70Safresh1 6956fb12b70Safresh1Note that both paths are assumed to have a notation that distinguishes a 6966fb12b70Safresh1directory path (with trailing ':') from a file path (without trailing ':'). 6976fb12b70Safresh1 6986fb12b70Safresh1If $base is not present or '', then $base is set to the current working 6996fb12b70Safresh1directory. If $base is relative, then it is converted to absolute form 7006fb12b70Safresh1using C<rel2abs()>. This means that it is taken to be relative to the 7016fb12b70Safresh1current working directory. 7026fb12b70Safresh1 7036fb12b70Safresh1If $base doesn't have a trailing colon, the last element of $base is 7046fb12b70Safresh1assumed to be a filename. This filename is ignored. Otherwise all path 7056fb12b70Safresh1components are assumed to be directories. 7066fb12b70Safresh1 7076fb12b70Safresh1If $path is already absolute, it is returned and $base is ignored. 7086fb12b70Safresh1 7096fb12b70Safresh1Based on code written by Shigio Yamaguchi. 7106fb12b70Safresh1 7116fb12b70Safresh1=cut 7126fb12b70Safresh1 7136fb12b70Safresh1sub rel2abs { 7146fb12b70Safresh1 my ($self,$path,$base) = @_; 7156fb12b70Safresh1 7166fb12b70Safresh1 if ( ! $self->file_name_is_absolute($path) ) { 7176fb12b70Safresh1 # Figure out the effective $base and clean it up. 7186fb12b70Safresh1 if ( !defined( $base ) || $base eq '' ) { 7199f11ffb7Safresh1 $base = Cwd::getcwd(); 7206fb12b70Safresh1 } 7216fb12b70Safresh1 elsif ( ! $self->file_name_is_absolute($base) ) { 7226fb12b70Safresh1 $base = $self->rel2abs($base) ; 7236fb12b70Safresh1 } 7246fb12b70Safresh1 7256fb12b70Safresh1 # Split up paths 7266fb12b70Safresh1 7276fb12b70Safresh1 # ignore $path's volume 7286fb12b70Safresh1 my ( $path_dirs, $path_file ) = ($self->splitpath($path))[1,2] ; 7296fb12b70Safresh1 7306fb12b70Safresh1 # ignore $base's file part 7316fb12b70Safresh1 my ( $base_vol, $base_dirs ) = $self->splitpath($base) ; 7326fb12b70Safresh1 7336fb12b70Safresh1 # Glom them together 7346fb12b70Safresh1 $path_dirs = ':' if ($path_dirs eq ''); 7356fb12b70Safresh1 $base_dirs =~ s/:$//; # remove trailing ':', if any 7366fb12b70Safresh1 $base_dirs = $base_dirs . $path_dirs; 7376fb12b70Safresh1 7386fb12b70Safresh1 $path = $self->catpath( $base_vol, $base_dirs, $path_file ); 7396fb12b70Safresh1 } 7406fb12b70Safresh1 return $path; 7416fb12b70Safresh1} 7426fb12b70Safresh1 7436fb12b70Safresh1 7446fb12b70Safresh1=back 7456fb12b70Safresh1 7466fb12b70Safresh1=head1 AUTHORS 7476fb12b70Safresh1 7486fb12b70Safresh1See the authors list in I<File::Spec>. Mac OS support by Paul Schinder 7496fb12b70Safresh1<schinder@pobox.com> and Thomas Wegner <wegner_thomas@yahoo.com>. 7506fb12b70Safresh1 7516fb12b70Safresh1=head1 COPYRIGHT 7526fb12b70Safresh1 7536fb12b70Safresh1Copyright (c) 2004 by the Perl 5 Porters. All rights reserved. 7546fb12b70Safresh1 7556fb12b70Safresh1This program is free software; you can redistribute it and/or modify 7566fb12b70Safresh1it under the same terms as Perl itself. 7576fb12b70Safresh1 7586fb12b70Safresh1=head1 SEE ALSO 7596fb12b70Safresh1 7606fb12b70Safresh1See L<File::Spec> and L<File::Spec::Unix>. This package overrides the 7616fb12b70Safresh1implementation of these methods, not the semantics. 7626fb12b70Safresh1 7636fb12b70Safresh1=cut 7646fb12b70Safresh1 7656fb12b70Safresh11; 766