| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package File::Spec::Unix; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | use strict; | 
| 4 |  |  |  |  |  |  | use vars qw($VERSION); | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | $VERSION = '3.62'; | 
| 7 |  |  |  |  |  |  | my $xs_version = $VERSION; | 
| 8 |  |  |  |  |  |  | $VERSION =~ tr/_//d; | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | #dont try to load XSLoader and DynaLoader only to ultimately fail on miniperl | 
| 11 |  |  |  |  |  |  | if(!defined &canonpath && defined &DynaLoader::boot_DynaLoader) { | 
| 12 |  |  |  |  |  |  | eval {#eval is questionable since we are handling potential errors like | 
| 13 |  |  |  |  |  |  | #"Cwd object version 3.48 does not match bootstrap parameter 3.50 | 
| 14 |  |  |  |  |  |  | #at lib/DynaLoader.pm line 216." by having this eval | 
| 15 |  |  |  |  |  |  | if ( $] >= 5.006 ) { | 
| 16 |  |  |  |  |  |  | require XSLoader; | 
| 17 |  |  |  |  |  |  | XSLoader::load("Cwd", $xs_version); | 
| 18 |  |  |  |  |  |  | } else { | 
| 19 |  |  |  |  |  |  | require Cwd; | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  | }; | 
| 22 |  |  |  |  |  |  | } | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | =head1 NAME | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | File::Spec::Unix - File::Spec for Unix, base for other File::Spec modules | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | require File::Spec::Unix; # Done automatically by File::Spec | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | Methods for manipulating file specifications.  Other File::Spec | 
| 35 |  |  |  |  |  |  | modules, such as File::Spec::Mac, inherit from File::Spec::Unix and | 
| 36 |  |  |  |  |  |  | override specific methods. | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =head1 METHODS | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | =over 2 | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =item canonpath() | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | No physical check on the filesystem, but a logical cleanup of a | 
| 45 |  |  |  |  |  |  | path. On UNIX eliminates successive slashes and successive "/.". | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | $cpath = File::Spec->canonpath( $path ) ; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | Note that this does *not* collapse F sections into F.  This | 
| 50 |  |  |  |  |  |  | is by design.  If F on your system is a symlink to F, | 
| 51 |  |  |  |  |  |  | then F is actually F, not F as a naive | 
| 52 |  |  |  |  |  |  | F<../>-removal would give you.  If you want to do this kind of | 
| 53 |  |  |  |  |  |  | processing, you probably want C's C function to | 
| 54 |  |  |  |  |  |  | actually traverse the filesystem cleaning up paths like this. | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =cut | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | sub _pp_canonpath { | 
| 59 | 0 |  |  | 0 |  |  | my ($self,$path) = @_; | 
| 60 | 0 | 0 |  |  |  |  | return unless defined $path; | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | # Handle POSIX-style node names beginning with double slash (qnx, nto) | 
| 63 |  |  |  |  |  |  | # (POSIX says: "a pathname that begins with two successive slashes | 
| 64 |  |  |  |  |  |  | # may be interpreted in an implementation-defined manner, although | 
| 65 |  |  |  |  |  |  | # more than two leading slashes shall be treated as a single slash.") | 
| 66 | 0 |  |  |  |  |  | my $node = ''; | 
| 67 | 0 |  | 0 |  |  |  | my $double_slashes_special = $^O eq 'qnx' || $^O eq 'nto'; | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 0 | 0 | 0 |  |  |  | if ( $double_slashes_special | 
|  |  |  | 0 |  |  |  |  | 
| 71 |  |  |  |  |  |  | && ( $path =~ s{^(//[^/]+)/?\z}{}s || $path =~ s{^(//[^/]+)/}{/}s ) ) { | 
| 72 | 0 |  |  |  |  |  | $node = $1; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  | # This used to be | 
| 75 |  |  |  |  |  |  | # $path =~ s|/+|/|g unless ($^O eq 'cygwin'); | 
| 76 |  |  |  |  |  |  | # but that made tests 29, 30, 35, 46, and 213 (as of #13272) to fail | 
| 77 |  |  |  |  |  |  | # (Mainly because trailing "" directories didn't get stripped). | 
| 78 |  |  |  |  |  |  | # Why would cygwin avoid collapsing multiple slashes into one? --jhi | 
| 79 | 0 |  |  |  |  |  | $path =~ s|/{2,}|/|g;                            # xx////xx  -> xx/xx | 
| 80 | 0 |  |  |  |  |  | $path =~ s{(?:/\.)+(?:/|\z)}{/}g;                # xx/././xx -> xx/xx | 
| 81 | 0 | 0 |  |  |  |  | $path =~ s|^(?:\./)+||s unless $path eq "./";    # ./xx      -> xx | 
| 82 | 0 |  |  |  |  |  | $path =~ s|^/(?:\.\./)+|/|;                      # /../../xx -> xx | 
| 83 | 0 |  |  |  |  |  | $path =~ s|^/\.\.$|/|;                         # /..       -> / | 
| 84 | 0 | 0 |  |  |  |  | $path =~ s|/\z|| unless $path eq "/";          # xx/       -> xx | 
| 85 | 0 |  |  |  |  |  | return "$node$path"; | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  | *canonpath = \&_pp_canonpath unless defined &canonpath; | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =item catdir() | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | Concatenate two or more directory names to form a complete path ending | 
| 92 |  |  |  |  |  |  | with a directory. But remove the trailing slash from the resulting | 
| 93 |  |  |  |  |  |  | string, because it doesn't look good, isn't necessary and confuses | 
| 94 |  |  |  |  |  |  | OS2. Of course, if this is the root directory, don't cut off the | 
| 95 |  |  |  |  |  |  | trailing slash :-) | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | =cut | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub _pp_catdir { | 
| 100 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 0 |  |  |  |  |  | $self->canonpath(join('/', @_, '')); # '' because need a trailing '/' | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  | *catdir = \&_pp_catdir unless defined &catdir; | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | =item catfile | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | Concatenate one or more directory names and a filename to form a | 
| 109 |  |  |  |  |  |  | complete path ending with a filename | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | =cut | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | sub _pp_catfile { | 
| 114 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 115 | 0 |  |  |  |  |  | my $file = $self->canonpath(pop @_); | 
| 116 | 0 | 0 |  |  |  |  | return $file unless @_; | 
| 117 | 0 |  |  |  |  |  | my $dir = $self->catdir(@_); | 
| 118 | 0 | 0 |  |  |  |  | $dir .= "/" unless substr($dir,-1) eq "/"; | 
| 119 | 0 |  |  |  |  |  | return $dir.$file; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | *catfile = \&_pp_catfile unless defined &catfile; | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | =item curdir | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | Returns a string representation of the current directory.  "." on UNIX. | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | =cut | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 0 |  |  | 0 | 1 |  | sub curdir { '.' } | 
| 130 |  |  |  |  |  |  | use constant _fn_curdir => "."; | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | =item devnull | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | Returns a string representation of the null device. "/dev/null" on UNIX. | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | =cut | 
| 137 |  |  |  |  |  |  |  | 
| 138 | 0 |  |  | 0 | 1 |  | sub devnull { '/dev/null' } | 
| 139 |  |  |  |  |  |  | use constant _fn_devnull => "/dev/null"; | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | =item rootdir | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | Returns a string representation of the root directory.  "/" on UNIX. | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | =cut | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 0 |  |  | 0 | 1 |  | sub rootdir { '/' } | 
| 148 |  |  |  |  |  |  | use constant _fn_rootdir => "/"; | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =item tmpdir | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | Returns a string representation of the first writable directory from | 
| 153 |  |  |  |  |  |  | the following list or the current directory if none from the list are | 
| 154 |  |  |  |  |  |  | writable: | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | $ENV{TMPDIR} | 
| 157 |  |  |  |  |  |  | /tmp | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | If running under taint mode, and if $ENV{TMPDIR} | 
| 160 |  |  |  |  |  |  | is tainted, it is not used. | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =cut | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | my ($tmpdir, %tmpenv); | 
| 165 |  |  |  |  |  |  | # Cache and return the calculated tmpdir, recording which env vars | 
| 166 |  |  |  |  |  |  | # determined it. | 
| 167 |  |  |  |  |  |  | sub _cache_tmpdir { | 
| 168 | 0 |  |  | 0 |  |  | @tmpenv{@_[2..$#_]} = @ENV{@_[2..$#_]}; | 
| 169 | 0 |  |  |  |  |  | return $tmpdir = $_[1]; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | # Retrieve the cached tmpdir, checking first whether relevant env vars have | 
| 172 |  |  |  |  |  |  | # changed and invalidated the cache. | 
| 173 |  |  |  |  |  |  | sub _cached_tmpdir { | 
| 174 | 0 |  |  | 0 |  |  | shift; | 
| 175 | 0 |  |  |  |  |  | local $^W; | 
| 176 | 0 | 0 |  |  |  |  | return if grep $ENV{$_} ne $tmpenv{$_}, @_; | 
| 177 | 0 |  |  |  |  |  | return $tmpdir; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | sub _tmpdir { | 
| 180 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 181 | 0 |  |  |  |  |  | my @dirlist = @_; | 
| 182 | 0 |  |  |  |  |  | my $taint = do { no strict 'refs'; ${"\cTAINT"} }; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 183 | 0 | 0 |  |  |  |  | if ($taint) { # Check for taint mode on perl >= 5.8.0 | 
|  |  | 0 |  |  |  |  |  | 
| 184 | 0 |  |  |  |  |  | require Scalar::Util; | 
| 185 | 0 |  |  |  |  |  | @dirlist = grep { ! Scalar::Util::tainted($_) } @dirlist; | 
|  | 0 |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | elsif ($] < 5.007) { # No ${^TAINT} before 5.8 | 
| 188 | 0 |  |  |  |  |  | @dirlist = grep { eval { eval('1'.substr $_,0,0) } } @dirlist; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 0 |  |  |  |  |  | foreach (@dirlist) { | 
| 192 | 0 | 0 | 0 |  |  |  | next unless defined && -d && -w _; | 
|  |  |  | 0 |  |  |  |  | 
| 193 | 0 |  |  |  |  |  | $tmpdir = $_; | 
| 194 | 0 |  |  |  |  |  | last; | 
| 195 |  |  |  |  |  |  | } | 
| 196 | 0 | 0 |  |  |  |  | $tmpdir = $self->curdir unless defined $tmpdir; | 
| 197 | 0 |  | 0 |  |  |  | $tmpdir = defined $tmpdir && $self->canonpath($tmpdir); | 
| 198 | 0 | 0 |  |  |  |  | if ( !$self->file_name_is_absolute($tmpdir) ) { | 
| 199 |  |  |  |  |  |  | # See [perl #120593] for the full details | 
| 200 |  |  |  |  |  |  | # If possible, return a full path, rather than '.' or 'lib', but | 
| 201 |  |  |  |  |  |  | # jump through some hoops to avoid returning a tainted value. | 
| 202 |  |  |  |  |  |  | ($tmpdir) = grep { | 
| 203 | 0 |  |  |  |  |  | $taint     ? ! Scalar::Util::tainted($_) : | 
| 204 | 0 | 0 |  |  |  |  | $] < 5.007 ? eval { eval('1'.substr $_,0,0) } : 1 | 
|  | 0 | 0 |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | } $self->rel2abs($tmpdir), $tmpdir; | 
| 206 |  |  |  |  |  |  | } | 
| 207 | 0 |  |  |  |  |  | return $tmpdir; | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | sub tmpdir { | 
| 211 | 0 |  |  | 0 | 1 |  | my $cached = $_[0]->_cached_tmpdir('TMPDIR'); | 
| 212 | 0 | 0 |  |  |  |  | return $cached if defined $cached; | 
| 213 | 0 |  |  |  |  |  | $_[0]->_cache_tmpdir($_[0]->_tmpdir( $ENV{TMPDIR}, "/tmp" ), 'TMPDIR'); | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | =item updir | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | Returns a string representation of the parent directory.  ".." on UNIX. | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | =cut | 
| 221 |  |  |  |  |  |  |  | 
| 222 | 0 |  |  | 0 | 1 |  | sub updir { '..' } | 
| 223 |  |  |  |  |  |  | use constant _fn_updir => ".."; | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =item no_upwards | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | Given a list of file names, strip out those that refer to a parent | 
| 228 |  |  |  |  |  |  | directory. (Does not strip symlinks, only '.', '..', and equivalents.) | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | =cut | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | sub no_upwards { | 
| 233 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 234 | 0 |  |  |  |  |  | return grep(!/^\.{1,2}\z/s, @_); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | =item case_tolerant | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | Returns a true or false value indicating, respectively, that alphabetic | 
| 240 |  |  |  |  |  |  | is not or is significant when comparing file specifications. | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | =cut | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 0 |  |  | 0 | 1 |  | sub case_tolerant { 0 } | 
| 245 |  |  |  |  |  |  | use constant _fn_case_tolerant => 0; | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | =item file_name_is_absolute | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | Takes as argument a path and returns true if it is an absolute path. | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | This does not consult the local filesystem on Unix, Win32, OS/2 or Mac | 
| 252 |  |  |  |  |  |  | OS (Classic).  It does consult the working environment for VMS (see | 
| 253 |  |  |  |  |  |  | L). | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | =cut | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | sub file_name_is_absolute { | 
| 258 | 0 |  |  | 0 | 1 |  | my ($self,$file) = @_; | 
| 259 | 0 |  |  |  |  |  | return scalar($file =~ m:^/:s); | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | =item path | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | Takes no argument, returns the environment variable PATH as an array. | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | =cut | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | sub path { | 
| 269 | 0 | 0 |  | 0 | 1 |  | return () unless exists $ENV{PATH}; | 
| 270 | 0 |  |  |  |  |  | my @path = split(':', $ENV{PATH}); | 
| 271 | 0 | 0 |  |  |  |  | foreach (@path) { $_ = '.' if $_ eq '' } | 
|  | 0 |  |  |  |  |  |  | 
| 272 | 0 |  |  |  |  |  | return @path; | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | =item join | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | join is the same as catfile. | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | =cut | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | sub join { | 
| 282 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 283 | 0 |  |  |  |  |  | return $self->catfile(@_); | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =item splitpath | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | ($volume,$directories,$file) = File::Spec->splitpath( $path ); | 
| 289 |  |  |  |  |  |  | ($volume,$directories,$file) = File::Spec->splitpath( $path, | 
| 290 |  |  |  |  |  |  | $no_file ); | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | Splits a path into volume, directory, and filename portions. On systems | 
| 293 |  |  |  |  |  |  | with no concept of volume, returns '' for volume. | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | For systems with no syntax differentiating filenames from directories, | 
| 296 |  |  |  |  |  |  | assumes that the last file is a path unless $no_file is true or a | 
| 297 |  |  |  |  |  |  | trailing separator or /. or /.. is present. On Unix this means that $no_file | 
| 298 |  |  |  |  |  |  | true makes this return ( '', $path, '' ). | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | The directory portion may or may not be returned with a trailing '/'. | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | The results can be passed to L to get back a path equivalent to | 
| 303 |  |  |  |  |  |  | (usually identical to) the original path. | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | =cut | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | sub splitpath { | 
| 308 | 0 |  |  | 0 | 1 |  | my ($self,$path, $nofile) = @_; | 
| 309 |  |  |  |  |  |  |  | 
| 310 | 0 |  |  |  |  |  | my ($volume,$directory,$file) = ('','',''); | 
| 311 |  |  |  |  |  |  |  | 
| 312 | 0 | 0 |  |  |  |  | if ( $nofile ) { | 
| 313 | 0 |  |  |  |  |  | $directory = $path; | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  | else { | 
| 316 | 0 |  |  |  |  |  | $path =~ m|^ ( (?: .* / (?: \.\.?\z )? )? ) ([^/]*) |xs; | 
| 317 | 0 |  |  |  |  |  | $directory = $1; | 
| 318 | 0 |  |  |  |  |  | $file      = $2; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 0 |  |  |  |  |  | return ($volume,$directory,$file); | 
| 322 |  |  |  |  |  |  | } | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | =item splitdir | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | The opposite of L. | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | @dirs = File::Spec->splitdir( $directories ); | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | $directories must be only the directory portion of the path on systems | 
| 332 |  |  |  |  |  |  | that have the concept of a volume or that have path syntax that differentiates | 
| 333 |  |  |  |  |  |  | files from directories. | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | Unlike just splitting the directories on the separator, empty | 
| 336 |  |  |  |  |  |  | directory names (C<''>) can be returned, because these are significant | 
| 337 |  |  |  |  |  |  | on some OSs. | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | On Unix, | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | File::Spec->splitdir( "/a/b//c/" ); | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | Yields: | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | ( '', 'a', 'b', '', 'c', '' ) | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | =cut | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | sub splitdir { | 
| 350 | 0 |  |  | 0 | 1 |  | return split m|/|, $_[1], -1;  # Preserve trailing fields | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | =item catpath() | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | Takes volume, directory and file portions and returns an entire path. Under | 
| 357 |  |  |  |  |  |  | Unix, $volume is ignored, and directory and file are concatenated.  A '/' is | 
| 358 |  |  |  |  |  |  | inserted if needed (though if the directory portion doesn't start with | 
| 359 |  |  |  |  |  |  | '/' it is not added).  On other OSs, $volume is significant. | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =cut | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | sub catpath { | 
| 364 | 0 |  |  | 0 | 1 |  | my ($self,$volume,$directory,$file) = @_; | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 0 | 0 | 0 |  |  |  | if ( $directory ne ''                && | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 367 |  |  |  |  |  |  | $file ne ''                     && | 
| 368 |  |  |  |  |  |  | substr( $directory, -1 ) ne '/' && | 
| 369 |  |  |  |  |  |  | substr( $file, 0, 1 ) ne '/' | 
| 370 |  |  |  |  |  |  | ) { | 
| 371 | 0 |  |  |  |  |  | $directory .= "/$file" ; | 
| 372 |  |  |  |  |  |  | } | 
| 373 |  |  |  |  |  |  | else { | 
| 374 | 0 |  |  |  |  |  | $directory .= $file ; | 
| 375 |  |  |  |  |  |  | } | 
| 376 |  |  |  |  |  |  |  | 
| 377 | 0 |  |  |  |  |  | return $directory ; | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =item abs2rel | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | Takes a destination path and an optional base path returns a relative path | 
| 383 |  |  |  |  |  |  | from the base path to the destination path: | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | $rel_path = File::Spec->abs2rel( $path ) ; | 
| 386 |  |  |  |  |  |  | $rel_path = File::Spec->abs2rel( $path, $base ) ; | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | If $base is not present or '', then L is used. If $base is | 
| 389 |  |  |  |  |  |  | relative, then it is converted to absolute form using | 
| 390 |  |  |  |  |  |  | L. This means that it is taken to be relative to | 
| 391 |  |  |  |  |  |  | L. | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | On systems that have a grammar that indicates filenames, this ignores the | 
| 394 |  |  |  |  |  |  | $base filename. Otherwise all path components are assumed to be | 
| 395 |  |  |  |  |  |  | directories. | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | If $path is relative, it is converted to absolute form using L. | 
| 398 |  |  |  |  |  |  | This means that it is taken to be relative to L. | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | No checks against the filesystem are made, so the result may not be correct if | 
| 401 |  |  |  |  |  |  | C<$base> contains symbolic links.  (Apply | 
| 402 |  |  |  |  |  |  | L beforehand if that | 
| 403 |  |  |  |  |  |  | is a concern.)  On VMS, there is interaction with the working environment, as | 
| 404 |  |  |  |  |  |  | logicals and macros are expanded. | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | Based on code written by Shigio Yamaguchi. | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | =cut | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | sub abs2rel { | 
| 411 | 0 |  |  | 0 | 1 |  | my($self,$path,$base) = @_; | 
| 412 | 0 | 0 | 0 |  |  |  | $base = $self->_cwd() unless defined $base and length $base; | 
| 413 |  |  |  |  |  |  |  | 
| 414 | 0 |  |  |  |  |  | ($path, $base) = map $self->canonpath($_), $path, $base; | 
| 415 |  |  |  |  |  |  |  | 
| 416 | 0 |  |  |  |  |  | my $path_directories; | 
| 417 |  |  |  |  |  |  | my $base_directories; | 
| 418 |  |  |  |  |  |  |  | 
| 419 | 0 | 0 |  |  |  |  | if (grep $self->file_name_is_absolute($_), $path, $base) { | 
| 420 | 0 |  |  |  |  |  | ($path, $base) = map $self->rel2abs($_), $path, $base; | 
| 421 |  |  |  |  |  |  |  | 
| 422 | 0 |  |  |  |  |  | my ($path_volume) = $self->splitpath($path, 1); | 
| 423 | 0 |  |  |  |  |  | my ($base_volume) = $self->splitpath($base, 1); | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | # Can't relativize across volumes | 
| 426 | 0 | 0 |  |  |  |  | return $path unless $path_volume eq $base_volume; | 
| 427 |  |  |  |  |  |  |  | 
| 428 | 0 |  |  |  |  |  | $path_directories = ($self->splitpath($path, 1))[1]; | 
| 429 | 0 |  |  |  |  |  | $base_directories = ($self->splitpath($base, 1))[1]; | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | # For UNC paths, the user might give a volume like //foo/bar that | 
| 432 |  |  |  |  |  |  | # strictly speaking has no directory portion.  Treat it as if it | 
| 433 |  |  |  |  |  |  | # had the root directory for that volume. | 
| 434 | 0 | 0 | 0 |  |  |  | if (!length($base_directories) and $self->file_name_is_absolute($base)) { | 
| 435 | 0 |  |  |  |  |  | $base_directories = $self->rootdir; | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  | } | 
| 438 |  |  |  |  |  |  | else { | 
| 439 | 0 |  |  |  |  |  | my $wd= ($self->splitpath($self->_cwd(), 1))[1]; | 
| 440 | 0 |  |  |  |  |  | $path_directories = $self->catdir($wd, $path); | 
| 441 | 0 |  |  |  |  |  | $base_directories = $self->catdir($wd, $base); | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | # Now, remove all leading components that are the same | 
| 445 | 0 |  |  |  |  |  | my @pathchunks = $self->splitdir( $path_directories ); | 
| 446 | 0 |  |  |  |  |  | my @basechunks = $self->splitdir( $base_directories ); | 
| 447 |  |  |  |  |  |  |  | 
| 448 | 0 | 0 |  |  |  |  | if ($base_directories eq $self->rootdir) { | 
| 449 | 0 | 0 |  |  |  |  | return $self->curdir if $path_directories eq $self->rootdir; | 
| 450 | 0 |  |  |  |  |  | shift @pathchunks; | 
| 451 | 0 |  |  |  |  |  | return $self->canonpath( $self->catpath('', $self->catdir( @pathchunks ), '') ); | 
| 452 |  |  |  |  |  |  | } | 
| 453 |  |  |  |  |  |  |  | 
| 454 | 0 |  |  |  |  |  | my @common; | 
| 455 | 0 |  | 0 |  |  |  | while (@pathchunks && @basechunks && $self->_same($pathchunks[0], $basechunks[0])) { | 
|  |  |  | 0 |  |  |  |  | 
| 456 | 0 |  |  |  |  |  | push @common, shift @pathchunks ; | 
| 457 | 0 |  |  |  |  |  | shift @basechunks ; | 
| 458 |  |  |  |  |  |  | } | 
| 459 | 0 | 0 | 0 |  |  |  | return $self->curdir unless @pathchunks || @basechunks; | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | # @basechunks now contains the directories the resulting relative path | 
| 462 |  |  |  |  |  |  | # must ascend out of before it can descend to $path_directory.  If there | 
| 463 |  |  |  |  |  |  | # are updir components, we must descend into the corresponding directories | 
| 464 |  |  |  |  |  |  | # (this only works if they are no symlinks). | 
| 465 | 0 |  |  |  |  |  | my @reverse_base; | 
| 466 | 0 |  |  |  |  |  | while( defined(my $dir= shift @basechunks) ) { | 
| 467 | 0 | 0 |  |  |  |  | if( $dir ne $self->updir ) { | 
|  |  | 0 |  |  |  |  |  | 
| 468 | 0 |  |  |  |  |  | unshift @reverse_base, $self->updir; | 
| 469 | 0 |  |  |  |  |  | push @common, $dir; | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  | elsif( @common ) { | 
| 472 | 0 | 0 | 0 |  |  |  | if( @reverse_base && $reverse_base[0] eq $self->updir ) { | 
| 473 | 0 |  |  |  |  |  | shift @reverse_base; | 
| 474 | 0 |  |  |  |  |  | pop @common; | 
| 475 |  |  |  |  |  |  | } | 
| 476 |  |  |  |  |  |  | else { | 
| 477 | 0 |  |  |  |  |  | unshift @reverse_base, pop @common; | 
| 478 |  |  |  |  |  |  | } | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  | } | 
| 481 | 0 |  |  |  |  |  | my $result_dirs = $self->catdir( @reverse_base, @pathchunks ); | 
| 482 | 0 |  |  |  |  |  | return $self->canonpath( $self->catpath('', $result_dirs, '') ); | 
| 483 |  |  |  |  |  |  | } | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | sub _same { | 
| 486 | 0 |  |  | 0 |  |  | $_[1] eq $_[2]; | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =item rel2abs() | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | Converts a relative path to an absolute path. | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | $abs_path = File::Spec->rel2abs( $path ) ; | 
| 494 |  |  |  |  |  |  | $abs_path = File::Spec->rel2abs( $path, $base ) ; | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | If $base is not present or '', then L is used. If $base is | 
| 497 |  |  |  |  |  |  | relative, then it is converted to absolute form using | 
| 498 |  |  |  |  |  |  | L. This means that it is taken to be relative to | 
| 499 |  |  |  |  |  |  | L. | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | On systems that have a grammar that indicates filenames, this ignores | 
| 502 |  |  |  |  |  |  | the $base filename. Otherwise all path components are assumed to be | 
| 503 |  |  |  |  |  |  | directories. | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | If $path is absolute, it is cleaned up and returned using L. | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | No checks against the filesystem are made.  On VMS, there is | 
| 508 |  |  |  |  |  |  | interaction with the working environment, as logicals and | 
| 509 |  |  |  |  |  |  | macros are expanded. | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | Based on code written by Shigio Yamaguchi. | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | =cut | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | sub rel2abs { | 
| 516 | 0 |  |  | 0 | 1 |  | my ($self,$path,$base ) = @_; | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | # Clean up $path | 
| 519 | 0 | 0 |  |  |  |  | if ( ! $self->file_name_is_absolute( $path ) ) { | 
| 520 |  |  |  |  |  |  | # Figure out the effective $base and clean it up. | 
| 521 | 0 | 0 | 0 |  |  |  | if ( !defined( $base ) || $base eq '' ) { | 
|  |  | 0 |  |  |  |  |  | 
| 522 | 0 |  |  |  |  |  | $base = $self->_cwd(); | 
| 523 |  |  |  |  |  |  | } | 
| 524 |  |  |  |  |  |  | elsif ( ! $self->file_name_is_absolute( $base ) ) { | 
| 525 | 0 |  |  |  |  |  | $base = $self->rel2abs( $base ) ; | 
| 526 |  |  |  |  |  |  | } | 
| 527 |  |  |  |  |  |  | else { | 
| 528 | 0 |  |  |  |  |  | $base = $self->canonpath( $base ) ; | 
| 529 |  |  |  |  |  |  | } | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | # Glom them together | 
| 532 | 0 |  |  |  |  |  | $path = $self->catdir( $base, $path ) ; | 
| 533 |  |  |  |  |  |  | } | 
| 534 |  |  |  |  |  |  |  | 
| 535 | 0 |  |  |  |  |  | return $self->canonpath( $path ) ; | 
| 536 |  |  |  |  |  |  | } | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | =back | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | Copyright (c) 2004 by the Perl 5 Porters.  All rights reserved. | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify | 
| 545 |  |  |  |  |  |  | it under the same terms as Perl itself. | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | Please submit bug reports and patches to perlbug@perl.org. | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | L | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | =cut | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | # Internal routine to File::Spec, no point in making this public since | 
| 556 |  |  |  |  |  |  | # it is the standard Cwd interface.  Most of the platform-specific | 
| 557 |  |  |  |  |  |  | # File::Spec subclasses use this. | 
| 558 |  |  |  |  |  |  | sub _cwd { | 
| 559 | 0 |  |  | 0 |  |  | require Cwd; | 
| 560 | 0 |  |  |  |  |  | Cwd::getcwd(); | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | # Internal method to reduce xx\..\yy -> yy | 
| 565 |  |  |  |  |  |  | sub _collapse { | 
| 566 | 0 |  |  | 0 |  |  | my($fs, $path) = @_; | 
| 567 |  |  |  |  |  |  |  | 
| 568 | 0 |  |  |  |  |  | my $updir  = $fs->updir; | 
| 569 | 0 |  |  |  |  |  | my $curdir = $fs->curdir; | 
| 570 |  |  |  |  |  |  |  | 
| 571 | 0 |  |  |  |  |  | my($vol, $dirs, $file) = $fs->splitpath($path); | 
| 572 | 0 |  |  |  |  |  | my @dirs = $fs->splitdir($dirs); | 
| 573 | 0 | 0 | 0 |  |  |  | pop @dirs if @dirs && $dirs[-1] eq ''; | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 0 |  |  |  |  |  | my @collapsed; | 
| 576 | 0 |  |  |  |  |  | foreach my $dir (@dirs) { | 
| 577 | 0 | 0 | 0 |  |  |  | if( $dir eq $updir              and   # if we have an updir | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 578 |  |  |  |  |  |  | @collapsed                  and   # and something to collapse | 
| 579 |  |  |  |  |  |  | length $collapsed[-1]       and   # and its not the rootdir | 
| 580 |  |  |  |  |  |  | $collapsed[-1] ne $updir    and   # nor another updir | 
| 581 |  |  |  |  |  |  | $collapsed[-1] ne $curdir         # nor the curdir | 
| 582 |  |  |  |  |  |  | ) | 
| 583 |  |  |  |  |  |  | {                                     # then | 
| 584 | 0 |  |  |  |  |  | pop @collapsed;                   # collapse | 
| 585 |  |  |  |  |  |  | } | 
| 586 |  |  |  |  |  |  | else {                                # else | 
| 587 | 0 |  |  |  |  |  | push @collapsed, $dir;            # just hang onto it | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  |  | 
| 591 | 0 |  |  |  |  |  | return $fs->catpath($vol, | 
| 592 |  |  |  |  |  |  | $fs->catdir(@collapsed), | 
| 593 |  |  |  |  |  |  | $file | 
| 594 |  |  |  |  |  |  | ); | 
| 595 |  |  |  |  |  |  | } | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | 1; |