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