1 package File::Spec::Mac;
4 use vars qw(@ISA $VERSION);
5 require File::Spec::Unix;
9 @ISA = qw(File::Spec::Unix);
15 File::Spec::Mac - File::Spec for Mac OS (Classic)
19 require File::Spec::Mac; # Done internally by File::Spec if needed
23 Methods for manipulating file specifications.
31 On Mac OS, there's nothing to be done. Returns what it's given.
36 my ($self,$path) = @_;
42 Concatenate two or more directory names to form a path separated by colons
43 (":") ending with a directory. Resulting paths are B<relative> by default,
44 but can be forced to be absolute (but avoid this, see below). Automatically
45 puts a trailing ":" on the end of the complete path, because that's what's
46 done in MacPerl's environment and helps to distinguish a file path from a
49 B<IMPORTANT NOTE:> Beginning with version 1.3 of this module, the resulting
50 path is relative by default and I<not> absolute. This descision was made due
51 to portability reasons. Since C<File::Spec-E<gt>catdir()> returns relative paths
52 on all other operating systems, it will now also follow this convention on Mac
53 OS. Note that this may break some existing scripts.
55 The intended purpose of this routine is to concatenate I<directory names>.
56 But because of the nature of Macintosh paths, some additional possibilities
57 are allowed to make using this routine give reasonable results for some
58 common situations. In other words, you are also allowed to concatenate
59 I<paths> instead of directory names (strictly speaking, a string like ":a"
60 is a path, but not a name, since it contains a punctuation character ":").
65 catdir("a","b") = ":a:b:"
66 catdir() = "" (special case)
68 calls like the following
71 catdir(":a","b") = ":a:b:"
72 catdir(":a:","b") = ":a:b:"
73 catdir(":a:",":b:") = ":a:b:"
78 Here are the rules that are used in C<catdir()>; note that we try to be as
79 compatible as possible to Unix:
85 The resulting path is relative by default, i.e. the resulting path will have a
90 A trailing colon is added automatically to the resulting path, to denote a
95 Generally, each argument has one leading ":" and one trailing ":"
96 removed (if any). They are then joined together by a ":". Special
97 treatment applies for arguments denoting updir paths like "::lib:",
98 see (4), or arguments consisting solely of colons ("colon paths"),
103 When an updir path like ":::lib::" is passed as argument, the number
104 of directories to climb up is handled correctly, not removing leading
105 or trailing colons when necessary. E.g.
107 catdir(":::a","::b","c") = ":::a::b:c:"
108 catdir(":::a::","::b","c") = ":::a:::b:c:"
113 Adding a colon ":" or empty string "" to a path at I<any> position
114 doesn't alter the path, i.e. these arguments are ignored. (When a ""
115 is passed as the first argument, it has a special meaning, see
116 (6)). This way, a colon ":" is handled like a "." (curdir) on Unix,
117 while an empty string "" is generally ignored (see
118 C<Unix-E<gt>canonpath()> ). Likewise, a "::" is handled like a ".."
119 (updir), and a ":::" is handled like a "../.." etc. E.g.
121 catdir("a",":",":","b") = ":a:b:"
122 catdir("a",":","::",":b") = ":a::b:"
126 If the first argument is an empty string "" or is a volume name, i.e. matches
127 the pattern /^[^:]+:/, the resulting path is B<absolute>.
131 Passing an empty string "" as the first argument to C<catdir()> is
132 like passingC<File::Spec-E<gt>rootdir()> as the first argument, i.e.
134 catdir("","a","b") is the same as
136 catdir(rootdir(),"a","b").
138 This is true on Unix, where C<catdir("","a","b")> yields "/a/b" and
139 C<rootdir()> is "/". Note that C<rootdir()> on Mac OS is the startup
140 volume, which is the closest in concept to Unix' "/". This should help
141 to run existing scripts originally written for Unix.
145 For absolute paths, some cleanup is done, to ensure that the volume
146 name isn't immediately followed by updirs. This is invalid, because
147 this would go beyond "root". Generally, these cases are handled like
148 their Unix counterparts:
151 Unix->catdir("","") = "/"
152 Unix->catdir("",".") = "/"
153 Unix->catdir("","..") = "/" # can't go beyond root
154 Unix->catdir("",".","..","..","a") = "/a"
156 Mac->catdir("","") = rootdir() # (e.g. "HD:")
157 Mac->catdir("",":") = rootdir()
158 Mac->catdir("","::") = rootdir() # can't go beyond root
159 Mac->catdir("",":","::","::","a") = rootdir() . "a:" # (e.g. "HD:a:")
161 However, this approach is limited to the first arguments following
162 "root" (again, see C<Unix-E<gt>canonpath()> ). If there are more
163 arguments that move up the directory tree, an invalid path going
164 beyond root can be created.
168 As you've seen, you can force C<catdir()> to create an absolute path
169 by passing either an empty string or a path that begins with a volume
170 name as the first argument. However, you are strongly encouraged not
171 to do so, since this is done only for backward compatibility. Newer
172 versions of File::Spec come with a method called C<catpath()> (see
173 below), that is designed to offer a portable solution for the creation
174 of absolute paths. It takes volume, directory and file portions and
175 returns an entire path. While C<catdir()> is still suitable for the
176 concatenation of I<directory names>, you are encouraged to use
177 C<catpath()> to concatenate I<volume names> and I<directory
180 $dir = File::Spec->catdir("tmp","sources");
181 $abs_path = File::Spec->catpath("MacintoshHD:", $dir,"");
185 "MacintoshHD:tmp:sources:" .
197 # take care of the first argument
199 if ($args[0] eq '') { # absolute path, rootdir
202 $first_arg = $self->rootdir;
204 } elsif ($args[0] =~ /^[^:]+:/) { # absolute path, volume name
206 $first_arg = shift @args;
207 # add a trailing ':' if need be (may be it's a path like HD:dir)
208 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/);
210 } else { # relative path
212 if ( $args[0] =~ /^::+\Z(?!\n)/ ) {
213 # updir colon path ('::', ':::' etc.), don't shift
215 } elsif ($args[0] eq ':') {
216 $first_arg = shift @args;
218 # add a trailing ':' if need be
219 $first_arg = shift @args;
220 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/);
224 # For all other arguments,
225 # (a) ignore arguments that equal ':' or '',
226 # (b) handle updir paths specially:
227 # '::' -> concatenate '::'
228 # '::' . '::' -> concatenate ':::' etc.
229 # (c) add a trailing ':' if need be
231 my $result = $first_arg;
233 my $arg = shift @args;
234 unless (($arg eq '') || ($arg eq ':')) {
235 if ($arg =~ /^::+\Z(?!\n)/ ) { # updir colon path like ':::'
236 my $updir_count = length($arg) - 1;
237 while ((@args) && ($args[0] =~ /^::+\Z(?!\n)/) ) { # while updir colon path
239 $updir_count += (length($arg) - 1);
241 $arg = (':' x $updir_count);
243 $arg =~ s/^://s; # remove a leading ':' if any
244 $arg = "$arg:" unless ($arg =~ /:\Z(?!\n)/); # ensure trailing ':'
250 if ( ($relative) && ($result !~ /^:/) ) {
251 # add a leading colon if need be
252 $result = ":$result";
256 # remove updirs immediately following the volume name
257 $result =~ s/([^:]+:)(:*)(.*)\Z(?!\n)/$1$3/;
265 Concatenate one or more directory names and a filename to form a
266 complete path ending with a filename. Resulting paths are B<relative>
267 by default, but can be forced to be absolute (but avoid this).
269 B<IMPORTANT NOTE:> Beginning with version 1.3 of this module, the
270 resulting path is relative by default and I<not> absolute. This
271 descision was made due to portability reasons. Since
272 C<File::Spec-E<gt>catfile()> returns relative paths on all other
273 operating systems, it will now also follow this convention on Mac OS.
274 Note that this may break some existing scripts.
276 The last argument is always considered to be the file portion. Since
277 C<catfile()> uses C<catdir()> (see above) for the concatenation of the
278 directory portions (if any), the following with regard to relative and
279 absolute paths is true:
282 catfile("file") = "file"
286 catfile("","") = rootdir() # (e.g. "HD:")
287 catfile("","file") = rootdir() . file # (e.g. "HD:file")
288 catfile("HD:","file") = "HD:file"
290 This means that C<catdir()> is called only when there are two or more
291 arguments, as one might expect.
293 Note that the leading ":" is removed from the filename, so that
295 catfile("a","b","file") = ":a:b:file" and
297 catfile("a","b",":file") = ":a:b:file"
299 give the same answer.
301 To concatenate I<volume names>, I<directory paths> and I<filenames>,
302 you are encouraged to use C<catpath()> (see below).
310 return $file unless @_;
311 my $dir = $self->catdir(@_);
318 Returns a string representing the current directory. On Mac OS, this is ":".
328 Returns a string representing the null device. On Mac OS, this is "Dev:Null".
338 Returns a string representing the root directory. Under MacPerl,
339 returns the name of the startup volume, since that's the closest in
340 concept, although other volumes aren't rooted there. The name has a
341 trailing ":", because that's the correct specification for a volume
348 # There's no real root directory on Mac OS. The name of the startup
349 # volume is returned, since that's the closest in concept.
352 my $system = Mac::Files::FindFolder(&Mac::Files::kOnSystemDisk,
353 &Mac::Files::kSystemFolderType);
354 $system =~ s/:.*\Z(?!\n)/:/s;
360 Returns the contents of $ENV{TMPDIR}, if that directory exits or the current working
361 directory otherwise. Under MacPerl, $ENV{TMPDIR} will contain a path like
362 "MacintoshHD:Temporary Items:", which is a hidden directory on your startup volume.
368 return $tmpdir if defined $tmpdir;
369 $tmpdir = $ENV{TMPDIR} if -d $ENV{TMPDIR};
370 unless (defined($tmpdir)) {
378 Returns a string representing the parent directory. On Mac OS, this is "::".
386 =item file_name_is_absolute
388 Takes as argument a path and returns true, if it is an absolute path.
389 If the path has a leading ":", it's a relative path. Otherwise, it's an
390 absolute path, unless the path doesn't contain any colons, i.e. it's a name
391 like "a". In this particular case, the path is considered to be relative
392 (i.e. it is considered to be a filename). Use ":" in the appropriate place
393 in the path if you want to distinguish unambiguously. As a special case,
394 the filename '' is always considered to be absolute. Note that with version
395 1.2 of File::Spec::Mac, this does no longer consult the local filesystem.
399 File::Spec->file_name_is_absolute("a"); # false (relative)
400 File::Spec->file_name_is_absolute(":a:b:"); # false (relative)
401 File::Spec->file_name_is_absolute("MacintoshHD:"); # true (absolute)
402 File::Spec->file_name_is_absolute(""); # true (absolute)
407 sub file_name_is_absolute {
408 my ($self,$file) = @_;
410 return (! ($file =~ m/^:/s) );
411 } elsif ( $file eq '' ) {
414 return 0; # i.e. a file like "a"
420 Returns the null list for the MacPerl application, since the concept is
421 usually meaningless under Mac OS. But if you're using the MacPerl tool under
422 MPW, it gives back $ENV{Commands} suitably split, as is done in
423 :lib:ExtUtils:MM_Mac.pm.
429 # The concept is meaningless under the MacPerl application.
430 # Under MPW, it has a meaning.
432 return unless exists $ENV{Commands};
433 return split(/,/, $ENV{Commands});
438 ($volume,$directories,$file) = File::Spec->splitpath( $path );
439 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
441 Splits a path in to volume, directory, and filename portions.
443 On Mac OS, assumes that the last part of the path is a filename unless
444 $no_file is true or a trailing separator ":" is present.
446 The volume portion is always returned with a trailing ":". The directory portion
447 is always returned with a leading (to denote a relative path) and a trailing ":"
448 (to denote a directory). The file portion is always returned I<without> a leading ":".
449 Empty portions are returned as empty string ''.
451 The results can be passed to C<catpath()> to get back a path equivalent to
452 (usually identical to) the original path.
458 my ($self,$path, $nofile) = @_;
459 my ($volume,$directory,$file);
462 ( $volume, $directory ) = $path =~ m|^((?:[^:]+:)?)(.*)|s;
475 $volume = '' unless defined($volume);
476 $directory = ":$directory" if ( $volume && $directory ); # take care of "HD::dir"
478 # Make sure non-empty directories begin and end in ':'
479 $directory .= ':' unless (substr($directory,-1) eq ':');
480 $directory = ":$directory" unless (substr($directory,0,1) eq ':');
484 $file = '' unless defined($file);
486 return ($volume,$directory,$file);
492 The opposite of C<catdir()>.
494 @dirs = File::Spec->splitdir( $directories );
496 $directories should be only the directory portion of the path on systems
497 that have the concept of a volume or that have path syntax that differentiates
498 files from directories. Consider using C<splitpath()> otherwise.
500 Unlike just splitting the directories on the separator, empty directory names
501 (C<"">) can be returned. Since C<catdir()> on Mac OS always appends a trailing
502 colon to distinguish a directory path from a file path, a single trailing colon
503 will be ignored, i.e. there's no empty directory name after it.
505 Hence, on Mac OS, both
507 File::Spec->splitdir( ":a:b::c:" ); and
508 File::Spec->splitdir( ":a:b::c" );
512 ( "a", "b", "::", "c")
516 File::Spec->splitdir( ":a:b::c::" );
520 ( "a", "b", "::", "c", "::")
526 my ($self, $path) = @_;
528 my ($head, $sep, $tail, $volume, $directories);
530 return ('') if ( (!defined($path)) || ($path eq '') );
531 return (':') if ($path eq ':');
533 ( $volume, $sep, $directories ) = $path =~ m|^((?:[^:]+:)?)(:*)(.*)|s;
535 # deprecated, but handle it correctly
537 push (@result, $volume);
541 while ($sep || $directories) {
542 if (length($sep) > 1) {
543 my $updir_count = length($sep) - 1;
544 for (my $i=0; $i<$updir_count; $i++) {
545 # push '::' updir_count times;
546 # simulate Unix '..' updirs
547 push (@result, '::');
552 ( $head, $sep, $tail ) = $directories =~ m|^((?:[^:]+)?)(:*)(.*)|s;
553 push (@result, $head);
554 $directories = $tail;
563 $path = File::Spec->catpath($volume,$directory,$file);
565 Takes volume, directory and file portions and returns an entire path. On Mac OS,
566 $volume, $directory and $file are concatenated. A ':' is inserted if need be. You
567 may pass an empty string for each portion. If all portions are empty, the empty
568 string is returned. If $volume is empty, the result will be a relative path,
569 beginning with a ':'. If $volume and $directory are empty, a leading ":" (if any)
570 is removed form $file and the remainder is returned. If $file is empty, the
571 resulting path will have a trailing ':'.
577 my ($self,$volume,$directory,$file) = @_;
579 if ( (! $volume) && (! $directory) ) {
580 $file =~ s/^:// if $file;
584 my $path = $volume; # may be ''
585 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':'
588 $directory =~ s/^://; # remove leading ':' if any
590 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':'
594 $file =~ s/^://; # remove leading ':' if any
603 Takes a destination path and an optional base path and returns a relative path
604 from the base path to the destination path:
606 $rel_path = File::Spec->abs2rel( $path ) ;
607 $rel_path = File::Spec->abs2rel( $path, $base ) ;
609 Note that both paths are assumed to have a notation that distinguishes a
610 directory path (with trailing ':') from a file path (without trailing ':').
612 If $base is not present or '', then the current working directory is used.
613 If $base is relative, then it is converted to absolute form using C<rel2abs()>.
614 This means that it is taken to be relative to the current working directory.
616 Since Mac OS has the concept of volumes, this assumes that both paths
617 are on the $destination volume, and ignores the $base volume (!).
619 If $base doesn't have a trailing colon, the last element of $base is
620 assumed to be a filename. This filename is ignored (!). Otherwise all path
621 components are assumed to be directories.
623 If $path is relative, it is converted to absolute form using C<rel2abs()>.
624 This means that it is taken to be relative to the current working directory.
626 Based on code written by Shigio Yamaguchi.
631 # maybe this should be done in canonpath() ?
632 sub _resolve_updirs {
636 # resolve any updirs, e.g. "HD:tmp::file" -> "HD:file"
638 $proceed = ($path =~ s/^(.*):[^:]+::(.*?)\z/$1:$2/);
646 my($self,$path,$base) = @_;
649 if ( ! $self->file_name_is_absolute( $path ) ) {
650 $path = $self->rel2abs( $path ) ;
653 # Figure out the effective $base and clean it up.
654 if ( !defined( $base ) || $base eq '' ) {
657 elsif ( ! $self->file_name_is_absolute( $base ) ) {
658 $base = $self->rel2abs( $base ) ;
659 $base = _resolve_updirs( $base ); # resolve updirs in $base
662 $base = _resolve_updirs( $base );
666 my ( $path_dirs, $path_file ) = ($self->splitpath( $path ))[1,2] ;
668 # ignore $base's volume and file
669 my $base_dirs = ($self->splitpath( $base ))[1] ;
671 # Now, remove all leading components that are the same
672 my @pathchunks = $self->splitdir( $path_dirs );
673 my @basechunks = $self->splitdir( $base_dirs );
675 while ( @pathchunks &&
677 lc( $pathchunks[0] ) eq lc( $basechunks[0] ) ) {
682 # @pathchunks now has the directories to descend in to.
683 if ( (@pathchunks) && ($pathchunks[0] ne '') ) {
684 $path_dirs = $self->catdir( @pathchunks );
689 # @basechunks now contains the number of directories to climb out of.
690 $base_dirs = (':' x @basechunks) . ':' ;
692 return $self->catpath( '', $self->catdir( $base_dirs, $path_dirs ), $path_file ) ;
697 Converts a relative path to an absolute path:
699 $abs_path = File::Spec->rel2abs( $path ) ;
700 $abs_path = File::Spec->rel2abs( $path, $base ) ;
702 Note that both paths are assumed to have a notation that distinguishes a
703 directory path (with trailing ':') from a file path (without trailing ':').
705 If $base is not present or '', then $base is set to the current working
706 directory. If $base is relative, then it is converted to absolute form
707 using C<rel2abs()>. This means that it is taken to be relative to the
708 current working directory.
710 If $base doesn't have a trailing colon, the last element of $base is
711 assumed to be a filename. This filename is ignored (!). Otherwise all path
712 components are assumed to be directories.
714 If $path is already absolute, it is returned and $base is ignored.
716 Based on code written by Shigio Yamaguchi.
721 my ($self,$path,$base) = @_;
723 if ( ! $self->file_name_is_absolute($path) ) {
724 # Figure out the effective $base and clean it up.
725 if ( !defined( $base ) || $base eq '' ) {
728 elsif ( ! $self->file_name_is_absolute($base) ) {
729 $base = $self->rel2abs($base) ;
734 # igonore $path's volume
735 my ( $path_dirs, $path_file ) = ($self->splitpath($path))[1,2] ;
737 # ignore $base's file part
738 my ( $base_vol, $base_dirs, undef ) = $self->splitpath($base) ;
741 $path_dirs = ':' if ($path_dirs eq '');
742 $base_dirs =~ s/:$//; # remove trailing ':', if any
743 $base_dirs = $base_dirs . $path_dirs;
745 $path = $self->catpath( $base_vol, $base_dirs, $path_file );
755 See the authors list in I<File::Spec>. Mac OS support by Paul Schinder
756 <schinder@pobox.com> and Thomas Wegner <wegner_thomas@yahoo.com>.