File Coverage

blib/lib/File/chdir.pm
Criterion Covered Total %
statement 90 104 86.5
branch 8 10 80.0
condition 3 5 60.0
subroutine 27 31 87.1
pod n/a
total 128 150 85.3


line stmt bran cond sub pod time code
1             package File::chdir;
2 6     6   125061 use 5.004;
  6         19  
  6         237  
3 6     6   29 use strict;
  6         9  
  6         205  
4 6     6   29 use vars qw($VERSION @ISA @EXPORT $CWD @CWD);
  6         6  
  6         614  
5             # ABSTRACT: a more sensible way to change directories
6             our $VERSION = '0.1009'; # VERSION
7              
8             require Exporter;
9             @ISA = qw(Exporter);
10             @EXPORT = qw(*CWD);
11              
12 6     6   30 use Carp;
  6         17  
  6         480  
13 6     6   29 use Cwd 3.16;
  6         140  
  6         428  
14 6     6   872 use File::Spec::Functions 3.27 qw/canonpath splitpath catpath splitdir catdir/;
  6         1263  
  6         2039  
15              
16             tie $CWD, 'File::chdir::SCALAR' or die "Can't tie \$CWD";
17             tie @CWD, 'File::chdir::ARRAY' or die "Can't tie \@CWD";
18              
19             sub _abs_path {
20             # Otherwise we'll never work under taint mode.
21 217     217   1235 my($cwd) = Cwd::getcwd =~ /(.*)/s;
22             # Run through File::Spec, since everything else uses it
23 217         813 return canonpath($cwd);
24             }
25              
26             # splitpath but also split directory
27             sub _split_cwd {
28 178     178   231 my ($vol, $dir) = splitpath(_abs_path, 1);
29 178         1122 my @dirs = splitdir( $dir );
30 178         842 shift @dirs; # get rid of leading empty "root" directory
31 178         577 return ($vol, @dirs);
32             }
33              
34             # catpath, but take list of directories
35             # restore the empty root dir and provide an empty file to avoid warnings
36             sub _catpath {
37 39     39   80 my ($vol, @dirs) = @_;
38 39         175 return catpath($vol, catdir(q{}, @dirs), q{});
39             }
40              
41             sub _chdir {
42             # Untaint target directory
43 56     56   207 my ($new_dir) = $_[0] =~ /(.*)/s;
44              
45 56         93 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
46 56 100       761 if ( ! CORE::chdir($new_dir) ) {
47 2         456 croak "Failed to change directory to '$new_dir': $!";
48             };
49 54         116 return 1;
50             }
51              
52             {
53             package File::chdir::SCALAR;
54 6     6   32 use Carp;
  6         8  
  6         549  
55              
56             BEGIN {
57 6     6   23 *_abs_path = \&File::chdir::_abs_path;
58 6         16 *_chdir = \&File::chdir::_chdir;
59 6         19 *_split_cwd = \&File::chdir::_split_cwd;
60 6         599 *_catpath = \&File::chdir::_catpath;
61             }
62              
63             sub TIESCALAR {
64 6     6   51 bless [], $_[0];
65             }
66              
67             # To be safe, in case someone chdir'd out from under us, we always
68             # check the Cwd explicitly.
69             sub FETCH {
70 39     39   13536 return _abs_path;
71             }
72              
73             sub STORE {
74 27 100   27   7518 return unless defined $_[1];
75 17         47 _chdir($_[1]);
76             }
77             }
78              
79              
80             {
81             package File::chdir::ARRAY;
82 6     6   32 use Carp;
  6         8  
  6         534  
83              
84             BEGIN {
85 6     6   15 *_abs_path = \&File::chdir::_abs_path;
86 6         8 *_chdir = \&File::chdir::_chdir;
87 6         10 *_split_cwd = \&File::chdir::_split_cwd;
88 6         3763 *_catpath = \&File::chdir::_catpath;
89             }
90              
91             sub TIEARRAY {
92 6     6   35 bless {}, $_[0];
93             }
94              
95             sub FETCH {
96 53     53   225 my($self, $idx) = @_;
97 53         79 my ($vol, @cwd) = _split_cwd;
98 53         202 return $cwd[$idx];
99             }
100              
101             sub STORE {
102 24     24   49 my($self, $idx, $val) = @_;
103              
104 24         46 my ($vol, @cwd) = _split_cwd;
105 24 100       73 if( $self->{Cleared} ) {
106 7         18 @cwd = ();
107 7         14 $self->{Cleared} = 0;
108             }
109              
110 24         41 $cwd[$idx] = $val;
111 24         76 my $dir = _catpath($vol,@cwd);
112              
113 24         179 _chdir($dir);
114 23         126 return $cwd[$idx];
115             }
116              
117             sub FETCHSIZE {
118 86     86   14013 my ($vol, @cwd) = _split_cwd;
119 86         425 return scalar @cwd;
120             }
121 0     0   0 sub STORESIZE {}
122              
123             sub PUSH {
124 7     7   3993 my($self) = shift;
125              
126 7         16 my $dir = _catpath(_split_cwd, @_);
127 7         51 _chdir($dir);
128 7         19 return $self->FETCHSIZE;
129             }
130              
131             sub POP {
132 4     4   437 my($self) = shift;
133              
134 4         7 my ($vol, @cwd) = _split_cwd;
135 4         9 my $popped = pop @cwd;
136 4         27 my $dir = _catpath($vol,@cwd);
137 4         27 _chdir($dir);
138 4         17 return $popped;
139             }
140              
141             sub SHIFT {
142 0     0   0 my($self) = shift;
143              
144 0         0 my ($vol, @cwd) = _split_cwd;
145 0         0 my $shifted = shift @cwd;
146 0         0 my $dir = _catpath($vol,@cwd);
147 0         0 _chdir($dir);
148 0         0 return $shifted;
149             }
150              
151             sub UNSHIFT {
152 0     0   0 my($self) = shift;
153              
154 0         0 my ($vol, @cwd) = _split_cwd;
155 0         0 my $dir = _catpath($vol, @_, @cwd);
156 0         0 _chdir($dir);
157 0         0 return $self->FETCHSIZE;
158             }
159              
160             sub CLEAR {
161 7     7   4933 my($self) = shift;
162 7         34 $self->{Cleared} = 1;
163             }
164              
165             sub SPLICE {
166 4     4   17 my $self = shift;
167 4   50     9 my $offset = shift || 0;
168 4   66     13 my $len = shift || $self->FETCHSIZE - $offset;
169 4         6 my @new_dirs = @_;
170              
171 4         7 my ($vol, @cwd) = _split_cwd;
172 4         12 my @orig_dirs = splice @cwd, $offset, $len, @new_dirs;
173 4         7 my $dir = _catpath($vol, @cwd);
174 4         27 _chdir($dir);
175 4         19 return @orig_dirs;
176             }
177              
178 7     7   34 sub EXTEND { }
179             sub EXISTS {
180 0     0   0 my($self, $idx) = @_;
181 0 0       0 return $self->FETCHSIZE >= $idx ? 1 : 0;
182             }
183              
184             sub DELETE {
185 3     3   4 my($self, $idx) = @_;
186 3 100       5 croak "Can't delete except at the end of \@CWD"
187             if $idx < $self->FETCHSIZE - 1;
188 2         4 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
189 2         6 $self->POP;
190             }
191             }
192              
193             1;
194              
195             __END__