xref: /openbsd-src/gnu/usr.bin/perl/dist/PathTools/lib/File/Spec/Mac.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
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