source: trunk/third/perl/lib/Cwd.pm @ 10951

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