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