File Coverage

blib/lib/File/chdir.pm
Criterion Covered Total %
statement 88 101 87.1
branch 8 10 80.0
condition 3 5 60.0
subroutine 27 31 87.1
pod n/a
total 126 147 85.7


line stmt bran cond sub pod time code
1             package File::chdir;
2 6     6   353362 use 5.004;
  6         63  
3 6     6   28 use strict;
  6         8  
  6         201  
4 6     6   31 use vars qw($VERSION @ISA @EXPORT $CWD @CWD);
  6         7  
  6         594  
5             # ABSTRACT: a more sensible way to change directories
6              
7             our $VERSION = '0.1011';
8              
9             require Exporter;
10             @ISA = qw(Exporter);
11             @EXPORT = qw(*CWD);
12              
13 6     6   41 use Carp;
  6         16  
  6         379  
14 6     6   32 use Cwd 3.16;
  6         179  
  6         398  
15 6     6   806 use File::Spec::Functions 3.27 qw/canonpath splitpath catpath splitdir catdir/;
  6         1507  
  6         2101  
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 258     258   2245 my($cwd) = Cwd::getcwd =~ /(.*)/s;
23             # Run through File::Spec, since everything else uses it
24 258         965 return canonpath($cwd);
25             }
26              
27             # splitpath but also split directory
28             sub _split_cwd {
29 219     219   266 my ($vol, $dir) = splitpath(_abs_path, 1);
30 219         1043 my @dirs = splitdir( $dir );
31 219         881 shift @dirs; # get rid of leading empty "root" directory
32 219         515 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 46     46   78 my ($vol, @dirs) = @_;
39 46         160 return catpath($vol, catdir(q{}, @dirs), q{});
40             }
41              
42             sub _chdir {
43             # Untaint target directory
44 63     63   169 my ($new_dir) = $_[0] =~ /(.*)/s;
45              
46 63         93 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
47 63 100       651 if ( ! CORE::chdir($new_dir) ) {
48 2         461 croak "Failed to change directory to '$new_dir': $!";
49             };
50 61         179 return 1;
51             }
52              
53             {
54             package File::chdir::SCALAR;
55 6     6   36 use Carp;
  6         15  
  6         507  
56              
57             BEGIN {
58 6     6   22 *_abs_path = \&File::chdir::_abs_path;
59 6         9 *_chdir = \&File::chdir::_chdir;
60 6         10 *_split_cwd = \&File::chdir::_split_cwd;
61 6         629 *_catpath = \&File::chdir::_catpath;
62             }
63              
64             sub TIESCALAR {
65 6     6   45 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   9689 return _abs_path;
72             }
73              
74             sub STORE {
75 27 100   27   6477 return unless defined $_[1];
76 17         37 _chdir($_[1]);
77             }
78             }
79              
80              
81             {
82             package File::chdir::ARRAY;
83 6     6   33 use Carp;
  6         8  
  6         527  
84              
85             BEGIN {
86 6     6   19 *_abs_path = \&File::chdir::_abs_path;
87 6         11 *_chdir = \&File::chdir::_chdir;
88 6         7 *_split_cwd = \&File::chdir::_split_cwd;
89 6         3697 *_catpath = \&File::chdir::_catpath;
90             }
91              
92             sub TIEARRAY {
93 6     6   36 bless {}, $_[0];
94             }
95              
96             sub FETCH {
97 70     70   262 my($self, $idx) = @_;
98 70         84 my ($vol, @cwd) = _split_cwd;
99 70         192 return $cwd[$idx];
100             }
101              
102             sub STORE {
103 31     31   58 my($self, $idx, $val) = @_;
104              
105 31         43 my ($vol, @cwd) = _split_cwd;
106 31 100       69 if( $self->{Cleared} ) {
107 7         11 @cwd = ();
108 7         12 $self->{Cleared} = 0;
109             }
110              
111 31         42 $cwd[$idx] = $val;
112 31         49 my $dir = _catpath($vol,@cwd);
113              
114 31         186 _chdir($dir);
115 30         133 return $cwd[$idx];
116             }
117              
118             sub FETCHSIZE {
119 103     103   12578 my ($vol, @cwd) = _split_cwd;
120 103         397 return scalar @cwd;
121             }
122       0     sub STORESIZE {}
123              
124             sub PUSH {
125 7     7   3297 my($self) = shift;
126              
127 7         14 my $dir = _catpath(_split_cwd, @_);
128 7         49 _chdir($dir);
129 7         20 return $self->FETCHSIZE;
130             }
131              
132             sub POP {
133 4     4   428 my($self) = shift;
134              
135 4         7 my ($vol, @cwd) = _split_cwd;
136 4         10 my $popped = pop @cwd;
137 4         7 my $dir = _catpath($vol,@cwd);
138 4         28 _chdir($dir);
139 4         15 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   3943 my($self) = shift;
163 7         21 $self->{Cleared} = 1;
164             }
165              
166             sub SPLICE {
167 4     4   30 my $self = shift;
168 4   50     8 my $offset = shift || 0;
169 4   66     13 my $len = shift || $self->FETCHSIZE - $offset;
170 4         5 my @new_dirs = @_;
171              
172 4         5 my ($vol, @cwd) = _split_cwd;
173 4         8 my @orig_dirs = splice @cwd, $offset, $len, @new_dirs;
174 4         7 my $dir = _catpath($vol, @cwd);
175 4         23 _chdir($dir);
176 4         19 return @orig_dirs;
177             }
178              
179       7     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   6 my($self, $idx) = @_;
187 3 100       5 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         5 $self->POP;
191             }
192             }
193              
194             1;
195              
196             __END__