home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 December / PCpro_2006_12.ISO / ossdvd / server / Perl2 / lib / File / Spec / Mac.pm < prev    next >
Encoding:
Perl POD Document  |  2002-06-19  |  22.2 KB  |  767 lines

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