Continue 4-arg substr() UTF-8 fixage.
[p5sagit/p5-mst-13.2.git] / lib / Cwd.pm
CommitLineData
a0d0e21e 1package Cwd;
2require 5.000;
a0d0e21e 3
f06db76b 4=head1 NAME
5
6getcwd - get pathname of current working directory
7
8=head1 SYNOPSIS
9
4633a7c4 10 use Cwd;
11 $dir = cwd;
12
13 use Cwd;
14 $dir = getcwd;
f06db76b 15
16 use Cwd;
4633a7c4 17 $dir = fastgetcwd;
f06db76b 18
19 use Cwd 'chdir';
20 chdir "/tmp";
21 print $ENV{'PWD'};
22
e4c51978 23 use Cwd 'abs_path'; # aka realpath()
20408e3c 24 print abs_path($ENV{'PWD'});
25
26 use Cwd 'fast_abs_path';
27 print fast_abs_path($ENV{'PWD'});
28
f06db76b 29=head1 DESCRIPTION
30
31The getcwd() function re-implements the getcwd(3) (or getwd(3)) functions
4633a7c4 32in Perl.
f06db76b 33
20408e3c 34The abs_path() function takes a single argument and returns the
e4c51978 35absolute pathname for that argument. It uses the same algorithm
36as getcwd(). (Actually, getcwd() is abs_path(".")) Symbolic links
37and relative-path components ("." and "..") are resolved to return
38the canonical pathname, just like realpath(3). Also callable as
39realpath().
20408e3c 40
cb1a09d0 41The fastcwd() function looks the same as getcwd(), but runs faster.
fb73857a 42It's also more dangerous because it might conceivably chdir() you out
43of a directory that it can't chdir() you back into. If fastcwd
44encounters a problem it will return undef but will probably leave you
45in a different directory. For a measure of extra security, if
46everything appears to have worked, the fastcwd() function will check
47that it leaves you in the same directory that it started in. If it has
48changed it will C<die> with the message "Unstable directory path,
49current directory changed unexpectedly". That should never happen.
f06db76b 50
20408e3c 51The fast_abs_path() function looks the same as abs_path(), but runs faster.
52And like fastcwd() is more dangerous.
53
4633a7c4 54The cwd() function looks the same as getcwd and fastgetcwd but is
55implemented using the most natural and safe form for the current
56architecture. For most systems it is identical to `pwd` (but without
fb73857a 57the trailing line terminator).
58
59It is recommended that cwd (or another *cwd() function) is used in
60I<all> code to ensure portability.
4633a7c4 61
62If you ask to override your chdir() built-in function, then your PWD
63environment variable will be kept up to date. (See
55497cff 64L<perlsub/Overriding Builtin Functions>.) Note that it will only be
1fef88e7 65kept up to date if all packages which use chdir import it from Cwd.
4633a7c4 66
f06db76b 67=cut
68
b060a406 69use strict;
96e4d5b1 70
71use Carp;
72
1f4f94f5 73our $VERSION = '2.04';
96e4d5b1 74
b060a406 75use base qw/ Exporter /;
76our @EXPORT = qw(cwd getcwd fastcwd fastgetcwd);
77our @EXPORT_OK = qw(chdir abs_path fast_abs_path realpath fast_realpath);
a0d0e21e 78
4633a7c4 79
8b88ae92 80# The 'natural and safe form' for UNIX (pwd may be setuid root)
96e4d5b1 81
8b88ae92 82sub _backtick_pwd {
7e03f963 83 my $cwd = `pwd`;
84 # `pwd` may fail e.g. if the disk is full
85 chomp($cwd) if defined $cwd;
4633a7c4 86 $cwd;
8b88ae92 87}
4633a7c4 88
89# Since some ports may predefine cwd internally (e.g., NT)
90# we take care not to override an existing definition for cwd().
91
ea54c8bd 92unless(defined &cwd) {
93 # The pwd command is not available in some chroot(2)'ed environments
4aecb5b5 94 if($^O eq 'MacOS' || grep { -x "$_/pwd" } split(':', $ENV{PATH})) {
ea54c8bd 95 *cwd = \&_backtick_pwd;
96 }
97 else {
98 *cwd = \&getcwd;
99 }
100}
a0d0e21e 101
1f4f94f5 102# set a reasonable (and very safe) default for fastgetcwd, in case it
103# isn't redefined later (20001212 rspier)
104*fastgetcwd = \&cwd;
748a9306 105
a0d0e21e 106# By Brandon S. Allbery
107#
108# Usage: $cwd = getcwd();
109
110sub getcwd
111{
07569ed3 112 abs_path('.');
a0d0e21e 113}
114
a0d0e21e 115# By John Bazik
116#
117# Usage: $cwd = &fastcwd;
118#
119# This is a faster version of getcwd. It's also more dangerous because
120# you might chdir out of a directory that you can't chdir back into.
fb73857a 121
a0d0e21e 122sub fastcwd {
123 my($odev, $oino, $cdev, $cino, $tdev, $tino);
124 my(@path, $path);
125 local(*DIR);
126
fb73857a 127 my($orig_cdev, $orig_cino) = stat('.');
128 ($cdev, $cino) = ($orig_cdev, $orig_cino);
a0d0e21e 129 for (;;) {
40000a8c 130 my $direntry;
a0d0e21e 131 ($odev, $oino) = ($cdev, $cino);
e79e61e4 132 CORE::chdir('..') || return undef;
a0d0e21e 133 ($cdev, $cino) = stat('.');
134 last if $odev == $cdev && $oino == $cino;
fb73857a 135 opendir(DIR, '.') || return undef;
a0d0e21e 136 for (;;) {
40000a8c 137 $direntry = readdir(DIR);
fb73857a 138 last unless defined $direntry;
40000a8c 139 next if $direntry eq '.';
140 next if $direntry eq '..';
a0d0e21e 141
40000a8c 142 ($tdev, $tino) = lstat($direntry);
a0d0e21e 143 last unless $tdev != $odev || $tino != $oino;
144 }
145 closedir(DIR);
fb73857a 146 return undef unless defined $direntry; # should never happen
40000a8c 147 unshift(@path, $direntry);
a0d0e21e 148 }
fb73857a 149 $path = '/' . join('/', @path);
21ac5ced 150 if ($^O eq 'apollo') { $path = "/".$path; }
fb73857a 151 # At this point $path may be tainted (if tainting) and chdir would fail.
152 # To be more useful we untaint it then check that we landed where we started.
392d8ab8 153 $path = $1 if $path =~ /^(.*)\z/s; # untaint
e79e61e4 154 CORE::chdir($path) || return undef;
fb73857a 155 ($cdev, $cino) = stat('.');
156 die "Unstable directory path, current directory changed unexpectedly"
157 if $cdev != $orig_cdev || $cino != $orig_cino;
a0d0e21e 158 $path;
159}
160
161
4633a7c4 162# Keeps track of current working directory in PWD environment var
a0d0e21e 163# Usage:
164# use Cwd 'chdir';
165# chdir $newdir;
166
4633a7c4 167my $chdir_init = 0;
a0d0e21e 168
4633a7c4 169sub chdir_init {
3b8e3443 170 if ($ENV{'PWD'} and $^O ne 'os2' and $^O ne 'dos' and $^O ne 'MSWin32') {
a0d0e21e 171 my($dd,$di) = stat('.');
172 my($pd,$pi) = stat($ENV{'PWD'});
173 if (!defined $dd or !defined $pd or $di != $pi or $dd != $pd) {
4633a7c4 174 $ENV{'PWD'} = cwd();
a0d0e21e 175 }
176 }
177 else {
3b8e3443 178 my $wd = cwd();
179 $wd = Win32::GetFullPathName($wd) if $^O eq 'MSWin32';
180 $ENV{'PWD'} = $wd;
a0d0e21e 181 }
4633a7c4 182 # Strip an automounter prefix (where /tmp_mnt/foo/bar == /foo/bar)
3b8e3443 183 if ($^O ne 'MSWin32' and $ENV{'PWD'} =~ m|(/[^/]+(/[^/]+/[^/]+))(.*)|s) {
a0d0e21e 184 my($pd,$pi) = stat($2);
185 my($dd,$di) = stat($1);
186 if (defined $pd and defined $dd and $di == $pi and $dd == $pd) {
187 $ENV{'PWD'}="$2$3";
188 }
189 }
190 $chdir_init = 1;
191}
192
193sub chdir {
22978713 194 my $newdir = @_ ? shift : ''; # allow for no arg (chdir to HOME dir)
3b8e3443 195 $newdir =~ s|///*|/|g unless $^O eq 'MSWin32';
a0d0e21e 196 chdir_init() unless $chdir_init;
4633a7c4 197 return 0 unless CORE::chdir $newdir;
3b8e3443 198 if ($^O eq 'VMS') {
199 return $ENV{'PWD'} = $ENV{'DEFAULT'}
200 }
4aecb5b5 201 elsif ($^O eq 'MacOS') {
202 return $ENV{'PWD'} = cwd();
203 }
3b8e3443 204 elsif ($^O eq 'MSWin32') {
205 $ENV{'PWD'} = Win32::GetFullPathName($newdir);
206 return 1;
207 }
748a9306 208
392d8ab8 209 if ($newdir =~ m#^/#s) {
a0d0e21e 210 $ENV{'PWD'} = $newdir;
4633a7c4 211 } else {
212 my @curdir = split(m#/#,$ENV{'PWD'});
213 @curdir = ('') unless @curdir;
214 my $component;
a0d0e21e 215 foreach $component (split(m#/#, $newdir)) {
216 next if $component eq '.';
217 pop(@curdir),next if $component eq '..';
218 push(@curdir,$component);
219 }
220 $ENV{'PWD'} = join('/',@curdir) || '/';
221 }
4633a7c4 222 1;
a0d0e21e 223}
224
d57281c6 225# Taken from Cwd.pm It is really getcwd with an optional
226# parameter instead of '.'
227#
8b88ae92 228
d57281c6 229sub abs_path
230{
231 my $start = @_ ? shift : '.';
232 my($dotdots, $cwd, @pst, @cst, $dir, @tst);
929327b2 233
d57281c6 234 unless (@cst = stat( $start ))
235 {
236 carp "stat($start): $!";
237 return '';
8b88ae92 238 }
d57281c6 239 $cwd = '';
240 $dotdots = $start;
241 do
242 {
243 $dotdots .= '/..';
244 @pst = @cst;
245 unless (opendir(PARENT, $dotdots))
246 {
247 carp "opendir($dotdots): $!";
248 return '';
249 }
250 unless (@cst = stat($dotdots))
251 {
252 carp "stat($dotdots): $!";
253 closedir(PARENT);
254 return '';
255 }
256 if ($pst[0] == $cst[0] && $pst[1] == $cst[1])
257 {
258 $dir = undef;
259 }
260 else
261 {
262 do
263 {
264 unless (defined ($dir = readdir(PARENT)))
265 {
266 carp "readdir($dotdots): $!";
267 closedir(PARENT);
268 return '';
269 }
270 $tst[0] = $pst[0]+1 unless (@tst = lstat("$dotdots/$dir"))
271 }
272 while ($dir eq '.' || $dir eq '..' || $tst[0] != $pst[0] ||
273 $tst[1] != $pst[1]);
274 }
275 $cwd = (defined $dir ? "$dir" : "" ) . "/$cwd" ;
276 closedir(PARENT);
277 } while (defined $dir);
278 chop($cwd) unless $cwd eq '/'; # drop the trailing /
279 $cwd;
8b88ae92 280}
281
e4c51978 282# added function alias for those of us more
283# used to the libc function. --tchrist 27-Jan-00
284*realpath = \&abs_path;
285
96e4d5b1 286sub fast_abs_path {
287 my $cwd = getcwd();
3c65c14b 288 my $path = @_ ? shift : '.';
e79e61e4 289 CORE::chdir($path) || croak "Cannot chdir to $path:$!";
96e4d5b1 290 my $realpath = getcwd();
e79e61e4 291 CORE::chdir($cwd) || croak "Cannot chdir back to $cwd:$!";
96e4d5b1 292 $realpath;
8b88ae92 293}
294
e4c51978 295# added function alias to follow principle of least surprise
296# based on previous aliasing. --tchrist 27-Jan-00
297*fast_realpath = \&fast_abs_path;
298
4633a7c4 299
300# --- PORTING SECTION ---
301
302# VMS: $ENV{'DEFAULT'} points to default directory at all times
bd3fa61c 303# 06-Mar-1996 Charles Bailey bailey@newman.upenn.edu
c6538b72 304# Note: Use of Cwd::chdir() causes the logical name PWD to be defined
8b88ae92 305# in the process logical name table as the default device and directory
306# seen by Perl. This may not be the same as the default device
4633a7c4 307# and directory seen by DCL after Perl exits, since the effects
308# the CRTL chdir() function persist only until Perl exits.
4633a7c4 309
310sub _vms_cwd {
96e4d5b1 311 return $ENV{'DEFAULT'};
312}
313
314sub _vms_abs_path {
315 return $ENV{'DEFAULT'} unless @_;
316 my $path = VMS::Filespec::pathify($_[0]);
317 croak("Invalid path name $_[0]") unless defined $path;
318 return VMS::Filespec::rmsexpand($path);
4633a7c4 319}
68dc0745 320
4633a7c4 321sub _os2_cwd {
322 $ENV{'PWD'} = `cmd /c cd`;
323 chop $ENV{'PWD'};
324 $ENV{'PWD'} =~ s:\\:/:g ;
325 return $ENV{'PWD'};
326}
327
96e4d5b1 328sub _win32_cwd {
2d7a9237 329 $ENV{'PWD'} = Win32::GetCwd();
96e4d5b1 330 $ENV{'PWD'} =~ s:\\:/:g ;
331 return $ENV{'PWD'};
332}
333
334*_NT_cwd = \&_win32_cwd if (!defined &_NT_cwd &&
2d7a9237 335 defined &Win32::GetCwd);
96e4d5b1 336
337*_NT_cwd = \&_os2_cwd unless defined &_NT_cwd;
68dc0745 338
39e571d4 339sub _dos_cwd {
340 if (!defined &Dos::GetCwd) {
341 $ENV{'PWD'} = `command /c cd`;
342 chop $ENV{'PWD'};
343 $ENV{'PWD'} =~ s:\\:/:g ;
344 } else {
345 $ENV{'PWD'} = Dos::GetCwd();
346 }
55497cff 347 return $ENV{'PWD'};
348}
349
7fbf1995 350sub _qnx_cwd {
351 $ENV{'PWD'} = `/usr/bin/fullpath -t`;
352 chop $ENV{'PWD'};
353 return $ENV{'PWD'};
354}
355
356sub _qnx_abs_path {
fa921dc6 357 my $path = @_ ? shift : '.';
7fbf1995 358 my $realpath=`/usr/bin/fullpath -t $path`;
359 chop $realpath;
360 return $realpath;
361}
362
ed79a026 363sub _epoc_cwd {
364 $ENV{'PWD'} = EPOC::getcwd();
365 return $ENV{'PWD'};
366}
367
ac1ad7f0 368{
db376a24 369 no warnings; # assignments trigger 'subroutine redefined' warning
4633a7c4 370
ac1ad7f0 371 if ($^O eq 'VMS') {
96e4d5b1 372 *cwd = \&_vms_cwd;
373 *getcwd = \&_vms_cwd;
374 *fastcwd = \&_vms_cwd;
375 *fastgetcwd = \&_vms_cwd;
376 *abs_path = \&_vms_abs_path;
377 *fast_abs_path = \&_vms_abs_path;
ac1ad7f0 378 }
379 elsif ($^O eq 'NT' or $^O eq 'MSWin32') {
380 # We assume that &_NT_cwd is defined as an XSUB or in the core.
96e4d5b1 381 *cwd = \&_NT_cwd;
382 *getcwd = \&_NT_cwd;
383 *fastcwd = \&_NT_cwd;
384 *fastgetcwd = \&_NT_cwd;
385 *abs_path = \&fast_abs_path;
ac1ad7f0 386 }
387 elsif ($^O eq 'os2') {
388 # sys_cwd may keep the builtin command
96e4d5b1 389 *cwd = defined &sys_cwd ? \&sys_cwd : \&_os2_cwd;
390 *getcwd = \&cwd;
391 *fastgetcwd = \&cwd;
392 *fastcwd = \&cwd;
393 *abs_path = \&fast_abs_path;
ac1ad7f0 394 }
39e571d4 395 elsif ($^O eq 'dos') {
396 *cwd = \&_dos_cwd;
397 *getcwd = \&_dos_cwd;
398 *fastgetcwd = \&_dos_cwd;
399 *fastcwd = \&_dos_cwd;
96e4d5b1 400 *abs_path = \&fast_abs_path;
ac1ad7f0 401 }
7fbf1995 402 elsif ($^O eq 'qnx') {
403 *cwd = \&_qnx_cwd;
404 *getcwd = \&_qnx_cwd;
405 *fastgetcwd = \&_qnx_cwd;
406 *fastcwd = \&_qnx_cwd;
407 *abs_path = \&_qnx_abs_path;
408 *fast_abs_path = \&_qnx_abs_path;
409 }
4fabb596 410 elsif ($^O eq 'cygwin') {
1cab015a 411 *getcwd = \&cwd;
412 *fastgetcwd = \&cwd;
413 *fastcwd = \&cwd;
414 *abs_path = \&fast_abs_path;
415 }
ed79a026 416 elsif ($^O eq 'epoc') {
fa6a1c44 417 *cwd = \&_epoc_cwd;
418 *getcwd = \&_epoc_cwd;
ed79a026 419 *fastgetcwd = \&_epoc_cwd;
420 *fastcwd = \&_epoc_cwd;
421 *abs_path = \&fast_abs_path;
422 }
4aecb5b5 423 elsif ($^O eq 'MacOS') {
424 *getcwd = \&cwd;
425 *fastgetcwd = \&cwd;
426 *fastcwd = \&cwd;
427 *abs_path = \&fast_abs_path;
428 }
55497cff 429}
4633a7c4 430
431# package main; eval join('',<DATA>) || die $@; # quick test
432
a0d0e21e 4331;
434
4633a7c4 435__END__
436BEGIN { import Cwd qw(:DEFAULT chdir); }
437print join("\n", cwd, getcwd, fastcwd, "");
438chdir('..');
439print join("\n", cwd, getcwd, fastcwd, "");
440print "$ENV{PWD}\n";