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