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