24ad1e2969e25e6288f670f34cfc221dac42f49d
[p5sagit/p5-mst-13.2.git] / lib / Cwd.pm
1 package Cwd;
2 use 5.006;
3
4 =head1 NAME
5
6 Cwd - get pathname of current working directory
7
8 =head1 SYNOPSIS
9
10     use Cwd;
11     my $dir = getcwd;
12
13     use Cwd 'abs_path';
14     my $abs_path = abs_path($file);
15
16 =head1 DESCRIPTION
17
18 This module provides functions for determining the pathname of the
19 current working directory.  It is recommended that getcwd (or another
20 *cwd() function) be used in I<all> code to ensure portability.
21
22 By default, it exports the functions cwd(), getcwd(), fastcwd(), and
23 fastgetcwd() into the caller's namespace.  
24
25
26 =head2 getcwd and friends
27
28 Each of these functions are called without arguments and return the
29 absolute path of the current working directory.
30
31 =over 4
32
33 =item getcwd
34
35     my $cwd = getcwd();
36
37 Returns the current working directory.
38
39 Re-implements the getcwd(3) (or getwd(3)) functions in Perl.
40
41 Taint-safe.
42
43 =item cwd
44
45     my $cwd = cwd();
46
47 The cwd() is the most natural form for the current architecture. For
48 most systems it is identical to `pwd` (but without the trailing line
49 terminator).
50
51 Taint-safe.
52
53 =item fastcwd
54
55     my $cwd = fastcwd();
56
57 A more dangerous version of getcwd(), but potentially faster.
58
59 It might conceivably chdir() you out of a directory that it can't
60 chdir() you back into.  If fastcwd encounters a problem it will return
61 undef but will probably leave you in a different directory.  For a
62 measure of extra security, if everything appears to have worked, the
63 fastcwd() function will check that it leaves you in the same directory
64 that it started in. If it has changed it will C<die> with the message
65 "Unstable directory path, current directory changed
66 unexpectedly". That should never happen.
67
68 =item fastgetcwd
69
70   my $cwd = fastgetcwd();
71
72 The fastgetcwd() function is provided as a synonym for cwd().
73
74 =back
75
76
77 =head2 abs_path and friends
78
79 These functions are exported only on request.  They each take a single
80 argument and return the absolute pathname for it.
81
82 =over 4
83
84 =item abs_path
85
86   my $abs_path = abs_path($file);
87
88 Uses the same algorithm as getcwd().  Symbolic links and relative-path
89 components ("." and "..") are resolved to return the canonical
90 pathname, just like realpath(3).
91
92 Taint-safe.
93
94 =item realpath
95
96   my $abs_path = realpath($file);
97
98 A synonym for abs_path().
99
100 Taint-safe.
101
102 =item fast_abs_path
103
104   my $abs_path = fast_abs_path($file);
105
106 A more dangerous, but potentially faster version of abs_path.
107
108 This function is B<Not> taint-safe : you can't use it in programs
109 that work under taint mode.
110
111 =back
112
113 =head2 $ENV{PWD}
114
115 If you ask to override your chdir() built-in function, 
116
117   use Cwd qw(chdir);
118
119 then your PWD environment variable will be kept up to date.  Note that
120 it will only be kept up to date if all packages which use chdir import
121 it from Cwd.
122
123
124 =head1 NOTES
125
126 =over 4
127
128 =item *
129
130 Since the path seperators are different on some operating systems ('/'
131 on Unix, ':' on MacPerl, etc...) we recommend you use the File::Spec
132 modules wherever portability is a concern.
133
134 =item *
135
136 Actually, on Mac OS, the C<getcwd()>, C<fastgetcwd()> and C<fastcwd()>
137 functions  are all aliases for the C<cwd()> function, which, on Mac OS,
138 calls `pwd`. Likewise, the C<abs_path()> function is an alias for
139 C<fast_abs_path()>.
140
141 =back
142
143 =head1 SEE ALSO
144
145 L<File::chdir>
146
147 =cut
148
149 use strict;
150
151 use Carp;
152
153 our $VERSION = '2.06';
154
155 use base qw/ Exporter /;
156 our @EXPORT = qw(cwd getcwd fastcwd fastgetcwd);
157 our @EXPORT_OK = qw(chdir abs_path fast_abs_path realpath fast_realpath);
158
159 # sys_cwd may keep the builtin command
160
161 # All the functionality of this module may provided by builtins,
162 # there is no sense to process the rest of the file.
163 # The best choice may be to have this in BEGIN, but how to return from BEGIN?
164
165 if ($^O eq 'os2' && defined &sys_cwd && defined &sys_abspath) {
166     local $^W = 0;
167     *cwd                = \&sys_cwd;
168     *getcwd             = \&cwd;
169     *fastgetcwd         = \&cwd;
170     *fastcwd            = \&cwd;
171     *abs_path           = \&sys_abspath;
172     *fast_abs_path      = \&abs_path;
173     *realpath           = \&abs_path;
174     *fast_realpath      = \&abs_path;
175     return 1;
176 }
177
178 eval {
179     require XSLoader;
180     undef *Cwd::fastcwd; # avoid redefinition warning
181     XSLoader::load('Cwd');
182 };
183
184
185 # Find the pwd command in the expected locations.  We assume these
186 # are safe.  This prevents _backtick_pwd() consulting $ENV{PATH}
187 # so everything works under taint mode.
188 my $pwd_cmd;
189 foreach my $try (qw(/bin/pwd /usr/bin/pwd)) {
190     if( -x $try ) {
191         $pwd_cmd = $try;
192         last;
193     }
194 }
195 $pwd_cmd ||= 'pwd';
196
197 # The 'natural and safe form' for UNIX (pwd may be setuid root)
198 sub _backtick_pwd {
199     local $ENV{PATH};
200     local $ENV{IFS};
201     local $ENV{CDPATH};
202     local $ENV{ENV};
203     local $ENV{BASH_ENV};
204     my $cwd = `$pwd_cmd`;
205     # Belt-and-suspenders in case someone said "undef $/".
206     local $/ = "\n";
207     # `pwd` may fail e.g. if the disk is full
208     chomp($cwd) if defined $cwd;
209     $cwd;
210 }
211
212 # Since some ports may predefine cwd internally (e.g., NT)
213 # we take care not to override an existing definition for cwd().
214
215 unless(defined &cwd) {
216     # The pwd command is not available in some chroot(2)'ed environments
217     if( $^O eq 'MacOS' || (defined $ENV{PATH} && 
218                            grep { -x "$_/pwd" } split(':', $ENV{PATH})) ) 
219     {
220         *cwd = \&_backtick_pwd;
221     }
222     else {
223         *cwd = \&getcwd;
224     }
225 }
226
227 # set a reasonable (and very safe) default for fastgetcwd, in case it
228 # isn't redefined later (20001212 rspier)
229 *fastgetcwd = \&cwd;
230
231 # By Brandon S. Allbery
232 #
233 # Usage: $cwd = getcwd();
234
235 sub getcwd
236 {
237     abs_path('.');
238 }
239
240
241 # By John Bazik
242 #
243 # Usage: $cwd = &fastcwd;
244 #
245 # This is a faster version of getcwd.  It's also more dangerous because
246 # you might chdir out of a directory that you can't chdir back into.
247     
248 sub fastcwd {
249     my($odev, $oino, $cdev, $cino, $tdev, $tino);
250     my(@path, $path);
251     local(*DIR);
252
253     my($orig_cdev, $orig_cino) = stat('.');
254     ($cdev, $cino) = ($orig_cdev, $orig_cino);
255     for (;;) {
256         my $direntry;
257         ($odev, $oino) = ($cdev, $cino);
258         CORE::chdir('..') || return undef;
259         ($cdev, $cino) = stat('.');
260         last if $odev == $cdev && $oino == $cino;
261         opendir(DIR, '.') || return undef;
262         for (;;) {
263             $direntry = readdir(DIR);
264             last unless defined $direntry;
265             next if $direntry eq '.';
266             next if $direntry eq '..';
267
268             ($tdev, $tino) = lstat($direntry);
269             last unless $tdev != $odev || $tino != $oino;
270         }
271         closedir(DIR);
272         return undef unless defined $direntry; # should never happen
273         unshift(@path, $direntry);
274     }
275     $path = '/' . join('/', @path);
276     if ($^O eq 'apollo') { $path = "/".$path; }
277     # At this point $path may be tainted (if tainting) and chdir would fail.
278     # Untaint it then check that we landed where we started.
279     $path =~ /^(.*)\z/s         # untaint
280         && CORE::chdir($1) or return undef;
281     ($cdev, $cino) = stat('.');
282     die "Unstable directory path, current directory changed unexpectedly"
283         if $cdev != $orig_cdev || $cino != $orig_cino;
284     $path;
285 }
286
287
288 # Keeps track of current working directory in PWD environment var
289 # Usage:
290 #       use Cwd 'chdir';
291 #       chdir $newdir;
292
293 my $chdir_init = 0;
294
295 sub chdir_init {
296     if ($ENV{'PWD'} and $^O ne 'os2' and $^O ne 'dos' and $^O ne 'MSWin32') {
297         my($dd,$di) = stat('.');
298         my($pd,$pi) = stat($ENV{'PWD'});
299         if (!defined $dd or !defined $pd or $di != $pi or $dd != $pd) {
300             $ENV{'PWD'} = cwd();
301         }
302     }
303     else {
304         my $wd = cwd();
305         $wd = Win32::GetFullPathName($wd) if $^O eq 'MSWin32';
306         $ENV{'PWD'} = $wd;
307     }
308     # Strip an automounter prefix (where /tmp_mnt/foo/bar == /foo/bar)
309     if ($^O ne 'MSWin32' and $ENV{'PWD'} =~ m|(/[^/]+(/[^/]+/[^/]+))(.*)|s) {
310         my($pd,$pi) = stat($2);
311         my($dd,$di) = stat($1);
312         if (defined $pd and defined $dd and $di == $pi and $dd == $pd) {
313             $ENV{'PWD'}="$2$3";
314         }
315     }
316     $chdir_init = 1;
317 }
318
319 sub chdir {
320     my $newdir = @_ ? shift : '';       # allow for no arg (chdir to HOME dir)
321     $newdir =~ s|///*|/|g unless $^O eq 'MSWin32';
322     chdir_init() unless $chdir_init;
323     my $newpwd;
324     if ($^O eq 'MSWin32') {
325         # get the full path name *before* the chdir()
326         $newpwd = Win32::GetFullPathName($newdir);
327     }
328
329     return 0 unless CORE::chdir $newdir;
330
331     if ($^O eq 'VMS') {
332         return $ENV{'PWD'} = $ENV{'DEFAULT'}
333     }
334     elsif ($^O eq 'MacOS') {
335         return $ENV{'PWD'} = cwd();
336     }
337     elsif ($^O eq 'MSWin32') {
338         $ENV{'PWD'} = $newpwd;
339         return 1;
340     }
341
342     if ($newdir =~ m#^/#s) {
343         $ENV{'PWD'} = $newdir;
344     } else {
345         my @curdir = split(m#/#,$ENV{'PWD'});
346         @curdir = ('') unless @curdir;
347         my $component;
348         foreach $component (split(m#/#, $newdir)) {
349             next if $component eq '.';
350             pop(@curdir),next if $component eq '..';
351             push(@curdir,$component);
352         }
353         $ENV{'PWD'} = join('/',@curdir) || '/';
354     }
355     1;
356 }
357
358
359 # In case the XS version doesn't load.
360 *abs_path = \&_perl_abs_path unless defined &abs_path;
361 sub _perl_abs_path
362 {
363     my $start = @_ ? shift : '.';
364     my($dotdots, $cwd, @pst, @cst, $dir, @tst);
365
366     unless (@cst = stat( $start ))
367     {
368         carp "stat($start): $!";
369         return '';
370     }
371     $cwd = '';
372     $dotdots = $start;
373     do
374     {
375         $dotdots .= '/..';
376         @pst = @cst;
377         unless (opendir(PARENT, $dotdots))
378         {
379             carp "opendir($dotdots): $!";
380             return '';
381         }
382         unless (@cst = stat($dotdots))
383         {
384             carp "stat($dotdots): $!";
385             closedir(PARENT);
386             return '';
387         }
388         if ($pst[0] == $cst[0] && $pst[1] == $cst[1])
389         {
390             $dir = undef;
391         }
392         else
393         {
394             do
395             {
396                 unless (defined ($dir = readdir(PARENT)))
397                 {
398                     carp "readdir($dotdots): $!";
399                     closedir(PARENT);
400                     return '';
401                 }
402                 $tst[0] = $pst[0]+1 unless (@tst = lstat("$dotdots/$dir"))
403             }
404             while ($dir eq '.' || $dir eq '..' || $tst[0] != $pst[0] ||
405                    $tst[1] != $pst[1]);
406         }
407         $cwd = (defined $dir ? "$dir" : "" ) . "/$cwd" ;
408         closedir(PARENT);
409     } while (defined $dir);
410     chop($cwd) unless $cwd eq '/'; # drop the trailing /
411     $cwd;
412 }
413
414
415 # added function alias for those of us more
416 # used to the libc function.  --tchrist 27-Jan-00
417 *realpath = \&abs_path;
418
419 sub fast_abs_path {
420     my $cwd = getcwd();
421     require File::Spec;
422     my $path = @_ ? shift : File::Spec->curdir;
423     CORE::chdir($path) || croak "Cannot chdir to $path: $!";
424     my $realpath = getcwd();
425     -d $cwd && CORE::chdir($cwd) ||
426         croak "Cannot chdir back to $cwd: $!";
427     $realpath;
428 }
429
430 # added function alias to follow principle of least surprise
431 # based on previous aliasing.  --tchrist 27-Jan-00
432 *fast_realpath = \&fast_abs_path;
433
434
435 # --- PORTING SECTION ---
436
437 # VMS: $ENV{'DEFAULT'} points to default directory at all times
438 # 06-Mar-1996  Charles Bailey  bailey@newman.upenn.edu
439 # Note: Use of Cwd::chdir() causes the logical name PWD to be defined
440 #   in the process logical name table as the default device and directory
441 #   seen by Perl. This may not be the same as the default device
442 #   and directory seen by DCL after Perl exits, since the effects
443 #   the CRTL chdir() function persist only until Perl exits.
444
445 sub _vms_cwd {
446     return $ENV{'DEFAULT'};
447 }
448
449 sub _vms_abs_path {
450     return $ENV{'DEFAULT'} unless @_;
451     my $path = VMS::Filespec::pathify($_[0]);
452     croak("Invalid path name $_[0]") unless defined $path;
453     return VMS::Filespec::rmsexpand($path);
454 }
455
456 sub _os2_cwd {
457     $ENV{'PWD'} = `cmd /c cd`;
458     chop $ENV{'PWD'};
459     $ENV{'PWD'} =~ s:\\:/:g ;
460     return $ENV{'PWD'};
461 }
462
463 sub _win32_cwd {
464     $ENV{'PWD'} = Win32::GetCwd();
465     $ENV{'PWD'} =~ s:\\:/:g ;
466     return $ENV{'PWD'};
467 }
468
469 *_NT_cwd = \&_win32_cwd if (!defined &_NT_cwd && 
470                             defined &Win32::GetCwd);
471
472 *_NT_cwd = \&_os2_cwd unless defined &_NT_cwd;
473
474 sub _dos_cwd {
475     if (!defined &Dos::GetCwd) {
476         $ENV{'PWD'} = `command /c cd`;
477         chop $ENV{'PWD'};
478         $ENV{'PWD'} =~ s:\\:/:g ;
479     } else {
480         $ENV{'PWD'} = Dos::GetCwd();
481     }
482     return $ENV{'PWD'};
483 }
484
485 sub _qnx_cwd {
486         local $ENV{PATH} = '';
487         local $ENV{CDPATH} = '';
488         local $ENV{ENV} = '';
489     $ENV{'PWD'} = `/usr/bin/fullpath -t`;
490     chop $ENV{'PWD'};
491     return $ENV{'PWD'};
492 }
493
494 sub _qnx_abs_path {
495         local $ENV{PATH} = '';
496         local $ENV{CDPATH} = '';
497         local $ENV{ENV} = '';
498     my $path = @_ ? shift : '.';
499     my $realpath=`/usr/bin/fullpath -t $path`;
500     chop $realpath;
501     return $realpath;
502 }
503
504 sub _epoc_cwd {
505     $ENV{'PWD'} = EPOC::getcwd();
506     return $ENV{'PWD'};
507 }
508
509 {
510     no warnings;        # assignments trigger 'subroutine redefined' warning
511
512     if ($^O eq 'VMS') {
513         *cwd            = \&_vms_cwd;
514         *getcwd         = \&_vms_cwd;
515         *fastcwd        = \&_vms_cwd;
516         *fastgetcwd     = \&_vms_cwd;
517         *abs_path       = \&_vms_abs_path;
518         *fast_abs_path  = \&_vms_abs_path;
519     }
520     elsif ($^O eq 'NT' or $^O eq 'MSWin32') {
521         # We assume that &_NT_cwd is defined as an XSUB or in the core.
522         *cwd            = \&_NT_cwd;
523         *getcwd         = \&_NT_cwd;
524         *fastcwd        = \&_NT_cwd;
525         *fastgetcwd     = \&_NT_cwd;
526         *abs_path       = \&fast_abs_path;
527         *realpath   = \&fast_abs_path;
528     }
529     elsif ($^O eq 'os2') {
530         # sys_cwd may keep the builtin command
531         *cwd            = defined &sys_cwd ? \&sys_cwd : \&_os2_cwd;
532         *getcwd         = \&cwd;
533         *fastgetcwd     = \&cwd;
534         *fastcwd        = \&cwd;
535         *abs_path       = \&fast_abs_path;
536     }
537     elsif ($^O eq 'dos') {
538         *cwd            = \&_dos_cwd;
539         *getcwd         = \&_dos_cwd;
540         *fastgetcwd     = \&_dos_cwd;
541         *fastcwd        = \&_dos_cwd;
542         *abs_path       = \&fast_abs_path;
543     }
544     elsif ($^O =~ m/^(?:qnx|nto)$/ ) {
545         *cwd            = \&_qnx_cwd;
546         *getcwd         = \&_qnx_cwd;
547         *fastgetcwd     = \&_qnx_cwd;
548         *fastcwd        = \&_qnx_cwd;
549         *abs_path       = \&_qnx_abs_path;
550         *fast_abs_path  = \&_qnx_abs_path;
551     }
552     elsif ($^O eq 'cygwin') {
553         *getcwd = \&cwd;
554         *fastgetcwd     = \&cwd;
555         *fastcwd        = \&cwd;
556         *abs_path       = \&fast_abs_path;
557     }
558     elsif ($^O eq 'epoc') {
559         *cwd            = \&_epoc_cwd;
560         *getcwd         = \&_epoc_cwd;
561         *fastgetcwd     = \&_epoc_cwd;
562         *fastcwd        = \&_epoc_cwd;
563         *abs_path       = \&fast_abs_path;
564     }
565     elsif ($^O eq 'MacOS') {
566         *getcwd     = \&cwd;
567         *fastgetcwd = \&cwd;
568         *fastcwd    = \&cwd;
569         *abs_path   = \&fast_abs_path;
570     }
571 }
572
573
574 1;