blob: 289e091899cb0a349100bd40b247e33b0cdbda27 [file] [log] [blame]
Rashed Abdel-Tawab4db47f42019-09-06 10:38:22 -07001package Cwd;
2use strict;
3use Exporter;
4use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
5
6$VERSION = '3.67';
7my $xs_version = $VERSION;
8$VERSION =~ tr/_//d;
9
10@ISA = qw/ Exporter /;
11@EXPORT = qw(cwd getcwd fastcwd fastgetcwd);
12push @EXPORT, qw(getdcwd) if $^O eq 'MSWin32';
13@EXPORT_OK = qw(chdir abs_path fast_abs_path realpath fast_realpath);
14
15# sys_cwd may keep the builtin command
16
17# All the functionality of this module may provided by builtins,
18# there is no sense to process the rest of the file.
19# The best choice may be to have this in BEGIN, but how to return from BEGIN?
20
21if ($^O eq 'os2') {
22 local $^W = 0;
23
24 *cwd = defined &sys_cwd ? \&sys_cwd : \&_os2_cwd;
25 *getcwd = \&cwd;
26 *fastgetcwd = \&cwd;
27 *fastcwd = \&cwd;
28
29 *fast_abs_path = \&sys_abspath if defined &sys_abspath;
30 *abs_path = \&fast_abs_path;
31 *realpath = \&fast_abs_path;
32 *fast_realpath = \&fast_abs_path;
33
34 return 1;
35}
36
37# Need to look up the feature settings on VMS. The preferred way is to use the
38# VMS::Feature module, but that may not be available to dual life modules.
39
40my $use_vms_feature;
41BEGIN {
42 if ($^O eq 'VMS') {
43 if (eval { local $SIG{__DIE__};
44 local @INC = @INC;
45 pop @INC if $INC[-1] eq '.';
46 require VMS::Feature; }) {
47 $use_vms_feature = 1;
48 }
49 }
50}
51
52# Need to look up the UNIX report mode. This may become a dynamic mode
53# in the future.
54sub _vms_unix_rpt {
55 my $unix_rpt;
56 if ($use_vms_feature) {
57 $unix_rpt = VMS::Feature::current("filename_unix_report");
58 } else {
59 my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
60 $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
61 }
62 return $unix_rpt;
63}
64
65# Need to look up the EFS character set mode. This may become a dynamic
66# mode in the future.
67sub _vms_efs {
68 my $efs;
69 if ($use_vms_feature) {
70 $efs = VMS::Feature::current("efs_charset");
71 } else {
72 my $env_efs = $ENV{'DECC$EFS_CHARSET'} || '';
73 $efs = $env_efs =~ /^[ET1]/i;
74 }
75 return $efs;
76}
77
78# If loading the XS stuff doesn't work, we can fall back to pure perl
79if(! defined &getcwd && defined &DynaLoader::boot_DynaLoader) {
80 eval {#eval is questionable since we are handling potential errors like
81 #"Cwd object version 3.48 does not match bootstrap parameter 3.50
82 #at lib/DynaLoader.pm line 216." by having this eval
83 if ( $] >= 5.006 ) {
84 require XSLoader;
85 XSLoader::load( __PACKAGE__, $xs_version);
86 } else {
87 require DynaLoader;
88 push @ISA, 'DynaLoader';
89 __PACKAGE__->bootstrap( $xs_version );
90 }
91 };
92}
93
94# Big nasty table of function aliases
95my %METHOD_MAP =
96 (
97 VMS =>
98 {
99 cwd => '_vms_cwd',
100 getcwd => '_vms_cwd',
101 fastcwd => '_vms_cwd',
102 fastgetcwd => '_vms_cwd',
103 abs_path => '_vms_abs_path',
104 fast_abs_path => '_vms_abs_path',
105 },
106
107 MSWin32 =>
108 {
109 # We assume that &_NT_cwd is defined as an XSUB or in the core.
110 cwd => '_NT_cwd',
111 getcwd => '_NT_cwd',
112 fastcwd => '_NT_cwd',
113 fastgetcwd => '_NT_cwd',
114 abs_path => 'fast_abs_path',
115 realpath => 'fast_abs_path',
116 },
117
118 dos =>
119 {
120 cwd => '_dos_cwd',
121 getcwd => '_dos_cwd',
122 fastgetcwd => '_dos_cwd',
123 fastcwd => '_dos_cwd',
124 abs_path => 'fast_abs_path',
125 },
126
127 # QNX4. QNX6 has a $os of 'nto'.
128 qnx =>
129 {
130 cwd => '_qnx_cwd',
131 getcwd => '_qnx_cwd',
132 fastgetcwd => '_qnx_cwd',
133 fastcwd => '_qnx_cwd',
134 abs_path => '_qnx_abs_path',
135 fast_abs_path => '_qnx_abs_path',
136 },
137
138 cygwin =>
139 {
140 getcwd => 'cwd',
141 fastgetcwd => 'cwd',
142 fastcwd => 'cwd',
143 abs_path => 'fast_abs_path',
144 realpath => 'fast_abs_path',
145 },
146
147 epoc =>
148 {
149 cwd => '_epoc_cwd',
150 getcwd => '_epoc_cwd',
151 fastgetcwd => '_epoc_cwd',
152 fastcwd => '_epoc_cwd',
153 abs_path => 'fast_abs_path',
154 },
155
156 MacOS =>
157 {
158 getcwd => 'cwd',
159 fastgetcwd => 'cwd',
160 fastcwd => 'cwd',
161 abs_path => 'fast_abs_path',
162 },
163
164 amigaos =>
165 {
166 getcwd => '_backtick_pwd',
167 fastgetcwd => '_backtick_pwd',
168 fastcwd => '_backtick_pwd',
169 abs_path => 'fast_abs_path',
170 }
171 );
172
173$METHOD_MAP{NT} = $METHOD_MAP{MSWin32};
174
175# Find the pwd command in the expected locations. We assume these
176# are safe. This prevents _backtick_pwd() consulting $ENV{PATH}
177# so everything works under taint mode.
178my $pwd_cmd;
179if($^O ne 'MSWin32') {
180 foreach my $try ('/bin/pwd',
181 '/usr/bin/pwd',
182 '/QOpenSys/bin/pwd', # OS/400 PASE.
183 ) {
184 if( -x $try ) {
185 $pwd_cmd = $try;
186 last;
187 }
188 }
189}
190
191# Android has a built-in pwd. Using $pwd_cmd will DTRT if
192# this perl was compiled with -Dd_useshellcmds, which is the
193# default for Android, but the block below is needed for the
194# miniperl running on the host when cross-compiling, and
195# potentially for native builds with -Ud_useshellcmds.
196if ($^O =~ /android/) {
197 # If targetsh is executable, then we're either a full
198 # perl, or a miniperl for a native build.
199 if (-x $Config::Config{targetsh}) {
200 $pwd_cmd = "$Config::Config{targetsh} -c pwd"
201 }
202 else {
203 my $sh = $Config::Config{sh} || (-x '/system/bin/sh' ? '/system/bin/sh' : 'sh');
204 $pwd_cmd = "$sh -c pwd"
205 }
206}
207
208my $found_pwd_cmd = defined($pwd_cmd);
209unless ($pwd_cmd) {
210 # Isn't this wrong? _backtick_pwd() will fail if someone has
211 # pwd in their path but it is not /bin/pwd or /usr/bin/pwd?
212 # See [perl #16774]. --jhi
213 $pwd_cmd = 'pwd';
214}
215
216# Lazy-load Carp
217sub _carp { require Carp; Carp::carp(@_) }
218sub _croak { require Carp; Carp::croak(@_) }
219
220# The 'natural and safe form' for UNIX (pwd may be setuid root)
221sub _backtick_pwd {
222
223 # Localize %ENV entries in a way that won't create new hash keys.
224 # Under AmigaOS we don't want to localize as it stops perl from
225 # finding 'sh' in the PATH.
226 my @localize = grep exists $ENV{$_}, qw(PATH IFS CDPATH ENV BASH_ENV) if $^O ne "amigaos";
227 local @ENV{@localize} if @localize;
228
229 my $cwd = `$pwd_cmd`;
230 # Belt-and-suspenders in case someone said "undef $/".
231 local $/ = "\n";
232 # `pwd` may fail e.g. if the disk is full
233 chomp($cwd) if defined $cwd;
234 $cwd;
235}
236
237# Since some ports may predefine cwd internally (e.g., NT)
238# we take care not to override an existing definition for cwd().
239
240unless ($METHOD_MAP{$^O}{cwd} or defined &cwd) {
241 # The pwd command is not available in some chroot(2)'ed environments
242 my $sep = $Config::Config{path_sep} || ':';
243 my $os = $^O; # Protect $^O from tainting
244
245 # Try again to find a pwd, this time searching the whole PATH.
246 if (defined $ENV{PATH} and $os ne 'MSWin32') { # no pwd on Windows
247 my @candidates = split($sep, $ENV{PATH});
248 while (!$found_pwd_cmd and @candidates) {
249 my $candidate = shift @candidates;
250 $found_pwd_cmd = 1 if -x "$candidate/pwd";
251 }
252 }
253
254 # MacOS has some special magic to make `pwd` work.
255 if( $os eq 'MacOS' || $found_pwd_cmd )
256 {
257 *cwd = \&_backtick_pwd;
258 }
259 else {
260 *cwd = \&getcwd;
261 }
262}
263
264if ($^O eq 'cygwin') {
265 # We need to make sure cwd() is called with no args, because it's
266 # got an arg-less prototype and will die if args are present.
267 local $^W = 0;
268 my $orig_cwd = \&cwd;
269 *cwd = sub { &$orig_cwd() }
270}
271
272# set a reasonable (and very safe) default for fastgetcwd, in case it
273# isn't redefined later (20001212 rspier)
274*fastgetcwd = \&cwd;
275
276# A non-XS version of getcwd() - also used to bootstrap the perl build
277# process, when miniperl is running and no XS loading happens.
278sub _perl_getcwd
279{
280 abs_path('.');
281}
282
283# By John Bazik
284#
285# Usage: $cwd = &fastcwd;
286#
287# This is a faster version of getcwd. It's also more dangerous because
288# you might chdir out of a directory that you can't chdir back into.
289
290sub fastcwd_ {
291 my($odev, $oino, $cdev, $cino, $tdev, $tino);
292 my(@path, $path);
293 local(*DIR);
294
295 my($orig_cdev, $orig_cino) = stat('.');
296 ($cdev, $cino) = ($orig_cdev, $orig_cino);
297 for (;;) {
298 my $direntry;
299 ($odev, $oino) = ($cdev, $cino);
300 CORE::chdir('..') || return undef;
301 ($cdev, $cino) = stat('.');
302 last if $odev == $cdev && $oino == $cino;
303 opendir(DIR, '.') || return undef;
304 for (;;) {
305 $direntry = readdir(DIR);
306 last unless defined $direntry;
307 next if $direntry eq '.';
308 next if $direntry eq '..';
309
310 ($tdev, $tino) = lstat($direntry);
311 last unless $tdev != $odev || $tino != $oino;
312 }
313 closedir(DIR);
314 return undef unless defined $direntry; # should never happen
315 unshift(@path, $direntry);
316 }
317 $path = '/' . join('/', @path);
318 if ($^O eq 'apollo') { $path = "/".$path; }
319 # At this point $path may be tainted (if tainting) and chdir would fail.
320 # Untaint it then check that we landed where we started.
321 $path =~ /^(.*)\z/s # untaint
322 && CORE::chdir($1) or return undef;
323 ($cdev, $cino) = stat('.');
324 die "Unstable directory path, current directory changed unexpectedly"
325 if $cdev != $orig_cdev || $cino != $orig_cino;
326 $path;
327}
328if (not defined &fastcwd) { *fastcwd = \&fastcwd_ }
329
330# Keeps track of current working directory in PWD environment var
331# Usage:
332# use Cwd 'chdir';
333# chdir $newdir;
334
335my $chdir_init = 0;
336
337sub chdir_init {
338 if ($ENV{'PWD'} and $^O ne 'os2' and $^O ne 'dos' and $^O ne 'MSWin32') {
339 my($dd,$di) = stat('.');
340 my($pd,$pi) = stat($ENV{'PWD'});
341 if (!defined $dd or !defined $pd or $di != $pi or $dd != $pd) {
342 $ENV{'PWD'} = cwd();
343 }
344 }
345 else {
346 my $wd = cwd();
347 $wd = Win32::GetFullPathName($wd) if $^O eq 'MSWin32';
348 $ENV{'PWD'} = $wd;
349 }
350 # Strip an automounter prefix (where /tmp_mnt/foo/bar == /foo/bar)
351 if ($^O ne 'MSWin32' and $ENV{'PWD'} =~ m|(/[^/]+(/[^/]+/[^/]+))(.*)|s) {
352 my($pd,$pi) = stat($2);
353 my($dd,$di) = stat($1);
354 if (defined $pd and defined $dd and $di == $pi and $dd == $pd) {
355 $ENV{'PWD'}="$2$3";
356 }
357 }
358 $chdir_init = 1;
359}
360
361sub chdir {
362 my $newdir = @_ ? shift : ''; # allow for no arg (chdir to HOME dir)
363 if ($^O eq "cygwin") {
364 $newdir =~ s|\A///+|//|;
365 $newdir =~ s|(?<=[^/])//+|/|g;
366 }
367 elsif ($^O ne 'MSWin32') {
368 $newdir =~ s|///*|/|g;
369 }
370 chdir_init() unless $chdir_init;
371 my $newpwd;
372 if ($^O eq 'MSWin32') {
373 # get the full path name *before* the chdir()
374 $newpwd = Win32::GetFullPathName($newdir);
375 }
376
377 return 0 unless CORE::chdir $newdir;
378
379 if ($^O eq 'VMS') {
380 return $ENV{'PWD'} = $ENV{'DEFAULT'}
381 }
382 elsif ($^O eq 'MacOS') {
383 return $ENV{'PWD'} = cwd();
384 }
385 elsif ($^O eq 'MSWin32') {
386 $ENV{'PWD'} = $newpwd;
387 return 1;
388 }
389
390 if (ref $newdir eq 'GLOB') { # in case a file/dir handle is passed in
391 $ENV{'PWD'} = cwd();
392 } elsif ($newdir =~ m#^/#s) {
393 $ENV{'PWD'} = $newdir;
394 } else {
395 my @curdir = split(m#/#,$ENV{'PWD'});
396 @curdir = ('') unless @curdir;
397 my $component;
398 foreach $component (split(m#/#, $newdir)) {
399 next if $component eq '.';
400 pop(@curdir),next if $component eq '..';
401 push(@curdir,$component);
402 }
403 $ENV{'PWD'} = join('/',@curdir) || '/';
404 }
405 1;
406}
407
408sub _perl_abs_path
409{
410 my $start = @_ ? shift : '.';
411 my($dotdots, $cwd, @pst, @cst, $dir, @tst);
412
413 unless (@cst = stat( $start ))
414 {
415 _carp("stat($start): $!");
416 return '';
417 }
418
419 unless (-d _) {
420 # Make sure we can be invoked on plain files, not just directories.
421 # NOTE that this routine assumes that '/' is the only directory separator.
422
423 my ($dir, $file) = $start =~ m{^(.*)/(.+)$}
424 or return cwd() . '/' . $start;
425
426 # Can't use "-l _" here, because the previous stat was a stat(), not an lstat().
427 if (-l $start) {
428 my $link_target = readlink($start);
429 die "Can't resolve link $start: $!" unless defined $link_target;
430
431 require File::Spec;
432 $link_target = $dir . '/' . $link_target
433 unless File::Spec->file_name_is_absolute($link_target);
434
435 return abs_path($link_target);
436 }
437
438 return $dir ? abs_path($dir) . "/$file" : "/$file";
439 }
440
441 $cwd = '';
442 $dotdots = $start;
443 do
444 {
445 $dotdots .= '/..';
446 @pst = @cst;
447 local *PARENT;
448 unless (opendir(PARENT, $dotdots))
449 {
450 # probably a permissions issue. Try the native command.
451 require File::Spec;
452 return File::Spec->rel2abs( $start, _backtick_pwd() );
453 }
454 unless (@cst = stat($dotdots))
455 {
456 _carp("stat($dotdots): $!");
457 closedir(PARENT);
458 return '';
459 }
460 if ($pst[0] == $cst[0] && $pst[1] == $cst[1])
461 {
462 $dir = undef;
463 }
464 else
465 {
466 do
467 {
468 unless (defined ($dir = readdir(PARENT)))
469 {
470 _carp("readdir($dotdots): $!");
471 closedir(PARENT);
472 return '';
473 }
474 $tst[0] = $pst[0]+1 unless (@tst = lstat("$dotdots/$dir"))
475 }
476 while ($dir eq '.' || $dir eq '..' || $tst[0] != $pst[0] ||
477 $tst[1] != $pst[1]);
478 }
479 $cwd = (defined $dir ? "$dir" : "" ) . "/$cwd" ;
480 closedir(PARENT);
481 } while (defined $dir);
482 chop($cwd) unless $cwd eq '/'; # drop the trailing /
483 $cwd;
484}
485
486my $Curdir;
487sub fast_abs_path {
488 local $ENV{PWD} = $ENV{PWD} || ''; # Guard against clobberage
489 my $cwd = getcwd();
490 require File::Spec;
491 my $path = @_ ? shift : ($Curdir ||= File::Spec->curdir);
492
493 # Detaint else we'll explode in taint mode. This is safe because
494 # we're not doing anything dangerous with it.
495 ($path) = $path =~ /(.*)/s;
496 ($cwd) = $cwd =~ /(.*)/s;
497
498 unless (-e $path) {
499 _croak("$path: No such file or directory");
500 }
501
502 unless (-d _) {
503 # Make sure we can be invoked on plain files, not just directories.
504
505 my ($vol, $dir, $file) = File::Spec->splitpath($path);
506 return File::Spec->catfile($cwd, $path) unless length $dir;
507
508 if (-l $path) {
509 my $link_target = readlink($path);
510 die "Can't resolve link $path: $!" unless defined $link_target;
511
512 $link_target = File::Spec->catpath($vol, $dir, $link_target)
513 unless File::Spec->file_name_is_absolute($link_target);
514
515 return fast_abs_path($link_target);
516 }
517
518 return $dir eq File::Spec->rootdir
519 ? File::Spec->catpath($vol, $dir, $file)
520 : fast_abs_path(File::Spec->catpath($vol, $dir, '')) . '/' . $file;
521 }
522
523 if (!CORE::chdir($path)) {
524 _croak("Cannot chdir to $path: $!");
525 }
526 my $realpath = getcwd();
527 if (! ((-d $cwd) && (CORE::chdir($cwd)))) {
528 _croak("Cannot chdir back to $cwd: $!");
529 }
530 $realpath;
531}
532
533# added function alias to follow principle of least surprise
534# based on previous aliasing. --tchrist 27-Jan-00
535*fast_realpath = \&fast_abs_path;
536
537# --- PORTING SECTION ---
538
539# VMS: $ENV{'DEFAULT'} points to default directory at all times
540# 06-Mar-1996 Charles Bailey bailey@newman.upenn.edu
541# Note: Use of Cwd::chdir() causes the logical name PWD to be defined
542# in the process logical name table as the default device and directory
543# seen by Perl. This may not be the same as the default device
544# and directory seen by DCL after Perl exits, since the effects
545# the CRTL chdir() function persist only until Perl exits.
546
547sub _vms_cwd {
548 return $ENV{'DEFAULT'};
549}
550
551sub _vms_abs_path {
552 return $ENV{'DEFAULT'} unless @_;
553 my $path = shift;
554
555 my $efs = _vms_efs;
556 my $unix_rpt = _vms_unix_rpt;
557
558 if (defined &VMS::Filespec::vmsrealpath) {
559 my $path_unix = 0;
560 my $path_vms = 0;
561
562 $path_unix = 1 if ($path =~ m#(?<=\^)/#);
563 $path_unix = 1 if ($path =~ /^\.\.?$/);
564 $path_vms = 1 if ($path =~ m#[\[<\]]#);
565 $path_vms = 1 if ($path =~ /^--?$/);
566
567 my $unix_mode = $path_unix;
568 if ($efs) {
569 # In case of a tie, the Unix report mode decides.
570 if ($path_vms == $path_unix) {
571 $unix_mode = $unix_rpt;
572 } else {
573 $unix_mode = 0 if $path_vms;
574 }
575 }
576
577 if ($unix_mode) {
578 # Unix format
579 return VMS::Filespec::unixrealpath($path);
580 }
581
582 # VMS format
583
584 my $new_path = VMS::Filespec::vmsrealpath($path);
585
586 # Perl expects directories to be in directory format
587 $new_path = VMS::Filespec::pathify($new_path) if -d $path;
588 return $new_path;
589 }
590
591 # Fallback to older algorithm if correct ones are not
592 # available.
593
594 if (-l $path) {
595 my $link_target = readlink($path);
596 die "Can't resolve link $path: $!" unless defined $link_target;
597
598 return _vms_abs_path($link_target);
599 }
600
601 # may need to turn foo.dir into [.foo]
602 my $pathified = VMS::Filespec::pathify($path);
603 $path = $pathified if defined $pathified;
604
605 return VMS::Filespec::rmsexpand($path);
606}
607
608sub _os2_cwd {
609 my $pwd = `cmd /c cd`;
610 chomp $pwd;
611 $pwd =~ s:\\:/:g ;
612 $ENV{'PWD'} = $pwd;
613 return $pwd;
614}
615
616sub _win32_cwd_simple {
617 my $pwd = `cd`;
618 chomp $pwd;
619 $pwd =~ s:\\:/:g ;
620 $ENV{'PWD'} = $pwd;
621 return $pwd;
622}
623
624sub _win32_cwd {
625 my $pwd;
626 $pwd = Win32::GetCwd();
627 $pwd =~ s:\\:/:g ;
628 $ENV{'PWD'} = $pwd;
629 return $pwd;
630}
631
632*_NT_cwd = defined &Win32::GetCwd ? \&_win32_cwd : \&_win32_cwd_simple;
633
634sub _dos_cwd {
635 my $pwd;
636 if (!defined &Dos::GetCwd) {
637 chomp($pwd = `command /c cd`);
638 $pwd =~ s:\\:/:g ;
639 } else {
640 $pwd = Dos::GetCwd();
641 }
642 $ENV{'PWD'} = $pwd;
643 return $pwd;
644}
645
646sub _qnx_cwd {
647 local $ENV{PATH} = '';
648 local $ENV{CDPATH} = '';
649 local $ENV{ENV} = '';
650 my $pwd = `/usr/bin/fullpath -t`;
651 chomp $pwd;
652 $ENV{'PWD'} = $pwd;
653 return $pwd;
654}
655
656sub _qnx_abs_path {
657 local $ENV{PATH} = '';
658 local $ENV{CDPATH} = '';
659 local $ENV{ENV} = '';
660 my $path = @_ ? shift : '.';
661 local *REALPATH;
662
663 defined( open(REALPATH, '-|') || exec '/usr/bin/fullpath', '-t', $path ) or
664 die "Can't open /usr/bin/fullpath: $!";
665 my $realpath = <REALPATH>;
666 close REALPATH;
667 chomp $realpath;
668 return $realpath;
669}
670
671sub _epoc_cwd {
672 return $ENV{'PWD'} = EPOC::getcwd();
673}
674
675# Now that all the base-level functions are set up, alias the
676# user-level functions to the right places
677
678if (exists $METHOD_MAP{$^O}) {
679 my $map = $METHOD_MAP{$^O};
680 foreach my $name (keys %$map) {
681 local $^W = 0; # assignments trigger 'subroutine redefined' warning
682 no strict 'refs';
683 *{$name} = \&{$map->{$name}};
684 }
685}
686
687# In case the XS version doesn't load.
688*abs_path = \&_perl_abs_path unless defined &abs_path;
689*getcwd = \&_perl_getcwd unless defined &getcwd;
690
691# added function alias for those of us more
692# used to the libc function. --tchrist 27-Jan-00
693*realpath = \&abs_path;
694
6951;
696__END__
697