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