1 package File::Spec::Mac;
4 use vars qw(@ISA $VERSION);
5 require File::Spec::Unix;
9 @ISA = qw(File::Spec::Unix);
13 $macfiles = eval { require Mac::Files };
16 sub case_tolerant { 1 }
21 File::Spec::Mac - File::Spec for Mac OS (Classic)
25 require File::Spec::Mac; # Done internally by File::Spec if needed
29 Methods for manipulating file specifications.
37 On Mac OS, there's nothing to be done. Returns what it's given.
42 my ($self,$path) = @_;
48 Concatenate two or more directory names to form a path separated by colons
49 (":") ending with a directory. Resulting paths are B<relative> by default,
50 but can be forced to be absolute (but avoid this, see below). Automatically
51 puts a trailing ":" on the end of the complete path, because that's what's
52 done in MacPerl's environment and helps to distinguish a file path from a
55 B<IMPORTANT NOTE:> Beginning with version 1.3 of this module, the resulting
56 path is relative by default and I<not> absolute. This descision was made due
57 to portability reasons. Since C<File::Spec-E<gt>catdir()> returns relative paths
58 on all other operating systems, it will now also follow this convention on Mac
59 OS. Note that this may break some existing scripts.
61 The intended purpose of this routine is to concatenate I<directory names>.
62 But because of the nature of Macintosh paths, some additional possibilities
63 are allowed to make using this routine give reasonable results for some
64 common situations. In other words, you are also allowed to concatenate
65 I<paths> instead of directory names (strictly speaking, a string like ":a"
66 is a path, but not a name, since it contains a punctuation character ":").
71 catdir("a","b") = ":a:b:"
72 catdir() = "" (special case)
74 calls like the following
77 catdir(":a","b") = ":a:b:"
78 catdir(":a:","b") = ":a:b:"
79 catdir(":a:",":b:") = ":a:b:"
84 Here are the rules that are used in C<catdir()>; note that we try to be as
85 compatible as possible to Unix:
91 The resulting path is relative by default, i.e. the resulting path will have a
96 A trailing colon is added automatically to the resulting path, to denote a
101 Generally, each argument has one leading ":" and one trailing ":"
102 removed (if any). They are then joined together by a ":". Special
103 treatment applies for arguments denoting updir paths like "::lib:",
104 see (4), or arguments consisting solely of colons ("colon paths"),
109 When an updir path like ":::lib::" is passed as argument, the number
110 of directories to climb up is handled correctly, not removing leading
111 or trailing colons when necessary. E.g.
113 catdir(":::a","::b","c") = ":::a::b:c:"
114 catdir(":::a::","::b","c") = ":::a:::b:c:"
118 Adding a colon ":" or empty string "" to a path at I<any> position
119 doesn't alter the path, i.e. these arguments are ignored. (When a ""
120 is passed as the first argument, it has a special meaning, see
121 (6)). This way, a colon ":" is handled like a "." (curdir) on Unix,
122 while an empty string "" is generally ignored (see
123 C<Unix-E<gt>canonpath()> ). Likewise, a "::" is handled like a ".."
124 (updir), and a ":::" is handled like a "../.." etc. E.g.
126 catdir("a",":",":","b") = ":a:b:"
127 catdir("a",":","::",":b") = ":a::b:"
131 If the first argument is an empty string "" or is a volume name, i.e. matches
132 the pattern /^[^:]+:/, the resulting path is B<absolute>.
136 Passing an empty string "" as the first argument to C<catdir()> is
137 like passingC<File::Spec-E<gt>rootdir()> as the first argument, i.e.
139 catdir("","a","b") is the same as
141 catdir(rootdir(),"a","b").
143 This is true on Unix, where C<catdir("","a","b")> yields "/a/b" and
144 C<rootdir()> is "/". Note that C<rootdir()> on Mac OS is the startup
145 volume, which is the closest in concept to Unix' "/". This should help
146 to run existing scripts originally written for Unix.
150 For absolute paths, some cleanup is done, to ensure that the volume
151 name isn't immediately followed by updirs. This is invalid, because
152 this would go beyond "root". Generally, these cases are handled like
153 their Unix counterparts:
156 Unix->catdir("","") = "/"
157 Unix->catdir("",".") = "/"
158 Unix->catdir("","..") = "/" # can't go beyond root
159 Unix->catdir("",".","..","..","a") = "/a"
161 Mac->catdir("","") = rootdir() # (e.g. "HD:")
162 Mac->catdir("",":") = rootdir()
163 Mac->catdir("","::") = rootdir() # can't go beyond root
164 Mac->catdir("",":","::","::","a") = rootdir() . "a:" # (e.g. "HD:a:")
166 However, this approach is limited to the first arguments following
167 "root" (again, see C<Unix-E<gt>canonpath()> ). If there are more
168 arguments that move up the directory tree, an invalid path going
169 beyond root can be created.
173 As you've seen, you can force C<catdir()> to create an absolute path
174 by passing either an empty string or a path that begins with a volume
175 name as the first argument. However, you are strongly encouraged not
176 to do so, since this is done only for backward compatibility. Newer
177 versions of File::Spec come with a method called C<catpath()> (see
178 below), that is designed to offer a portable solution for the creation
179 of absolute paths. It takes volume, directory and file portions and
180 returns an entire path. While C<catdir()> is still suitable for the
181 concatenation of I<directory names>, you are encouraged to use
182 C<catpath()> to concatenate I<volume names> and I<directory
185 $dir = File::Spec->catdir("tmp","sources");
186 $abs_path = File::Spec->catpath("MacintoshHD:", $dir,"");
190 "MacintoshHD:tmp:sources:" .
201 # take care of the first argument
203 if ($args[0] eq '') { # absolute path, rootdir
206 $first_arg = $self->rootdir;
208 } elsif ($args[0] =~ /^[^:]+:/) { # absolute path, volume name
210 $first_arg = shift @args;
211 # add a trailing ':' if need be (may be it's a path like HD:dir)
212 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/);
214 } else { # relative path
216 if ( $args[0] =~ /^::+\Z(?!\n)/ ) {
217 # updir colon path ('::', ':::' etc.), don't shift
219 } elsif ($args[0] eq ':') {
220 $first_arg = shift @args;
222 # add a trailing ':' if need be
223 $first_arg = shift @args;
224 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/);
228 # For all other arguments,
229 # (a) ignore arguments that equal ':' or '',
230 # (b) handle updir paths specially:
231 # '::' -> concatenate '::'
232 # '::' . '::' -> concatenate ':::' etc.
233 # (c) add a trailing ':' if need be
235 my $result = $first_arg;
237 my $arg = shift @args;
238 unless (($arg eq '') || ($arg eq ':')) {
239 if ($arg =~ /^::+\Z(?!\n)/ ) { # updir colon path like ':::'
240 my $updir_count = length($arg) - 1;
241 while ((@args) && ($args[0] =~ /^::+\Z(?!\n)/) ) { # while updir colon path
243 $updir_count += (length($arg) - 1);
245 $arg = (':' x $updir_count);
247 $arg =~ s/^://s; # remove a leading ':' if any
248 $arg = "$arg:" unless ($arg =~ /:\Z(?!\n)/); # ensure trailing ':'
254 if ( ($relative) && ($result !~ /^:/) ) {
255 # add a leading colon if need be
256 $result = ":$result";
260 # remove updirs immediately following the volume name
261 $result =~ s/([^:]+:)(:*)(.*)\Z(?!\n)/$1$3/;
269 Concatenate one or more directory names and a filename to form a
270 complete path ending with a filename. Resulting paths are B<relative>
271 by default, but can be forced to be absolute (but avoid this).
273 B<IMPORTANT NOTE:> Beginning with version 1.3 of this module, the
274 resulting path is relative by default and I<not> absolute. This
275 descision was made due to portability reasons. Since
276 C<File::Spec-E<gt>catfile()> returns relative paths on all other
277 operating systems, it will now also follow this convention on Mac OS.
278 Note that this may break some existing scripts.
280 The last argument is always considered to be the file portion. Since
281 C<catfile()> uses C<catdir()> (see above) for the concatenation of the
282 directory portions (if any), the following with regard to relative and
283 absolute paths is true:
286 catfile("file") = "file"
290 catfile("","") = rootdir() # (e.g. "HD:")
291 catfile("","file") = rootdir() . file # (e.g. "HD:file")
292 catfile("HD:","file") = "HD:file"
294 This means that C<catdir()> is called only when there are two or more
295 arguments, as one might expect.
297 Note that the leading ":" is removed from the filename, so that
299 catfile("a","b","file") = ":a:b:file" and
301 catfile("a","b",":file") = ":a:b:file"
303 give the same answer.
305 To concatenate I<volume names>, I<directory paths> and I<filenames>,
306 you are encouraged to use C<catpath()> (see below).
314 return $file unless @_;
315 my $dir = $self->catdir(@_);
322 Returns a string representing the current directory. On Mac OS, this is ":".
332 Returns a string representing the null device. On Mac OS, this is "Dev:Null".
342 Returns a string representing the root directory. Under MacPerl,
343 returns the name of the startup volume, since that's the closest in
344 concept, although other volumes aren't rooted there. The name has a
345 trailing ":", because that's the correct specification for a volume
348 If Mac::Files could not be loaded, the empty string is returned.
354 # There's no real root directory on Mac OS. The name of the startup
355 # volume is returned, since that's the closest in concept.
357 return '' unless $macfiles;
358 my $system = Mac::Files::FindFolder(&Mac::Files::kOnSystemDisk,
359 &Mac::Files::kSystemFolderType);
360 $system =~ s/:.*\Z(?!\n)/:/s;
366 Returns the contents of $ENV{TMPDIR}, if that directory exits or the
367 current working directory otherwise. Under MacPerl, $ENV{TMPDIR} will
368 contain a path like "MacintoshHD:Temporary Items:", which is a hidden
369 directory on your startup volume.
375 return $tmpdir if defined $tmpdir;
376 $tmpdir = $_[0]->_tmpdir( $ENV{TMPDIR} );
381 Returns a string representing the parent directory. On Mac OS, this is "::".
389 =item file_name_is_absolute
391 Takes as argument a path and returns true, if it is an absolute path.
392 If the path has a leading ":", it's a relative path. Otherwise, it's an
393 absolute path, unless the path doesn't contain any colons, i.e. it's a name
394 like "a". In this particular case, the path is considered to be relative
395 (i.e. it is considered to be a filename). Use ":" in the appropriate place
396 in the path if you want to distinguish unambiguously. As a special case,
397 the filename '' is always considered to be absolute. Note that with version
398 1.2 of File::Spec::Mac, this does no longer consult the local filesystem.
402 File::Spec->file_name_is_absolute("a"); # false (relative)
403 File::Spec->file_name_is_absolute(":a:b:"); # false (relative)
404 File::Spec->file_name_is_absolute("MacintoshHD:"); # true (absolute)
405 File::Spec->file_name_is_absolute(""); # true (absolute)
410 sub file_name_is_absolute {
411 my ($self,$file) = @_;
413 return (! ($file =~ m/^:/s) );
414 } elsif ( $file eq '' ) {
417 return 0; # i.e. a file like "a"
423 Returns the null list for the MacPerl application, since the concept is
424 usually meaningless under Mac OS. But if you're using the MacPerl tool under
425 MPW, it gives back $ENV{Commands} suitably split, as is done in
426 :lib:ExtUtils:MM_Mac.pm.
432 # The concept is meaningless under the MacPerl application.
433 # Under MPW, it has a meaning.
435 return unless exists $ENV{Commands};
436 return split(/,/, $ENV{Commands});
441 ($volume,$directories,$file) = File::Spec->splitpath( $path );
442 ($volume,$directories,$file) = File::Spec->splitpath( $path, $no_file );
444 Splits a path into volume, directory, and filename portions.
446 On Mac OS, assumes that the last part of the path is a filename unless
447 $no_file is true or a trailing separator ":" is present.
449 The volume portion is always returned with a trailing ":". The directory portion
450 is always returned with a leading (to denote a relative path) and a trailing ":"
451 (to denote a directory). The file portion is always returned I<without> a leading ":".
452 Empty portions are returned as empty string ''.
454 The results can be passed to C<catpath()> to get back a path equivalent to
455 (usually identical to) the original path.
461 my ($self,$path, $nofile) = @_;
462 my ($volume,$directory,$file);
465 ( $volume, $directory ) = $path =~ m|^((?:[^:]+:)?)(.*)|s;
478 $volume = '' unless defined($volume);
479 $directory = ":$directory" if ( $volume && $directory ); # take care of "HD::dir"
481 # Make sure non-empty directories begin and end in ':'
482 $directory .= ':' unless (substr($directory,-1) eq ':');
483 $directory = ":$directory" unless (substr($directory,0,1) eq ':');
487 $file = '' unless defined($file);
489 return ($volume,$directory,$file);
495 The opposite of C<catdir()>.
497 @dirs = File::Spec->splitdir( $directories );
499 $directories should be only the directory portion of the path on systems
500 that have the concept of a volume or that have path syntax that differentiates
501 files from directories. Consider using C<splitpath()> otherwise.
503 Unlike just splitting the directories on the separator, empty directory names
504 (C<"">) can be returned. Since C<catdir()> on Mac OS always appends a trailing
505 colon to distinguish a directory path from a file path, a single trailing colon
506 will be ignored, i.e. there's no empty directory name after it.
508 Hence, on Mac OS, both
510 File::Spec->splitdir( ":a:b::c:" ); and
511 File::Spec->splitdir( ":a:b::c" );
515 ( "a", "b", "::", "c")
519 File::Spec->splitdir( ":a:b::c::" );
523 ( "a", "b", "::", "c", "::")
529 my ($self, $path) = @_;
531 my ($head, $sep, $tail, $volume, $directories);
533 return ('') if ( (!defined($path)) || ($path eq '') );
534 return (':') if ($path eq ':');
536 ( $volume, $sep, $directories ) = $path =~ m|^((?:[^:]+:)?)(:*)(.*)|s;
538 # deprecated, but handle it correctly
540 push (@result, $volume);
544 while ($sep || $directories) {
545 if (length($sep) > 1) {
546 my $updir_count = length($sep) - 1;
547 for (my $i=0; $i<$updir_count; $i++) {
548 # push '::' updir_count times;
549 # simulate Unix '..' updirs
550 push (@result, '::');
555 ( $head, $sep, $tail ) = $directories =~ m|^((?:[^:]+)?)(:*)(.*)|s;
556 push (@result, $head);
557 $directories = $tail;
566 $path = File::Spec->catpath($volume,$directory,$file);
568 Takes volume, directory and file portions and returns an entire path. On Mac OS,
569 $volume, $directory and $file are concatenated. A ':' is inserted if need be. You
570 may pass an empty string for each portion. If all portions are empty, the empty
571 string is returned. If $volume is empty, the result will be a relative path,
572 beginning with a ':'. If $volume and $directory are empty, a leading ":" (if any)
573 is removed form $file and the remainder is returned. If $file is empty, the
574 resulting path will have a trailing ':'.
580 my ($self,$volume,$directory,$file) = @_;
582 if ( (! $volume) && (! $directory) ) {
583 $file =~ s/^:// if $file;
587 # We look for a volume in $volume, then in $directory, but not both
589 my ($dir_volume, $dir_dirs) = $self->splitpath($directory, 1);
591 $volume = $dir_volume unless length $volume;
592 my $path = $volume; # may be ''
593 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':'
596 $directory = $dir_dirs if $volume;
597 $directory =~ s/^://; # remove leading ':' if any
599 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':'
603 $file =~ s/^://; # remove leading ':' if any
612 Takes a destination path and an optional base path and returns a relative path
613 from the base path to the destination path:
615 $rel_path = File::Spec->abs2rel( $path ) ;
616 $rel_path = File::Spec->abs2rel( $path, $base ) ;
618 Note that both paths are assumed to have a notation that distinguishes a
619 directory path (with trailing ':') from a file path (without trailing ':').
621 If $base is not present or '', then the current working directory is used.
622 If $base is relative, then it is converted to absolute form using C<rel2abs()>.
623 This means that it is taken to be relative to the current working directory.
625 If $path and $base appear to be on two different volumes, we will not
626 attempt to resolve the two paths, and we will instead simply return
627 $path. Note that previous versions of this module ignored the volume
628 of $base, which resulted in garbage results part of the time.
630 If $base doesn't have a trailing colon, the last element of $base is
631 assumed to be a filename. This filename is ignored. Otherwise all path
632 components are assumed to be directories.
634 If $path is relative, it is converted to absolute form using C<rel2abs()>.
635 This means that it is taken to be relative to the current working directory.
637 Based on code written by Shigio Yamaguchi.
642 # maybe this should be done in canonpath() ?
643 sub _resolve_updirs {
647 # resolve any updirs, e.g. "HD:tmp::file" -> "HD:file"
649 $proceed = ($path =~ s/^(.*):[^:]+::(.*?)\z/$1:$2/);
657 my($self,$path,$base) = @_;
660 if ( ! $self->file_name_is_absolute( $path ) ) {
661 $path = $self->rel2abs( $path ) ;
664 # Figure out the effective $base and clean it up.
665 if ( !defined( $base ) || $base eq '' ) {
666 $base = $self->_cwd();
668 elsif ( ! $self->file_name_is_absolute( $base ) ) {
669 $base = $self->rel2abs( $base ) ;
670 $base = _resolve_updirs( $base ); # resolve updirs in $base
673 $base = _resolve_updirs( $base );
676 # Split up paths - ignore $base's file
677 my ( $path_vol, $path_dirs, $path_file ) = $self->splitpath( $path );
678 my ( $base_vol, $base_dirs ) = $self->splitpath( $base );
680 return $path unless lc( $path_vol ) eq lc( $base_vol );
682 # Now, remove all leading components that are the same
683 my @pathchunks = $self->splitdir( $path_dirs );
684 my @basechunks = $self->splitdir( $base_dirs );
686 while ( @pathchunks &&
688 lc( $pathchunks[0] ) eq lc( $basechunks[0] ) ) {
693 # @pathchunks now has the directories to descend in to.
694 # ensure relative path, even if @pathchunks is empty
695 $path_dirs = $self->catdir( ':', @pathchunks );
697 # @basechunks now contains the number of directories to climb out of.
698 $base_dirs = (':' x @basechunks) . ':' ;
700 return $self->catpath( '', $self->catdir( $base_dirs, $path_dirs ), $path_file ) ;
705 Converts a relative path to an absolute path:
707 $abs_path = File::Spec->rel2abs( $path ) ;
708 $abs_path = File::Spec->rel2abs( $path, $base ) ;
710 Note that both paths are assumed to have a notation that distinguishes a
711 directory path (with trailing ':') from a file path (without trailing ':').
713 If $base is not present or '', then $base is set to the current working
714 directory. If $base is relative, then it is converted to absolute form
715 using C<rel2abs()>. This means that it is taken to be relative to the
716 current working directory.
718 If $base doesn't have a trailing colon, the last element of $base is
719 assumed to be a filename. This filename is ignored. Otherwise all path
720 components are assumed to be directories.
722 If $path is already absolute, it is returned and $base is ignored.
724 Based on code written by Shigio Yamaguchi.
729 my ($self,$path,$base) = @_;
731 if ( ! $self->file_name_is_absolute($path) ) {
732 # Figure out the effective $base and clean it up.
733 if ( !defined( $base ) || $base eq '' ) {
734 $base = $self->_cwd();
736 elsif ( ! $self->file_name_is_absolute($base) ) {
737 $base = $self->rel2abs($base) ;
742 # igonore $path's volume
743 my ( $path_dirs, $path_file ) = ($self->splitpath($path))[1,2] ;
745 # ignore $base's file part
746 my ( $base_vol, $base_dirs ) = $self->splitpath($base) ;
749 $path_dirs = ':' if ($path_dirs eq '');
750 $base_dirs =~ s/:$//; # remove trailing ':', if any
751 $base_dirs = $base_dirs . $path_dirs;
753 $path = $self->catpath( $base_vol, $base_dirs, $path_file );
763 See the authors list in I<File::Spec>. Mac OS support by Paul Schinder
764 <schinder@pobox.com> and Thomas Wegner <wegner_thomas@yahoo.com>.
768 Copyright (c) 2004 by the Perl 5 Porters. All rights reserved.
770 This program is free software; you can redistribute it and/or modify
771 it under the same terms as Perl itself.
775 See L<File::Spec> and L<File::Spec::Unix>. This package overrides the
776 implementation of these methods, not the semantics.