File Coverage

blib/lib/File/Spec/Mac.pm
Criterion Covered Total %
statement 138 157 87.9
branch 79 94 84.0
condition 27 33 81.8
subroutine 13 19 68.4
pod 15 15 100.0
total 272 318 85.5


line stmt bran cond sub pod time code
1             package File::Spec::Mac;
2              
3 2     2   2508 use strict;
  2         4  
  2         61  
4 2     2   9 use vars qw(@ISA $VERSION);
  2         4  
  2         4556  
5             require File::Spec::Unix;
6              
7             $VERSION = '3.62';
8             $VERSION =~ tr/_//d;
9              
10             @ISA = qw(File::Spec::Unix);
11              
12             my $macfiles;
13             if ($^O eq 'MacOS') {
14             $macfiles = eval { require Mac::Files };
15             }
16              
17 1     1 1 801 sub case_tolerant { 1 }
18              
19              
20             =head1 NAME
21              
22             File::Spec::Mac - File::Spec for Mac OS (Classic)
23              
24             =head1 SYNOPSIS
25              
26             require File::Spec::Mac; # Done internally by File::Spec if needed
27              
28             =head1 DESCRIPTION
29              
30             Methods for manipulating file specifications.
31              
32             =head1 METHODS
33              
34             =over 2
35              
36             =item canonpath
37              
38             On Mac OS, there's nothing to be done. Returns what it's given.
39              
40             =cut
41              
42             sub canonpath {
43 7     7 1 3092 my ($self,$path) = @_;
44 7         45 return $path;
45             }
46              
47             =item catdir()
48              
49             Concatenate two or more directory names to form a path separated by colons
50             (":") ending with a directory. Resulting paths are B by default,
51             but can be forced to be absolute (but avoid this, see below). Automatically
52             puts a trailing ":" on the end of the complete path, because that's what's
53             done in MacPerl's environment and helps to distinguish a file path from a
54             directory path.
55              
56             B Beginning with version 1.3 of this module, the resulting
57             path is relative by default and I absolute. This decision was made due
58             to portability reasons. Since Ccatdir()> returns relative paths
59             on all other operating systems, it will now also follow this convention on Mac
60             OS. Note that this may break some existing scripts.
61              
62             The intended purpose of this routine is to concatenate I.
63             But because of the nature of Macintosh paths, some additional possibilities
64             are allowed to make using this routine give reasonable results for some
65             common situations. In other words, you are also allowed to concatenate
66             I instead of directory names (strictly speaking, a string like ":a"
67             is a path, but not a name, since it contains a punctuation character ":").
68              
69             So, beside calls like
70              
71             catdir("a") = ":a:"
72             catdir("a","b") = ":a:b:"
73             catdir() = "" (special case)
74              
75             calls like the following
76              
77             catdir(":a:") = ":a:"
78             catdir(":a","b") = ":a:b:"
79             catdir(":a:","b") = ":a:b:"
80             catdir(":a:",":b:") = ":a:b:"
81             catdir(":") = ":"
82              
83             are allowed.
84              
85             Here are the rules that are used in C; note that we try to be as
86             compatible as possible to Unix:
87              
88             =over 2
89              
90             =item 1.
91              
92             The resulting path is relative by default, i.e. the resulting path will have a
93             leading colon.
94              
95             =item 2.
96              
97             A trailing colon is added automatically to the resulting path, to denote a
98             directory.
99              
100             =item 3.
101              
102             Generally, each argument has one leading ":" and one trailing ":"
103             removed (if any). They are then joined together by a ":". Special
104             treatment applies for arguments denoting updir paths like "::lib:",
105             see (4), or arguments consisting solely of colons ("colon paths"),
106             see (5).
107              
108             =item 4.
109              
110             When an updir path like ":::lib::" is passed as argument, the number
111             of directories to climb up is handled correctly, not removing leading
112             or trailing colons when necessary. E.g.
113              
114             catdir(":::a","::b","c") = ":::a::b:c:"
115             catdir(":::a::","::b","c") = ":::a:::b:c:"
116              
117             =item 5.
118              
119             Adding a colon ":" or empty string "" to a path at I position
120             doesn't alter the path, i.e. these arguments are ignored. (When a ""
121             is passed as the first argument, it has a special meaning, see
122             (6)). This way, a colon ":" is handled like a "." (curdir) on Unix,
123             while an empty string "" is generally ignored (see
124             Ccanonpath()> ). Likewise, a "::" is handled like a ".."
125             (updir), and a ":::" is handled like a "../.." etc. E.g.
126              
127             catdir("a",":",":","b") = ":a:b:"
128             catdir("a",":","::",":b") = ":a::b:"
129              
130             =item 6.
131              
132             If the first argument is an empty string "" or is a volume name, i.e. matches
133             the pattern /^[^:]+:/, the resulting path is B.
134              
135             =item 7.
136              
137             Passing an empty string "" as the first argument to C is
138             like passingCrootdir()> as the first argument, i.e.
139              
140             catdir("","a","b") is the same as
141              
142             catdir(rootdir(),"a","b").
143              
144             This is true on Unix, where C yields "/a/b" and
145             C is "/". Note that C on Mac OS is the startup
146             volume, which is the closest in concept to Unix' "/". This should help
147             to run existing scripts originally written for Unix.
148              
149             =item 8.
150              
151             For absolute paths, some cleanup is done, to ensure that the volume
152             name isn't immediately followed by updirs. This is invalid, because
153             this would go beyond "root". Generally, these cases are handled like
154             their Unix counterparts:
155              
156             Unix:
157             Unix->catdir("","") = "/"
158             Unix->catdir("",".") = "/"
159             Unix->catdir("","..") = "/" # can't go
160             # beyond root
161             Unix->catdir("",".","..","..","a") = "/a"
162             Mac:
163             Mac->catdir("","") = rootdir() # (e.g. "HD:")
164             Mac->catdir("",":") = rootdir()
165             Mac->catdir("","::") = rootdir() # can't go
166             # beyond root
167             Mac->catdir("",":","::","::","a") = rootdir() . "a:"
168             # (e.g. "HD:a:")
169              
170             However, this approach is limited to the first arguments following
171             "root" (again, see Ccanonpath()> ). If there are more
172             arguments that move up the directory tree, an invalid path going
173             beyond root can be created.
174              
175             =back
176              
177             As you've seen, you can force C to create an absolute path
178             by passing either an empty string or a path that begins with a volume
179             name as the first argument. However, you are strongly encouraged not
180             to do so, since this is done only for backward compatibility. Newer
181             versions of File::Spec come with a method called C (see
182             below), that is designed to offer a portable solution for the creation
183             of absolute paths. It takes volume, directory and file portions and
184             returns an entire path. While C is still suitable for the
185             concatenation of I, you are encouraged to use
186             C to concatenate I and I
187             paths>. E.g.
188              
189             $dir = File::Spec->catdir("tmp","sources");
190             $abs_path = File::Spec->catpath("MacintoshHD:", $dir,"");
191              
192             yields
193              
194             "MacintoshHD:tmp:sources:" .
195              
196             =cut
197              
198             sub catdir {
199 95     95 1 26476 my $self = shift;
200 95 100       274 return '' unless @_;
201 94         243 my @args = @_;
202 94         122 my $first_arg;
203             my $relative;
204              
205             # take care of the first argument
206              
207 94 100       373 if ($args[0] eq '') { # absolute path, rootdir
    100          
208 1         2 shift @args;
209 1         2 $relative = 0;
210 1         4 $first_arg = $self->rootdir;
211              
212             } elsif ($args[0] =~ /^[^:]+:/) { # absolute path, volume name
213 14         18 $relative = 0;
214 14         25 $first_arg = shift @args;
215             # add a trailing ':' if need be (may be it's a path like HD:dir)
216 14 50       56 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/);
217              
218             } else { # relative path
219 79         113 $relative = 1;
220 79 100       273 if ( $args[0] =~ /^::+\Z(?!\n)/ ) {
    100          
221             # updir colon path ('::', ':::' etc.), don't shift
222 15         26 $first_arg = ':';
223             } elsif ($args[0] eq ':') {
224 35         65 $first_arg = shift @args;
225             } else {
226             # add a trailing ':' if need be
227 29         50 $first_arg = shift @args;
228 29 100       112 $first_arg = "$first_arg:" unless ($first_arg =~ /:\Z(?!\n)/);
229             }
230             }
231              
232             # For all other arguments,
233             # (a) ignore arguments that equal ':' or '',
234             # (b) handle updir paths specially:
235             # '::' -> concatenate '::'
236             # '::' . '::' -> concatenate ':::' etc.
237             # (c) add a trailing ':' if need be
238              
239 94         161 my $result = $first_arg;
240 94         233 while (@args) {
241 123         246 my $arg = shift @args;
242 123 100 100     656 unless (($arg eq '') || ($arg eq ':')) {
243 105 100       277 if ($arg =~ /^::+\Z(?!\n)/ ) { # updir colon path like ':::'
244 24         42 my $updir_count = length($arg) - 1;
245 24   100     130 while ((@args) && ($args[0] =~ /^::+\Z(?!\n)/) ) { # while updir colon path
246 4         8 $arg = shift @args;
247 4         18 $updir_count += (length($arg) - 1);
248             }
249 24         65 $arg = (':' x $updir_count);
250             } else {
251 81         199 $arg =~ s/^://s; # remove a leading ':' if any
252 81 100       285 $arg = "$arg:" unless ($arg =~ /:\Z(?!\n)/); # ensure trailing ':'
253             }
254 105         327 $result .= $arg;
255             }#unless
256             }
257              
258 94 100 100     515 if ( ($relative) && ($result !~ /^:/) ) {
259             # add a leading colon if need be
260 23         53 $result = ":$result";
261             }
262              
263 94 100       215 unless ($relative) {
264             # remove updirs immediately following the volume name
265 15         110 $result =~ s/([^:]+:)(:*)(.*)\Z(?!\n)/$1$3/;
266             }
267              
268 94         560 return $result;
269             }
270              
271             =item catfile
272              
273             Concatenate one or more directory names and a filename to form a
274             complete path ending with a filename. Resulting paths are B
275             by default, but can be forced to be absolute (but avoid this).
276              
277             B Beginning with version 1.3 of this module, the
278             resulting path is relative by default and I absolute. This
279             decision was made due to portability reasons. Since
280             Ccatfile()> returns relative paths on all other
281             operating systems, it will now also follow this convention on Mac OS.
282             Note that this may break some existing scripts.
283              
284             The last argument is always considered to be the file portion. Since
285             C uses C (see above) for the concatenation of the
286             directory portions (if any), the following with regard to relative and
287             absolute paths is true:
288              
289             catfile("") = ""
290             catfile("file") = "file"
291              
292             but
293              
294             catfile("","") = rootdir() # (e.g. "HD:")
295             catfile("","file") = rootdir() . file # (e.g. "HD:file")
296             catfile("HD:","file") = "HD:file"
297              
298             This means that C is called only when there are two or more
299             arguments, as one might expect.
300              
301             Note that the leading ":" is removed from the filename, so that
302              
303             catfile("a","b","file") = ":a:b:file" and
304              
305             catfile("a","b",":file") = ":a:b:file"
306              
307             give the same answer.
308              
309             To concatenate I, I and I,
310             you are encouraged to use C (see below).
311              
312             =cut
313              
314             sub catfile {
315 12     12 1 4972 my $self = shift;
316 12 100       44 return '' unless @_;
317 11         18 my $file = pop @_;
318 11 100       45 return $file unless @_;
319 8         24 my $dir = $self->catdir(@_);
320 8         19 $file =~ s/^://s;
321 8         54 return $dir.$file;
322             }
323              
324             =item curdir
325              
326             Returns a string representing the current directory. On Mac OS, this is ":".
327              
328             =cut
329              
330             sub curdir {
331 0     0 1 0 return ":";
332             }
333              
334             =item devnull
335              
336             Returns a string representing the null device. On Mac OS, this is "Dev:Null".
337              
338             =cut
339              
340             sub devnull {
341 0     0 1 0 return "Dev:Null";
342             }
343              
344             =item rootdir
345              
346             Returns a string representing the root directory. Under MacPerl,
347             returns the name of the startup volume, since that's the closest in
348             concept, although other volumes aren't rooted there. The name has a
349             trailing ":", because that's the correct specification for a volume
350             name on Mac OS.
351              
352             If Mac::Files could not be loaded, the empty string is returned.
353              
354             =cut
355              
356             sub rootdir {
357             #
358             # There's no real root directory on Mac OS. The name of the startup
359             # volume is returned, since that's the closest in concept.
360             #
361 0 0   0   0 return '' unless $macfiles;
362 0         0 my $system = Mac::Files::FindFolder(&Mac::Files::kOnSystemDisk,
363             &Mac::Files::kSystemFolderType);
364 0         0 $system =~ s/:.*\Z(?!\n)/:/s;
365 0         0 return $system;
366             }
367              
368             =item tmpdir
369              
370             Returns the contents of $ENV{TMPDIR}, if that directory exits or the
371             current working directory otherwise. Under MacPerl, $ENV{TMPDIR} will
372             contain a path like "MacintoshHD:Temporary Items:", which is a hidden
373             directory on your startup volume.
374              
375             =cut
376              
377             sub tmpdir {
378 0     0 1 0 my $cached = $_[0]->_cached_tmpdir('TMPDIR');
379 0 0       0 return $cached if defined $cached;
380 0         0 $_[0]->_cache_tmpdir($_[0]->_tmpdir( $ENV{TMPDIR} ), 'TMPDIR');
381             }
382              
383             =item updir
384              
385             Returns a string representing the parent directory. On Mac OS, this is "::".
386              
387             =cut
388              
389             sub updir {
390 0     0 1 0 return "::";
391             }
392              
393             =item file_name_is_absolute
394              
395             Takes as argument a path and returns true, if it is an absolute path.
396             If the path has a leading ":", it's a relative path. Otherwise, it's an
397             absolute path, unless the path doesn't contain any colons, i.e. it's a name
398             like "a". In this particular case, the path is considered to be relative
399             (i.e. it is considered to be a filename). Use ":" in the appropriate place
400             in the path if you want to distinguish unambiguously. As a special case,
401             the filename '' is always considered to be absolute. Note that with version
402             1.2 of File::Spec::Mac, this does no longer consult the local filesystem.
403              
404             E.g.
405              
406             File::Spec->file_name_is_absolute("a"); # false (relative)
407             File::Spec->file_name_is_absolute(":a:b:"); # false (relative)
408             File::Spec->file_name_is_absolute("MacintoshHD:");
409             # true (absolute)
410             File::Spec->file_name_is_absolute(""); # true (absolute)
411              
412              
413             =cut
414              
415             sub file_name_is_absolute {
416 70     70 1 120 my ($self,$file) = @_;
417 70 100       205 if ($file =~ /:/) {
    50          
418 69         288 return (! ($file =~ m/^:/s) );
419             } elsif ( $file eq '' ) {
420 1         5 return 1 ;
421             } else {
422 0         0 return 0; # i.e. a file like "a"
423             }
424             }
425              
426             =item path
427              
428             Returns the null list for the MacPerl application, since the concept is
429             usually meaningless under Mac OS. But if you're using the MacPerl tool under
430             MPW, it gives back $ENV{Commands} suitably split, as is done in
431             :lib:ExtUtils:MM_Mac.pm.
432              
433             =cut
434              
435             sub path {
436             #
437             # The concept is meaningless under the MacPerl application.
438             # Under MPW, it has a meaning.
439             #
440 0 0   0 1 0 return unless exists $ENV{Commands};
441 0         0 return split(/,/, $ENV{Commands});
442             }
443              
444             =item splitpath
445              
446             ($volume,$directories,$file) = File::Spec->splitpath( $path );
447             ($volume,$directories,$file) = File::Spec->splitpath( $path,
448             $no_file );
449              
450             Splits a path into volume, directory, and filename portions.
451              
452             On Mac OS, assumes that the last part of the path is a filename unless
453             $no_file is true or a trailing separator ":" is present.
454              
455             The volume portion is always returned with a trailing ":". The directory portion
456             is always returned with a leading (to denote a relative path) and a trailing ":"
457             (to denote a directory). The file portion is always returned I a leading ":".
458             Empty portions are returned as empty string ''.
459              
460             The results can be passed to C to get back a path equivalent to
461             (usually identical to) the original path.
462              
463              
464             =cut
465              
466             sub splitpath {
467 142     142 1 16390 my ($self,$path, $nofile) = @_;
468 142         197 my ($volume,$directory,$file);
469              
470 142 100       302 if ( $nofile ) {
471 63         337 ( $volume, $directory ) = $path =~ m|^((?:[^:]+:)?)(.*)|s;
472             }
473             else {
474 79         332 $path =~
475             m|^( (?: [^:]+: )? )
476             ( (?: .*: )? )
477             ( .* )
478             |xs;
479 79         169 $volume = $1;
480 79         136 $directory = $2;
481 79         145 $file = $3;
482             }
483              
484 142 50       369 $volume = '' unless defined($volume);
485 142 100 100     600 $directory = ":$directory" if ( $volume && $directory ); # take care of "HD::dir"
486 142 100       314 if ($directory) {
487             # Make sure non-empty directories begin and end in ':'
488 133 100       404 $directory .= ':' unless (substr($directory,-1) eq ':');
489 133 100       508 $directory = ":$directory" unless (substr($directory,0,1) eq ':');
490             } else {
491 9         20 $directory = '';
492             }
493 142 100       330 $file = '' unless defined($file);
494              
495 142         690 return ($volume,$directory,$file);
496             }
497              
498              
499             =item splitdir
500              
501             The opposite of C.
502              
503             @dirs = File::Spec->splitdir( $directories );
504              
505             $directories should be only the directory portion of the path on systems
506             that have the concept of a volume or that have path syntax that differentiates
507             files from directories. Consider using C otherwise.
508              
509             Unlike just splitting the directories on the separator, empty directory names
510             (C<"">) can be returned. Since C on Mac OS always appends a trailing
511             colon to distinguish a directory path from a file path, a single trailing colon
512             will be ignored, i.e. there's no empty directory name after it.
513              
514             Hence, on Mac OS, both
515              
516             File::Spec->splitdir( ":a:b::c:" ); and
517             File::Spec->splitdir( ":a:b::c" );
518              
519             yield:
520              
521             ( "a", "b", "::", "c")
522              
523             while
524              
525             File::Spec->splitdir( ":a:b::c::" );
526              
527             yields:
528              
529             ( "a", "b", "::", "c", "::")
530              
531              
532             =cut
533              
534             sub splitdir {
535 54     54 1 13286 my ($self, $path) = @_;
536 54         108 my @result = ();
537 54         75 my ($head, $sep, $tail, $volume, $directories);
538              
539 54 100 100     375 return @result if ( (!defined($path)) || ($path eq '') );
540 50 100       146 return (':') if ($path eq ':');
541              
542 49         273 ( $volume, $sep, $directories ) = $path =~ m|^((?:[^:]+:)?)(:*)(.*)|s;
543              
544             # deprecated, but handle it correctly
545 49 100       139 if ($volume) {
546 8         15 push (@result, $volume);
547 8         17 $sep .= ':';
548             }
549              
550 49   66     146 while ($sep || $directories) {
551 132 100       319 if (length($sep) > 1) {
552 12         22 my $updir_count = length($sep) - 1;
553 12         42 for (my $i=0; $i<$updir_count; $i++) {
554             # push '::' updir_count times;
555             # simulate Unix '..' updirs
556 15         60 push (@result, '::');
557             }
558             }
559 132         200 $sep = '';
560 132 100       490 if ($directories) {
561 86         405 ( $head, $sep, $tail ) = $directories =~ m|^((?:[^:]+)?)(:*)(.*)|s;
562 86         220 push (@result, $head);
563 86         300 $directories = $tail;
564             }
565             }
566 49         326 return @result;
567             }
568              
569              
570             =item catpath
571              
572             $path = File::Spec->catpath($volume,$directory,$file);
573              
574             Takes volume, directory and file portions and returns an entire path. On Mac OS,
575             $volume, $directory and $file are concatenated. A ':' is inserted if need be. You
576             may pass an empty string for each portion. If all portions are empty, the empty
577             string is returned. If $volume is empty, the result will be a relative path,
578             beginning with a ':'. If $volume and $directory are empty, a leading ":" (if any)
579             is removed form $file and the remainder is returned. If $file is empty, the
580             resulting path will have a trailing ':'.
581              
582              
583             =cut
584              
585             sub catpath {
586 60     60 1 19835 my ($self,$volume,$directory,$file) = @_;
587              
588 60 100 66     220 if ( (! $volume) && (! $directory) ) {
589 3 100       17 $file =~ s/^:// if $file;
590 3         38 return $file ;
591             }
592              
593             # We look for a volume in $volume, then in $directory, but not both
594              
595 57         158 my ($dir_volume, $dir_dirs) = $self->splitpath($directory, 1);
596              
597 57 100       168 $volume = $dir_volume unless length $volume;
598 57         94 my $path = $volume; # may be ''
599 57 100       160 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':'
600              
601 57 100       150 if ($directory) {
602 53 100       132 $directory = $dir_dirs if $volume;
603 53         185 $directory =~ s/^://; # remove leading ':' if any
604 53         102 $path .= $directory;
605 53 100       163 $path .= ':' unless (substr($path, -1) eq ':'); # ensure trailing ':'
606             }
607              
608 57 100       141 if ($file) {
609 22         48 $file =~ s/^://; # remove leading ':' if any
610 22         43 $path .= $file;
611             }
612              
613 57         495 return $path;
614             }
615              
616             =item abs2rel
617              
618             Takes a destination path and an optional base path and returns a relative path
619             from the base path to the destination path:
620              
621             $rel_path = File::Spec->abs2rel( $path ) ;
622             $rel_path = File::Spec->abs2rel( $path, $base ) ;
623              
624             Note that both paths are assumed to have a notation that distinguishes a
625             directory path (with trailing ':') from a file path (without trailing ':').
626              
627             If $base is not present or '', then the current working directory is used.
628             If $base is relative, then it is converted to absolute form using C.
629             This means that it is taken to be relative to the current working directory.
630              
631             If $path and $base appear to be on two different volumes, we will not
632             attempt to resolve the two paths, and we will instead simply return
633             $path. Note that previous versions of this module ignored the volume
634             of $base, which resulted in garbage results part of the time.
635              
636             If $base doesn't have a trailing colon, the last element of $base is
637             assumed to be a filename. This filename is ignored. Otherwise all path
638             components are assumed to be directories.
639              
640             If $path is relative, it is converted to absolute form using C.
641             This means that it is taken to be relative to the current working directory.
642              
643             Based on code written by Shigio Yamaguchi.
644              
645              
646             =cut
647              
648             # maybe this should be done in canonpath() ?
649             sub _resolve_updirs {
650 21     21   34 my $path = shift @_;
651 21         28 my $proceed;
652              
653             # resolve any updirs, e.g. "HD:tmp::file" -> "HD:file"
654 21         31 do {
655 25         125 $proceed = ($path =~ s/^(.*):[^:]+::(.*?)\z/$1:$2/);
656             } while ($proceed);
657              
658 21         51 return $path;
659             }
660              
661              
662             sub abs2rel {
663 21     21 1 9945 my($self,$path,$base) = @_;
664              
665             # Clean up $path
666 21 50       60 if ( ! $self->file_name_is_absolute( $path ) ) {
667 0         0 $path = $self->rel2abs( $path ) ;
668             }
669              
670             # Figure out the effective $base and clean it up.
671 21 50 33     138 if ( !defined( $base ) || $base eq '' ) {
    50          
672 0         0 $base = $self->_cwd();
673             }
674             elsif ( ! $self->file_name_is_absolute( $base ) ) {
675 0         0 $base = $self->rel2abs( $base ) ;
676 0         0 $base = _resolve_updirs( $base ); # resolve updirs in $base
677             }
678             else {
679 21         50 $base = _resolve_updirs( $base );
680             }
681              
682             # Split up paths - ignore $base's file
683 21         61 my ( $path_vol, $path_dirs, $path_file ) = $self->splitpath( $path );
684 21         61 my ( $base_vol, $base_dirs ) = $self->splitpath( $base );
685              
686 21 100       102 return $path unless lc( $path_vol ) eq lc( $base_vol );
687              
688             # Now, remove all leading components that are the same
689 18         47 my @pathchunks = $self->splitdir( $path_dirs );
690 18         47 my @basechunks = $self->splitdir( $base_dirs );
691            
692 18   100     158 while ( @pathchunks &&
      100        
693             @basechunks &&
694             lc( $pathchunks[0] ) eq lc( $basechunks[0] ) ) {
695 17         27 shift @pathchunks ;
696 17         102 shift @basechunks ;
697             }
698              
699             # @pathchunks now has the directories to descend in to.
700             # ensure relative path, even if @pathchunks is empty
701 18         57 $path_dirs = $self->catdir( ':', @pathchunks );
702              
703             # @basechunks now contains the number of directories to climb out of.
704 18         56 $base_dirs = (':' x @basechunks) . ':' ;
705              
706 18         47 return $self->catpath( '', $self->catdir( $base_dirs, $path_dirs ), $path_file ) ;
707             }
708              
709             =item rel2abs
710              
711             Converts a relative path to an absolute path:
712              
713             $abs_path = File::Spec->rel2abs( $path ) ;
714             $abs_path = File::Spec->rel2abs( $path, $base ) ;
715              
716             Note that both paths are assumed to have a notation that distinguishes a
717             directory path (with trailing ':') from a file path (without trailing ':').
718              
719             If $base is not present or '', then $base is set to the current working
720             directory. If $base is relative, then it is converted to absolute form
721             using C. This means that it is taken to be relative to the
722             current working directory.
723              
724             If $base doesn't have a trailing colon, the last element of $base is
725             assumed to be a filename. This filename is ignored. Otherwise all path
726             components are assumed to be directories.
727              
728             If $path is already absolute, it is returned and $base is ignored.
729              
730             Based on code written by Shigio Yamaguchi.
731              
732             =cut
733              
734             sub rel2abs {
735 20     20 1 7011 my ($self,$path,$base) = @_;
736              
737 20 100       49 if ( ! $self->file_name_is_absolute($path) ) {
738             # Figure out the effective $base and clean it up.
739 7 50 33     45 if ( !defined( $base ) || $base eq '' ) {
    50          
740 0         0 $base = $self->_cwd();
741             }
742             elsif ( ! $self->file_name_is_absolute($base) ) {
743 0         0 $base = $self->rel2abs($base) ;
744             }
745              
746             # Split up paths
747              
748             # ignore $path's volume
749 7         24 my ( $path_dirs, $path_file ) = ($self->splitpath($path))[1,2] ;
750              
751             # ignore $base's file part
752 7         20 my ( $base_vol, $base_dirs ) = $self->splitpath($base) ;
753              
754             # Glom them together
755 7 50       49 $path_dirs = ':' if ($path_dirs eq '');
756 7         28 $base_dirs =~ s/:$//; # remove trailing ':', if any
757 7         14 $base_dirs = $base_dirs . $path_dirs;
758              
759 7         18 $path = $self->catpath( $base_vol, $base_dirs, $path_file );
760             }
761 20         123 return $path;
762             }
763              
764              
765             =back
766              
767             =head1 AUTHORS
768              
769             See the authors list in I. Mac OS support by Paul Schinder
770             and Thomas Wegner .
771              
772             =head1 COPYRIGHT
773              
774             Copyright (c) 2004 by the Perl 5 Porters. All rights reserved.
775              
776             This program is free software; you can redistribute it and/or modify
777             it under the same terms as Perl itself.
778              
779             =head1 SEE ALSO
780              
781             See L and L. This package overrides the
782             implementation of these methods, not the semantics.
783              
784             =cut
785              
786             1;